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 //------------------------------------------------------------------------------