gitee.com/liuxuezhan/go-micro-v1.18.0@v1.0.0/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 "gitee.com/liuxuezhan/go-micro-v1.18.0/broker" 11 "gitee.com/liuxuezhan/go-micro-v1.18.0/codec/json" 12 nats "github.com/nats-io/nats.go" 13 ) 14 15 type natsBroker struct { 16 sync.Once 17 sync.RWMutex 18 addrs []string 19 conn *nats.Conn 20 opts broker.Options 21 nopts nats.Options 22 drain bool 23 closeCh chan (error) 24 } 25 26 type subscriber struct { 27 s *nats.Subscription 28 opts broker.SubscribeOptions 29 } 30 31 type publication struct { 32 t string 33 m *broker.Message 34 } 35 36 func (p *publication) Topic() string { 37 return p.t 38 } 39 40 func (p *publication) Message() *broker.Message { 41 return p.m 42 } 43 44 func (p *publication) Ack() error { 45 // nats does not support acking 46 return nil 47 } 48 49 func (s *subscriber) Options() broker.SubscribeOptions { 50 return s.opts 51 } 52 53 func (s *subscriber) Topic() string { 54 return s.s.Subject 55 } 56 57 func (s *subscriber) Unsubscribe() error { 58 return s.s.Unsubscribe() 59 } 60 61 func (n *natsBroker) Address() string { 62 if n.conn != nil && n.conn.IsConnected() { 63 return n.conn.ConnectedUrl() 64 } 65 if len(n.addrs) > 0 { 66 return n.addrs[0] 67 } 68 69 return "" 70 } 71 72 func setAddrs(addrs []string) []string { 73 //nolint:prealloc 74 var cAddrs []string 75 for _, addr := range addrs { 76 if len(addr) == 0 { 77 continue 78 } 79 if !strings.HasPrefix(addr, "nats://") { 80 addr = "nats://" + addr 81 } 82 cAddrs = append(cAddrs, addr) 83 } 84 if len(cAddrs) == 0 { 85 cAddrs = []string{nats.DefaultURL} 86 } 87 return cAddrs 88 } 89 90 func (n *natsBroker) Connect() error { 91 n.Lock() 92 defer n.Unlock() 93 94 status := nats.CLOSED 95 if n.conn != nil { 96 status = n.conn.Status() 97 } 98 99 switch status { 100 case nats.CONNECTED, nats.RECONNECTING, nats.CONNECTING: 101 return nil 102 default: // DISCONNECTED or CLOSED or DRAINING 103 opts := n.nopts 104 opts.Servers = n.addrs 105 opts.Secure = n.opts.Secure 106 opts.TLSConfig = n.opts.TLSConfig 107 108 // secure might not be set 109 if n.opts.TLSConfig != nil { 110 opts.Secure = true 111 } 112 113 c, err := opts.Connect() 114 if err != nil { 115 return err 116 } 117 n.conn = c 118 return nil 119 } 120 } 121 122 func (n *natsBroker) Disconnect() error { 123 n.RLock() 124 defer n.RUnlock() 125 if n.drain { 126 n.conn.Drain() 127 return <-n.closeCh 128 } 129 n.conn.Close() 130 return nil 131 } 132 133 func (n *natsBroker) Init(opts ...broker.Option) error { 134 n.setOption(opts...) 135 return nil 136 } 137 138 func (n *natsBroker) Options() broker.Options { 139 return n.opts 140 } 141 142 func (n *natsBroker) Publish(topic string, msg *broker.Message, opts ...broker.PublishOption) error { 143 b, err := n.opts.Codec.Marshal(msg) 144 if err != nil { 145 return err 146 } 147 n.RLock() 148 defer n.RUnlock() 149 return n.conn.Publish(topic, b) 150 } 151 152 func (n *natsBroker) Subscribe(topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) { 153 if n.conn == nil { 154 return nil, errors.New("not connected") 155 } 156 157 opt := broker.SubscribeOptions{ 158 AutoAck: true, 159 Context: context.Background(), 160 } 161 162 for _, o := range opts { 163 o(&opt) 164 } 165 166 fn := func(msg *nats.Msg) { 167 var m broker.Message 168 if err := n.opts.Codec.Unmarshal(msg.Data, &m); err != nil { 169 return 170 } 171 handler(&publication{m: &m, t: msg.Subject}) 172 } 173 174 var sub *nats.Subscription 175 var err error 176 177 n.RLock() 178 if len(opt.Queue) > 0 { 179 sub, err = n.conn.QueueSubscribe(topic, opt.Queue, fn) 180 } else { 181 sub, err = n.conn.Subscribe(topic, fn) 182 } 183 n.RUnlock() 184 if err != nil { 185 return nil, err 186 } 187 return &subscriber{s: sub, opts: opt}, nil 188 } 189 190 func (n *natsBroker) String() string { 191 return "nats" 192 } 193 194 func NewBroker(opts ...broker.Option) broker.Broker { 195 options := broker.Options{ 196 // Default codec 197 Codec: json.Marshaler{}, 198 Context: context.Background(), 199 } 200 201 n := &natsBroker{ 202 opts: options, 203 } 204 n.setOption(opts...) 205 206 return n 207 } 208 209 func (n *natsBroker) setOption(opts ...broker.Option) { 210 for _, o := range opts { 211 o(&n.opts) 212 } 213 214 n.Once.Do(func() { 215 n.nopts = nats.GetDefaultOptions() 216 }) 217 218 if nopts, ok := n.opts.Context.Value(optionsKey{}).(nats.Options); ok { 219 n.nopts = nopts 220 } 221 222 // broker.Options have higher priority than nats.Options 223 // only if Addrs, Secure or TLSConfig were not set through a broker.Option 224 // we read them from nats.Option 225 if len(n.opts.Addrs) == 0 { 226 n.opts.Addrs = n.nopts.Servers 227 } 228 229 if !n.opts.Secure { 230 n.opts.Secure = n.nopts.Secure 231 } 232 233 if n.opts.TLSConfig == nil { 234 n.opts.TLSConfig = n.nopts.TLSConfig 235 } 236 n.addrs = setAddrs(n.opts.Addrs) 237 238 if n.opts.Context.Value(drainConnectionKey{}) != nil { 239 n.drain = true 240 n.closeCh = make(chan error) 241 n.nopts.ClosedCB = n.onClose 242 n.nopts.AsyncErrorCB = n.onAsyncError 243 } 244 } 245 246 func (n *natsBroker) onClose(conn *nats.Conn) { 247 n.closeCh <- nil 248 } 249 250 func (n *natsBroker) onAsyncError(conn *nats.Conn, sub *nats.Subscription, err error) { 251 // There are kinds of different async error nats might callback, but we are interested 252 // in ErrDrainTimeout only here. 253 if err == nats.ErrDrainTimeout { 254 n.closeCh <- err 255 } 256 }