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  }