github.com/annwntech/go-micro/v2@v2.9.5/broker/nats/nats.go (about) 1 // Package nats provides a NATS broker 2 package nats 3 4 import ( 5 "context" 6 "errors" 7 "strings" 8 "sync" 9 10 "github.com/annwntech/go-micro/v2/broker" 11 "github.com/annwntech/go-micro/v2/codec/json" 12 "github.com/annwntech/go-micro/v2/logger" 13 "github.com/annwntech/go-micro/v2/registry" 14 nats "github.com/nats-io/nats.go" 15 ) 16 17 type natsBroker struct { 18 sync.Once 19 sync.RWMutex 20 21 // indicate if we're connected 22 connected bool 23 24 addrs []string 25 conn *nats.Conn 26 opts broker.Options 27 nopts nats.Options 28 29 // should we drain the connection 30 drain bool 31 closeCh chan (error) 32 } 33 34 type subscriber struct { 35 s *nats.Subscription 36 opts broker.SubscribeOptions 37 } 38 39 type publication struct { 40 t string 41 err error 42 m *broker.Message 43 } 44 45 func (p *publication) Topic() string { 46 return p.t 47 } 48 49 func (p *publication) Message() *broker.Message { 50 return p.m 51 } 52 53 func (p *publication) Ack() error { 54 // nats does not support acking 55 return nil 56 } 57 58 func (p *publication) Error() error { 59 return p.err 60 } 61 62 func (s *subscriber) Options() broker.SubscribeOptions { 63 return s.opts 64 } 65 66 func (s *subscriber) Topic() string { 67 return s.s.Subject 68 } 69 70 func (s *subscriber) Unsubscribe() error { 71 return s.s.Unsubscribe() 72 } 73 74 func (n *natsBroker) Address() string { 75 if n.conn != nil && n.conn.IsConnected() { 76 return n.conn.ConnectedUrl() 77 } 78 79 if len(n.addrs) > 0 { 80 return n.addrs[0] 81 } 82 83 return "" 84 } 85 86 func (n *natsBroker) setAddrs(addrs []string) []string { 87 //nolint:prealloc 88 var cAddrs []string 89 for _, addr := range addrs { 90 if len(addr) == 0 { 91 continue 92 } 93 if !strings.HasPrefix(addr, "nats://") { 94 addr = "nats://" + addr 95 } 96 cAddrs = append(cAddrs, addr) 97 } 98 if len(cAddrs) == 0 { 99 cAddrs = []string{nats.DefaultURL} 100 } 101 return cAddrs 102 } 103 104 func (n *natsBroker) Connect() error { 105 n.Lock() 106 defer n.Unlock() 107 108 if n.connected { 109 return nil 110 } 111 112 status := nats.CLOSED 113 if n.conn != nil { 114 status = n.conn.Status() 115 } 116 117 switch status { 118 case nats.CONNECTED, nats.RECONNECTING, nats.CONNECTING: 119 n.connected = true 120 return nil 121 default: // DISCONNECTED or CLOSED or DRAINING 122 opts := n.nopts 123 opts.Servers = n.addrs 124 opts.Secure = n.opts.Secure 125 opts.TLSConfig = n.opts.TLSConfig 126 127 // secure might not be set 128 if n.opts.TLSConfig != nil { 129 opts.Secure = true 130 } 131 132 c, err := opts.Connect() 133 if err != nil { 134 return err 135 } 136 n.conn = c 137 n.connected = true 138 return nil 139 } 140 } 141 142 func (n *natsBroker) Disconnect() error { 143 n.Lock() 144 defer n.Unlock() 145 146 // drain the connection if specified 147 if n.drain { 148 n.conn.Drain() 149 n.closeCh <- nil 150 } 151 152 // close the client connection 153 n.conn.Close() 154 155 // set not connected 156 n.connected = false 157 158 return nil 159 } 160 161 func (n *natsBroker) Init(opts ...broker.Option) error { 162 n.setOption(opts...) 163 return nil 164 } 165 166 func (n *natsBroker) Options() broker.Options { 167 return n.opts 168 } 169 170 func (n *natsBroker) Publish(topic string, msg *broker.Message, opts ...broker.PublishOption) error { 171 n.RLock() 172 defer n.RUnlock() 173 174 if n.conn == nil { 175 return errors.New("not connected") 176 } 177 178 b, err := n.opts.Codec.Marshal(msg) 179 if err != nil { 180 return err 181 } 182 return n.conn.Publish(topic, b) 183 } 184 185 func (n *natsBroker) Subscribe(topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) { 186 n.RLock() 187 if n.conn == nil { 188 n.RUnlock() 189 return nil, errors.New("not connected") 190 } 191 n.RUnlock() 192 193 opt := broker.SubscribeOptions{ 194 AutoAck: true, 195 Context: context.Background(), 196 } 197 198 for _, o := range opts { 199 o(&opt) 200 } 201 202 fn := func(msg *nats.Msg) { 203 var m broker.Message 204 pub := &publication{t: msg.Subject} 205 eh := n.opts.ErrorHandler 206 err := n.opts.Codec.Unmarshal(msg.Data, &m) 207 pub.err = err 208 pub.m = &m 209 if err != nil { 210 m.Body = msg.Data 211 if logger.V(logger.ErrorLevel, logger.DefaultLogger) { 212 logger.Error(err) 213 } 214 if eh != nil { 215 eh(pub) 216 } 217 return 218 } 219 if err := handler(pub); err != nil { 220 pub.err = err 221 if logger.V(logger.ErrorLevel, logger.DefaultLogger) { 222 logger.Error(err) 223 } 224 if eh != nil { 225 eh(pub) 226 } 227 } 228 } 229 230 var sub *nats.Subscription 231 var err error 232 233 n.RLock() 234 if len(opt.Queue) > 0 { 235 sub, err = n.conn.QueueSubscribe(topic, opt.Queue, fn) 236 } else { 237 sub, err = n.conn.Subscribe(topic, fn) 238 } 239 n.RUnlock() 240 if err != nil { 241 return nil, err 242 } 243 return &subscriber{s: sub, opts: opt}, nil 244 } 245 246 func (n *natsBroker) String() string { 247 return "nats" 248 } 249 250 func (n *natsBroker) setOption(opts ...broker.Option) { 251 for _, o := range opts { 252 o(&n.opts) 253 } 254 255 n.Once.Do(func() { 256 n.nopts = nats.GetDefaultOptions() 257 }) 258 259 if nopts, ok := n.opts.Context.Value(optionsKey{}).(nats.Options); ok { 260 n.nopts = nopts 261 } 262 263 // broker.Options have higher priority than nats.Options 264 // only if Addrs, Secure or TLSConfig were not set through a broker.Option 265 // we read them from nats.Option 266 if len(n.opts.Addrs) == 0 { 267 n.opts.Addrs = n.nopts.Servers 268 } 269 270 if !n.opts.Secure { 271 n.opts.Secure = n.nopts.Secure 272 } 273 274 if n.opts.TLSConfig == nil { 275 n.opts.TLSConfig = n.nopts.TLSConfig 276 } 277 n.addrs = n.setAddrs(n.opts.Addrs) 278 279 if n.opts.Context.Value(drainConnectionKey{}) != nil { 280 n.drain = true 281 n.closeCh = make(chan error) 282 n.nopts.ClosedCB = n.onClose 283 n.nopts.AsyncErrorCB = n.onAsyncError 284 n.nopts.DisconnectedErrCB = n.onDisconnectedError 285 } 286 } 287 288 func (n *natsBroker) onClose(conn *nats.Conn) { 289 n.closeCh <- nil 290 } 291 292 func (n *natsBroker) onAsyncError(conn *nats.Conn, sub *nats.Subscription, err error) { 293 // There are kinds of different async error nats might callback, but we are interested 294 // in ErrDrainTimeout only here. 295 if err == nats.ErrDrainTimeout { 296 n.closeCh <- err 297 } 298 } 299 300 func (n *natsBroker) onDisconnectedError(conn *nats.Conn, err error) { 301 n.closeCh <- err 302 } 303 304 func NewBroker(opts ...broker.Option) broker.Broker { 305 options := broker.Options{ 306 // Default codec 307 Codec: json.Marshaler{}, 308 Context: context.Background(), 309 Registry: registry.DefaultRegistry, 310 } 311 312 n := &natsBroker{ 313 opts: options, 314 } 315 n.setOption(opts...) 316 317 return n 318 }