github.com/Jeffail/benthos/v3@v3.65.0/lib/input/reader/scale_proto.go (about) 1 package reader 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "strings" 8 "sync" 9 "time" 10 11 "github.com/Jeffail/benthos/v3/lib/log" 12 "github.com/Jeffail/benthos/v3/lib/message" 13 "github.com/Jeffail/benthos/v3/lib/metrics" 14 "github.com/Jeffail/benthos/v3/lib/types" 15 "go.nanomsg.org/mangos/v3" 16 "go.nanomsg.org/mangos/v3/protocol/pull" 17 "go.nanomsg.org/mangos/v3/protocol/sub" 18 19 // Import all transport types 20 _ "go.nanomsg.org/mangos/v3/transport/all" 21 ) 22 23 //------------------------------------------------------------------------------ 24 25 // ScaleProtoConfig contains configuration fields for the ScaleProto input type. 26 type ScaleProtoConfig struct { 27 URLs []string `json:"urls" yaml:"urls"` 28 Bind bool `json:"bind" yaml:"bind"` 29 SocketType string `json:"socket_type" yaml:"socket_type"` 30 SubFilters []string `json:"sub_filters" yaml:"sub_filters"` 31 PollTimeout string `json:"poll_timeout" yaml:"poll_timeout"` 32 RepTimeout string `json:"reply_timeout" yaml:"reply_timeout"` 33 } 34 35 // NewScaleProtoConfig creates a new ScaleProtoConfig with default values. 36 func NewScaleProtoConfig() ScaleProtoConfig { 37 return ScaleProtoConfig{ 38 URLs: []string{"tcp://*:5555"}, 39 Bind: true, 40 SocketType: "PULL", 41 SubFilters: []string{}, 42 PollTimeout: "5s", 43 RepTimeout: "5s", 44 } 45 } 46 47 //------------------------------------------------------------------------------ 48 49 // ScaleProto is an input type that contains Scalability Protocols messages. 50 type ScaleProto struct { 51 socket mangos.Socket 52 cMut sync.Mutex 53 54 pollTimeout time.Duration 55 repTimeout time.Duration 56 57 urls []string 58 conf ScaleProtoConfig 59 stats metrics.Type 60 log log.Modular 61 } 62 63 // NewScaleProto creates a new ScaleProto input type. 64 func NewScaleProto(conf ScaleProtoConfig, log log.Modular, stats metrics.Type) (*ScaleProto, error) { 65 s := ScaleProto{ 66 conf: conf, 67 stats: stats, 68 log: log, 69 } 70 71 for _, u := range conf.URLs { 72 for _, splitU := range strings.Split(u, ",") { 73 if len(splitU) > 0 { 74 // TODO: V4 Remove this work around 75 s.urls = append(s.urls, strings.Replace(splitU, "//*:", "//0.0.0.0:", 1)) 76 } 77 } 78 } 79 80 if conf.SocketType == "SUB" && len(conf.SubFilters) == 0 { 81 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") 82 } 83 84 if tout := conf.PollTimeout; len(tout) > 0 { 85 var err error 86 if s.pollTimeout, err = time.ParseDuration(tout); err != nil { 87 return nil, fmt.Errorf("failed to parse poll timeout string: %v", err) 88 } 89 } 90 if tout := conf.RepTimeout; len(tout) > 0 { 91 var err error 92 if s.repTimeout, err = time.ParseDuration(tout); err != nil { 93 return nil, fmt.Errorf("failed to parse reply timeout string: %v", err) 94 } 95 } 96 97 return &s, nil 98 } 99 100 //------------------------------------------------------------------------------ 101 102 // getSocketFromType returns a socket based on a socket type string. 103 func getSocketFromType(t string) (mangos.Socket, error) { 104 switch t { 105 case "PULL": 106 return pull.NewSocket() 107 case "SUB": 108 return sub.NewSocket() 109 } 110 return nil, types.ErrInvalidScaleProtoType 111 } 112 113 //------------------------------------------------------------------------------ 114 115 // Connect establishes a nanomsg socket. 116 func (s *ScaleProto) Connect() error { 117 return s.ConnectWithContext(context.Background()) 118 } 119 120 // ConnectWithContext establishes a nanomsg socket. 121 func (s *ScaleProto) ConnectWithContext(ctx context.Context) error { 122 s.cMut.Lock() 123 defer s.cMut.Unlock() 124 125 if s.socket != nil { 126 return nil 127 } 128 129 var socket mangos.Socket 130 var err error 131 132 defer func() { 133 if err != nil && socket != nil { 134 socket.Close() 135 } 136 }() 137 138 socket, err = getSocketFromType(s.conf.SocketType) 139 if err != nil { 140 return err 141 } 142 143 if s.conf.Bind { 144 for _, addr := range s.urls { 145 if err = socket.Listen(addr); err != nil { 146 break 147 } 148 } 149 } else { 150 for _, addr := range s.urls { 151 if err = socket.Dial(addr); err != nil { 152 break 153 } 154 } 155 } 156 if err != nil { 157 return err 158 } 159 160 // TODO: This is only used for request/response sockets, and is invalid with 161 // other socket types. 162 // err = socket.SetOption(mangos.OptionSendDeadline, s.pollTimeout) 163 // if err != nil { 164 // return err 165 // } 166 167 // Set timeout to prevent endless lock. 168 err = socket.SetOption(mangos.OptionRecvDeadline, s.repTimeout) 169 if err != nil { 170 return err 171 } 172 173 for _, filter := range s.conf.SubFilters { 174 if err := socket.SetOption(mangos.OptionSubscribe, []byte(filter)); err != nil { 175 return err 176 } 177 } 178 179 if s.conf.Bind { 180 s.log.Infof( 181 "Receiving Scalability Protocols messages at bound URLs: %s\n", 182 s.urls, 183 ) 184 } else { 185 s.log.Infof( 186 "Receiving Scalability Protocols messages at connected URLs: %s\n", 187 s.urls, 188 ) 189 } 190 191 s.socket = socket 192 return nil 193 } 194 195 // Read attempts to read a new message from the nanomsg socket. 196 func (s *ScaleProto) Read() (types.Message, error) { 197 msg, _, err := s.ReadWithContext(context.Background()) 198 return msg, err 199 } 200 201 // ReadWithContext attempts to read a new message from the nanomsg socket. 202 func (s *ScaleProto) ReadWithContext(ctx context.Context) (types.Message, AsyncAckFn, error) { 203 s.cMut.Lock() 204 socket := s.socket 205 s.cMut.Unlock() 206 207 if socket == nil { 208 return nil, nil, types.ErrNotConnected 209 } 210 data, err := socket.Recv() 211 if err != nil { 212 if err == mangos.ErrRecvTimeout { 213 return nil, nil, types.ErrTimeout 214 } 215 return nil, nil, err 216 } 217 return message.New([][]byte{data}), noopAsyncAckFn, nil 218 } 219 220 // Acknowledge instructs whether the pending messages were propagated 221 // successfully. 222 func (s *ScaleProto) Acknowledge(err error) error { 223 return nil 224 } 225 226 // CloseAsync shuts down the ScaleProto input and stops processing requests. 227 func (s *ScaleProto) CloseAsync() { 228 s.cMut.Lock() 229 if s.socket != nil { 230 s.socket.Close() 231 s.socket = nil 232 } 233 s.cMut.Unlock() 234 } 235 236 // WaitForClose blocks until the ScaleProto input has closed down. 237 func (s *ScaleProto) WaitForClose(timeout time.Duration) error { 238 return nil 239 } 240 241 //------------------------------------------------------------------------------