// This file originally came from official Nats.io GitHub repository. // You can reach the original file with the following link: // https://github.com/nats-io/stan.go/blob/master/examples/stan-sub/main.go // Copyright 2016-2019 The NATS Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "flag" "fmt" "log" "os" "os/signal" "syscall" "time" nats "github.com/nats-io/nats.go" "github.com/nats-io/stan.go" "github.com/nats-io/stan.go/pb" ) var usageStr = ` Usage: stan-sub [options] Options: -s, --server NATS Streaming server URL(s) -c, --cluster NATS Streaming cluster name -id, --clientid NATS Streaming client ID -cr, --creds NATS 2.0 Credentials Subscription Options: --qgroup Queue group --all Deliver all available messages --last Deliver starting with last published message --since Deliver messages in last interval (e.g. 1s, 1hr) --seq Start at seqno --new_only Only deliver new messages --durable Durable subscriber name --unsub Unsubscribe the durable on exit ` // NOTE: Use tls scheme for TLS, e.g. stan-sub -s tls://demo.nats.io:4443 foo func usage() { log.Fatalf(usageStr) } func printMsg(m *stan.Msg, i int) { log.Printf("[#%d] Received: %s\n", i, m) } func main() { var ( clusterID, clientID string URL string userCreds string showTime bool qgroup string unsubscribe bool startSeq uint64 startDelta string deliverAll bool newOnly bool deliverLast bool durable string ) flag.StringVar(&URL, "s", stan.DefaultNatsURL, "The nats server URLs (separated by comma)") flag.StringVar(&URL, "server", stan.DefaultNatsURL, "The nats server URLs (separated by comma)") flag.StringVar(&clusterID, "c", "test-cluster", "The NATS Streaming cluster ID") flag.StringVar(&clusterID, "cluster", "test-cluster", "The NATS Streaming cluster ID") flag.StringVar(&clientID, "id", "stan-sub", "The NATS Streaming client ID to connect with") flag.StringVar(&clientID, "clientid", "stan-sub", "The NATS Streaming client ID to connect with") flag.BoolVar(&showTime, "t", false, "Display timestamps") // Subscription options flag.Uint64Var(&startSeq, "seq", 0, "Start at sequence no.") flag.BoolVar(&deliverAll, "all", true, "Deliver all") flag.BoolVar(&newOnly, "new_only", false, "Only new messages") flag.BoolVar(&deliverLast, "last", false, "Start with last value") flag.StringVar(&startDelta, "since", "", "Deliver messages since specified time offset") flag.StringVar(&durable, "durable", "", "Durable subscriber name") flag.StringVar(&qgroup, "qgroup", "", "Queue group name") flag.BoolVar(&unsubscribe, "unsub", false, "Unsubscribe the durable on exit") flag.BoolVar(&unsubscribe, "unsubscribe", false, "Unsubscribe the durable on exit") flag.StringVar(&userCreds, "cr", "", "Credentials File") flag.StringVar(&userCreds, "creds", "", "Credentials File") log.SetFlags(0) flag.Usage = usage flag.Parse() args := flag.Args() if len(args) < 1 { log.Printf("Error: A subject must be specified.") usage() } // Connect Options. opts := []nats.Option{nats.Name("NATS Streaming Example Subscriber")} // Use UserCredentials if userCreds != "" { opts = append(opts, nats.UserCredentials(userCreds)) } // Connect to NATS nc, err := nats.Connect(URL, opts...) if err != nil { log.Fatal(err) } defer nc.Close() sc, err := stan.Connect(clusterID, clientID, stan.NatsConn(nc), stan.SetConnectionLostHandler(func(_ stan.Conn, reason error) { log.Fatalf("Connection lost, reason: %v", reason) })) if err != nil { log.Fatalf("Can't connect: %v.\nMake sure a NATS Streaming Server is running at: %s", err, URL) } log.Printf("Connected to %s clusterID: [%s] clientID: [%s]\n", URL, clusterID, clientID) // Process Subscriber Options. startOpt := stan.StartAt(pb.StartPosition_NewOnly) if startSeq != 0 { startOpt = stan.StartAtSequence(startSeq) } else if deliverLast { startOpt = stan.StartWithLastReceived() } else if deliverAll && !newOnly { startOpt = stan.DeliverAllAvailable() } else if startDelta != "" { ago, err := time.ParseDuration(startDelta) if err != nil { sc.Close() log.Fatal(err) } startOpt = stan.StartAtTimeDelta(ago) } subj, i := args[0], 0 mcb := func(msg *stan.Msg) { i++ printMsg(msg, i) } sub, err := sc.QueueSubscribe(subj, qgroup, mcb, startOpt, stan.DurableName(durable)) if err != nil { sc.Close() log.Fatal(err) } log.Printf("Listening on [%s], clientID=[%s], qgroup=[%s] durable=[%s]\n", subj, clientID, qgroup, durable) if showTime { log.SetFlags(log.LstdFlags) } // Wait for a SIGINT (perhaps triggered by user with CTRL-C) // Run cleanup when signal is received signalChan := make(chan os.Signal, 1) cleanupDone := make(chan bool) signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM) go func() { for sig := range signalChan { fmt.Printf("\nReceived signal %s, unsubscribing and closing connection...\n\n", sig.String()) // Do not unsubscribe a durable on exit, except if asked to. if durable == "" || unsubscribe { err := sub.Unsubscribe() if err != nil { log.Fatal(err) } } sc.Close() cleanupDone <- true } }() <-cleanupDone }