github.com/Jeffail/benthos/v3@v3.65.0/lib/input/reader/nats.go (about)

     1  package reader
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"errors"
     7  	"strings"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/Jeffail/benthos/v3/internal/impl/nats/auth"
    12  	btls "github.com/Jeffail/benthos/v3/lib/util/tls"
    13  
    14  	"github.com/Jeffail/benthos/v3/lib/log"
    15  	"github.com/Jeffail/benthos/v3/lib/message"
    16  	"github.com/Jeffail/benthos/v3/lib/metrics"
    17  	"github.com/Jeffail/benthos/v3/lib/types"
    18  	"github.com/nats-io/nats.go"
    19  )
    20  
    21  //------------------------------------------------------------------------------
    22  
    23  // NATSConfig contains configuration fields for the NATS input type.
    24  type NATSConfig struct {
    25  	URLs          []string    `json:"urls" yaml:"urls"`
    26  	Subject       string      `json:"subject" yaml:"subject"`
    27  	QueueID       string      `json:"queue" yaml:"queue"`
    28  	PrefetchCount int         `json:"prefetch_count" yaml:"prefetch_count"`
    29  	TLS           btls.Config `json:"tls" yaml:"tls"`
    30  	Auth          auth.Config `json:"auth" yaml:"auth"`
    31  }
    32  
    33  // NewNATSConfig creates a new NATSConfig with default values.
    34  func NewNATSConfig() NATSConfig {
    35  	return NATSConfig{
    36  		URLs:          []string{nats.DefaultURL},
    37  		Subject:       "benthos_messages",
    38  		QueueID:       "benthos_queue",
    39  		PrefetchCount: 32,
    40  		TLS:           btls.NewConfig(),
    41  		Auth:          auth.New(),
    42  	}
    43  }
    44  
    45  //------------------------------------------------------------------------------
    46  
    47  // NATS is an input type that receives NATS messages.
    48  type NATS struct {
    49  	urls  string
    50  	conf  NATSConfig
    51  	stats metrics.Type
    52  	log   log.Modular
    53  
    54  	cMut sync.Mutex
    55  
    56  	natsConn      *nats.Conn
    57  	natsSub       *nats.Subscription
    58  	natsChan      chan *nats.Msg
    59  	interruptChan chan struct{}
    60  	tlsConf       *tls.Config
    61  }
    62  
    63  // NewNATS creates a new NATS input type.
    64  func NewNATS(conf NATSConfig, log log.Modular, stats metrics.Type) (*NATS, error) {
    65  	n := NATS{
    66  		conf:          conf,
    67  		stats:         stats,
    68  		log:           log,
    69  		interruptChan: make(chan struct{}),
    70  	}
    71  	n.urls = strings.Join(conf.URLs, ",")
    72  	if conf.PrefetchCount < 0 {
    73  		return nil, errors.New("prefetch count must be greater than or equal to zero")
    74  	}
    75  	var err error
    76  	if conf.TLS.Enabled {
    77  		if n.tlsConf, err = conf.TLS.Get(); err != nil {
    78  			return nil, err
    79  		}
    80  	}
    81  
    82  	return &n, nil
    83  }
    84  
    85  //------------------------------------------------------------------------------
    86  
    87  // Connect establishes a connection to a NATS server.
    88  func (n *NATS) Connect() error {
    89  	return n.ConnectWithContext(context.Background())
    90  }
    91  
    92  // ConnectWithContext establishes a connection to a NATS server.
    93  func (n *NATS) ConnectWithContext(ctx context.Context) error {
    94  	n.cMut.Lock()
    95  	defer n.cMut.Unlock()
    96  
    97  	if n.natsConn != nil {
    98  		return nil
    99  	}
   100  
   101  	var natsConn *nats.Conn
   102  	var natsSub *nats.Subscription
   103  	var err error
   104  	var opts []nats.Option
   105  
   106  	if n.tlsConf != nil {
   107  		opts = append(opts, nats.Secure(n.tlsConf))
   108  	}
   109  
   110  	opts = append(opts, auth.GetOptions(n.conf.Auth)...)
   111  
   112  	if natsConn, err = nats.Connect(n.urls, opts...); err != nil {
   113  		return err
   114  	}
   115  	natsChan := make(chan *nats.Msg, n.conf.PrefetchCount)
   116  
   117  	if len(n.conf.QueueID) > 0 {
   118  		natsSub, err = natsConn.ChanQueueSubscribe(n.conf.Subject, n.conf.QueueID, natsChan)
   119  	} else {
   120  		natsSub, err = natsConn.ChanSubscribe(n.conf.Subject, natsChan)
   121  	}
   122  
   123  	if err != nil {
   124  		return err
   125  	}
   126  
   127  	n.log.Infof("Receiving NATS messages from subject: %v\n", n.conf.Subject)
   128  
   129  	n.natsConn = natsConn
   130  	n.natsSub = natsSub
   131  	n.natsChan = natsChan
   132  	return nil
   133  }
   134  
   135  func (n *NATS) disconnect() {
   136  	n.cMut.Lock()
   137  	defer n.cMut.Unlock()
   138  
   139  	if n.natsSub != nil {
   140  		n.natsSub.Unsubscribe()
   141  		n.natsSub = nil
   142  	}
   143  	if n.natsConn != nil {
   144  		n.natsConn.Close()
   145  		n.natsConn = nil
   146  	}
   147  	n.natsChan = nil
   148  }
   149  
   150  // ReadWithContext attempts to read a new message from the NATS subject.
   151  func (n *NATS) ReadWithContext(ctx context.Context) (types.Message, AsyncAckFn, error) {
   152  	n.cMut.Lock()
   153  	natsChan := n.natsChan
   154  	natsConn := n.natsConn
   155  	n.cMut.Unlock()
   156  
   157  	var msg *nats.Msg
   158  	var open bool
   159  	select {
   160  	case msg, open = <-natsChan:
   161  	case <-ctx.Done():
   162  		return nil, nil, types.ErrTimeout
   163  	case _, open = <-n.interruptChan:
   164  	}
   165  	if !open {
   166  		n.disconnect()
   167  		return nil, nil, types.ErrNotConnected
   168  	}
   169  
   170  	bmsg := message.New([][]byte{msg.Data})
   171  	meta := bmsg.Get(0).Metadata()
   172  	meta.Set("nats_subject", msg.Subject)
   173  	// process message headers if server supports the feature
   174  	if natsConn.HeadersSupported() {
   175  		for key := range msg.Header {
   176  			value := msg.Header.Get(key)
   177  			meta.Set(key, value)
   178  		}
   179  	}
   180  
   181  	return bmsg, func(ctx context.Context, res types.Response) error {
   182  		var ackErr error
   183  		if res.Error() != nil {
   184  			ackErr = msg.Nak()
   185  		} else {
   186  			ackErr = msg.Ack()
   187  		}
   188  		if errors.Is(ackErr, nats.ErrMsgNoReply) {
   189  			ackErr = nil
   190  		}
   191  		return ackErr
   192  	}, nil
   193  }
   194  
   195  // Read attempts to read a new message from the NATS subject.
   196  //
   197  // Deprecated: Use ReadWithContext instead.
   198  func (n *NATS) Read() (types.Message, error) {
   199  	m, _, err := n.ReadWithContext(context.Background())
   200  	return m, err
   201  }
   202  
   203  // Acknowledge confirms whether or not our unacknowledged messages have been
   204  // successfully propagated or not.
   205  //
   206  // Deprecated: Use ReadWithContext instead.
   207  func (n *NATS) Acknowledge(err error) error {
   208  	return nil
   209  }
   210  
   211  // CloseAsync shuts down the NATS input and stops processing requests.
   212  func (n *NATS) CloseAsync() {
   213  	close(n.interruptChan)
   214  }
   215  
   216  // WaitForClose blocks until the NATS input has closed down.
   217  func (n *NATS) WaitForClose(timeout time.Duration) error {
   218  	return nil
   219  }
   220  
   221  //------------------------------------------------------------------------------