github.com/simpleiot/simpleiot@v0.18.3/client/edge.go (about) 1 package client 2 3 import ( 4 "log" 5 "net" 6 "time" 7 8 "github.com/nats-io/nats.go" 9 ) 10 11 // EdgeOptions describes options for connecting edge devices 12 type EdgeOptions struct { 13 URI string 14 AuthToken string 15 NoEcho bool 16 Connected func() 17 Disconnected func() 18 Reconnected func() 19 Closed func() 20 } 21 22 // EdgeConnect is a function that attempts connections for edge devices with appropriate 23 // timeouts, backups, etc. Currently set to disconnect if we don't have a connection after 6m, 24 // and then exp backup to try to connect every 6m after that. 25 func EdgeConnect(eo EdgeOptions) (*nats.Conn, error) { 26 authEnabled := "no" 27 if eo.AuthToken != "" { 28 authEnabled = "yes" 29 } 30 31 natsErrHandler := func(_ *nats.Conn, sub *nats.Subscription, natsErr error) { 32 log.Printf("error: %v\n", natsErr) 33 switch natsErr { 34 case nats.ErrSlowConsumer: 35 pendingMsgs, _, err := sub.Pending() 36 if err != nil { 37 log.Printf("couldn't get pending messages: %v", err) 38 return 39 } 40 log.Printf("Falling behind with %d pending messages on subject %q.\n", 41 pendingMsgs, sub.Subject) 42 // Log error, notify operations... 43 default: 44 log.Println("Nats client error:", natsErr) 45 } 46 // check for other errors 47 } 48 49 siotOptions := func(o *nats.Options) error { 50 _ = nats.Timeout(30 * time.Second)(o) 51 _ = nats.DrainTimeout(30 * time.Second)(o) 52 _ = nats.PingInterval(2 * time.Minute)(o) 53 _ = nats.MaxPingsOutstanding(3)(o) 54 _ = nats.RetryOnFailedConnect(true)(o) 55 _ = nats.ReconnectBufSize(128 * 1024)(o) 56 _ = nats.ReconnectWait(10 * time.Second)(o) 57 _ = nats.MaxReconnects(-1)(o) 58 _ = nats.SetCustomDialer(&net.Dialer{ 59 KeepAlive: -1, 60 })(o) 61 62 _ = nats.CustomReconnectDelay(func(attempts int) time.Duration { 63 delay := ExpBackoff(attempts, 6*time.Minute) 64 log.Printf("NATS reconnect attempts: %v, delay: %v", attempts, delay) 65 return delay 66 })(o) 67 68 _ = nats.Token(eo.AuthToken)(o) 69 70 if eo.NoEcho { 71 o.NoEcho = true 72 } 73 74 _ = nats.ErrorHandler(natsErrHandler)(o) 75 76 _ = nats.ConnectHandler(func(_ *nats.Conn) { 77 if eo.Connected != nil { 78 eo.Connected() 79 } 80 })(o) 81 82 _ = nats.ErrorHandler(func(_ *nats.Conn, sub *nats.Subscription, 83 err error) { 84 if sub != nil { 85 log.Printf("NATS Error, sub: %v, err: %v\n", sub.Subject, err) 86 } else { 87 log.Printf("NATS Error, err: %v\n", err) 88 } 89 })(o) 90 91 _ = nats.ReconnectHandler(func(_ *nats.Conn) { 92 if eo.Reconnected != nil { 93 eo.Reconnected() 94 } 95 })(o) 96 97 _ = nats.DisconnectHandler(func(_ *nats.Conn) { 98 if eo.Disconnected != nil { 99 eo.Disconnected() 100 } 101 })(o) 102 103 _ = nats.ClosedHandler(func(_ *nats.Conn) { 104 if eo.Closed != nil { 105 eo.Closed() 106 } 107 })(o) 108 109 return nil 110 } 111 112 uri, err := sanitizeURI(eo.URI) 113 if err != nil { 114 log.Printf("Error sanitizing URI %v: %v", eo.URI, err) 115 return nil, err 116 } 117 118 log.Printf("NATS edge connect to: %v, auth enabled: %v", uri, authEnabled) 119 nc, err := nats.Connect(uri, siotOptions) 120 121 if err != nil { 122 return nil, err 123 } 124 125 log.Println("NATS: TLS required:", nc.TLSRequired()) 126 127 return nc, nil 128 }