github.com/Jeffail/benthos/v3@v3.65.0/lib/input/reader/zmq4.go (about) 1 //go:build ZMQ4 2 // +build ZMQ4 3 4 package reader 5 6 import ( 7 "context" 8 "errors" 9 "fmt" 10 "strings" 11 "time" 12 13 "github.com/Jeffail/benthos/v3/lib/log" 14 "github.com/Jeffail/benthos/v3/lib/message" 15 "github.com/Jeffail/benthos/v3/lib/metrics" 16 "github.com/Jeffail/benthos/v3/lib/types" 17 "github.com/pebbe/zmq4" 18 ) 19 20 //------------------------------------------------------------------------------ 21 22 // ZMQ4 is an input type that consumes ZMQ messages. 23 type ZMQ4 struct { 24 urls []string 25 conf *ZMQ4Config 26 stats metrics.Type 27 log log.Modular 28 29 pollTimeout time.Duration 30 poller *zmq4.Poller 31 socket *zmq4.Socket 32 } 33 34 // NewZMQ4 creates a new ZMQ4 input type. 35 func NewZMQ4(conf *ZMQ4Config, log log.Modular, stats metrics.Type) (*ZMQ4, error) { 36 z := ZMQ4{ 37 conf: conf, 38 stats: stats, 39 log: log, 40 } 41 42 for _, u := range conf.URLs { 43 for _, splitU := range strings.Split(u, ",") { 44 if len(splitU) > 0 { 45 z.urls = append(z.urls, splitU) 46 } 47 } 48 } 49 50 _, err := getZMQType(conf.SocketType) 51 if nil != err { 52 return nil, err 53 } 54 55 if conf.SocketType == "SUB" && len(conf.SubFilters) == 0 { 56 return nil, errors.New("must provide at least one sub filter when connecting with a SUB socket, in order to subscribe to all messages add an empty string") 57 } 58 59 if tout := conf.PollTimeout; len(tout) > 0 { 60 if z.pollTimeout, err = time.ParseDuration(tout); err != nil { 61 return nil, fmt.Errorf("failed to parse poll timeout string: %v", err) 62 } 63 } 64 65 return &z, nil 66 } 67 68 //------------------------------------------------------------------------------ 69 70 func getZMQType(t string) (zmq4.Type, error) { 71 switch t { 72 case "SUB": 73 return zmq4.SUB, nil 74 case "PULL": 75 return zmq4.PULL, nil 76 } 77 return zmq4.PULL, types.ErrInvalidZMQType 78 } 79 80 //------------------------------------------------------------------------------ 81 82 // Connect establishes a ZMQ4 socket. 83 func (z *ZMQ4) Connect() error { 84 return z.ConnectWithContext(context.Background()) 85 } 86 87 // ConnectWithContext establishes a ZMQ4 socket. 88 func (z *ZMQ4) ConnectWithContext(ignored context.Context) error { 89 if z.socket != nil { 90 return nil 91 } 92 93 t, err := getZMQType(z.conf.SocketType) 94 if nil != err { 95 return err 96 } 97 98 ctx, err := zmq4.NewContext() 99 if nil != err { 100 return err 101 } 102 103 var socket *zmq4.Socket 104 if socket, err = ctx.NewSocket(t); nil != err { 105 return err 106 } 107 108 defer func() { 109 if err != nil && socket != nil { 110 socket.Close() 111 } 112 }() 113 114 socket.SetRcvhwm(z.conf.HighWaterMark) 115 116 for _, address := range z.urls { 117 if z.conf.Bind { 118 err = socket.Bind(address) 119 } else { 120 err = socket.Connect(address) 121 } 122 if err != nil { 123 return err 124 } 125 } 126 127 for _, filter := range z.conf.SubFilters { 128 if err = socket.SetSubscribe(filter); err != nil { 129 return err 130 } 131 } 132 133 z.socket = socket 134 z.poller = zmq4.NewPoller() 135 z.poller.Add(z.socket, zmq4.POLLIN) 136 137 if z.conf.Bind { 138 z.log.Infof("Receiving ZMQ4 messages on bound URLs: %s\n", z.urls) 139 } else { 140 z.log.Infof("Receiving ZMQ4 messages on connected URLs: %s\n", z.urls) 141 } 142 return nil 143 } 144 145 // Read attempts to read a new message from the ZMQ socket. 146 func (z *ZMQ4) Read() (types.Message, error) { 147 msg, _, err := z.ReadWithContext(context.Background()) 148 return msg, err 149 } 150 151 // ReadWithContext attempts to read a new message from the ZMQ socket. 152 func (z *ZMQ4) ReadWithContext(ctx context.Context) (types.Message, AsyncAckFn, error) { 153 if z.socket == nil { 154 return nil, nil, types.ErrNotConnected 155 } 156 157 data, err := z.socket.RecvMessageBytes(zmq4.DONTWAIT) 158 if err != nil { 159 var polled []zmq4.Polled 160 if polled, err = z.poller.Poll(z.pollTimeout); len(polled) == 1 { 161 data, err = z.socket.RecvMessageBytes(0) 162 } else if err == nil { 163 return nil, nil, types.ErrTimeout 164 } 165 } 166 if err != nil { 167 return nil, nil, err 168 } 169 170 return message.New(data), noopAsyncAckFn, nil 171 } 172 173 // Acknowledge instructs whether the pending messages were propagated 174 // successfully. 175 func (z *ZMQ4) Acknowledge(err error) error { 176 return nil 177 } 178 179 // CloseAsync shuts down the ZMQ4 input and stops processing requests. 180 func (z *ZMQ4) CloseAsync() { 181 if z.socket != nil { 182 z.socket.Close() 183 z.socket = nil 184 } 185 } 186 187 // WaitForClose blocks until the ZMQ4 input has closed down. 188 func (z *ZMQ4) WaitForClose(timeout time.Duration) error { 189 return nil 190 } 191 192 //------------------------------------------------------------------------------