github.com/Jeffail/benthos/v3@v3.65.0/lib/output/writer/nanomsg.go (about) 1 package writer 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 "sync" 8 "time" 9 10 "github.com/Jeffail/benthos/v3/lib/log" 11 "github.com/Jeffail/benthos/v3/lib/metrics" 12 "github.com/Jeffail/benthos/v3/lib/types" 13 "go.nanomsg.org/mangos/v3" 14 "go.nanomsg.org/mangos/v3/protocol/pub" 15 "go.nanomsg.org/mangos/v3/protocol/push" 16 17 // Import all transport types 18 _ "go.nanomsg.org/mangos/v3/transport/all" 19 ) 20 21 //------------------------------------------------------------------------------ 22 23 // NanomsgConfig contains configuration fields for the Nanomsg output type. 24 type NanomsgConfig struct { 25 URLs []string `json:"urls" yaml:"urls"` 26 Bind bool `json:"bind" yaml:"bind"` 27 SocketType string `json:"socket_type" yaml:"socket_type"` 28 PollTimeout string `json:"poll_timeout" yaml:"poll_timeout"` 29 MaxInFlight int `json:"max_in_flight" yaml:"max_in_flight"` 30 } 31 32 // NewNanomsgConfig creates a new NanomsgConfig with default values. 33 func NewNanomsgConfig() NanomsgConfig { 34 return NanomsgConfig{ 35 URLs: []string{"tcp://localhost:5556"}, 36 Bind: false, 37 SocketType: "PUSH", 38 PollTimeout: "5s", 39 MaxInFlight: 1, 40 } 41 } 42 43 //------------------------------------------------------------------------------ 44 45 // Nanomsg is an output type that serves Nanomsg messages. 46 type Nanomsg struct { 47 log log.Modular 48 stats metrics.Type 49 50 urls []string 51 conf NanomsgConfig 52 53 timeout time.Duration 54 55 socket mangos.Socket 56 sockMut sync.RWMutex 57 } 58 59 // NewNanomsg creates a new Nanomsg output type. 60 func NewNanomsg(conf NanomsgConfig, log log.Modular, stats metrics.Type) (*Nanomsg, error) { 61 s := Nanomsg{ 62 log: log, 63 stats: stats, 64 conf: conf, 65 } 66 for _, u := range conf.URLs { 67 for _, splitU := range strings.Split(u, ",") { 68 if len(splitU) > 0 { 69 // TODO: V4 Remove this work around 70 s.urls = append(s.urls, strings.Replace(splitU, "//*:", "//0.0.0.0:", 1)) 71 } 72 } 73 } 74 75 if tout := conf.PollTimeout; len(tout) > 0 { 76 var err error 77 if s.timeout, err = time.ParseDuration(tout); err != nil { 78 return nil, fmt.Errorf("failed to parse poll timeout string: %v", err) 79 } 80 } 81 82 socket, err := getSocketFromType(conf.SocketType) 83 if err != nil { 84 return nil, err 85 } 86 socket.Close() 87 return &s, nil 88 } 89 90 //------------------------------------------------------------------------------ 91 92 // getSocketFromType returns a socket based on a socket type string. 93 func getSocketFromType(t string) (mangos.Socket, error) { 94 switch t { 95 case "PUSH": 96 return push.NewSocket() 97 case "PUB": 98 return pub.NewSocket() 99 } 100 return nil, types.ErrInvalidScaleProtoType 101 } 102 103 // ConnectWithContext establishes a connection to a nanomsg socket. 104 func (s *Nanomsg) ConnectWithContext(ctx context.Context) error { 105 return s.Connect() 106 } 107 108 // Connect establishes a connection to a nanomsg socket. 109 func (s *Nanomsg) Connect() error { 110 s.sockMut.Lock() 111 defer s.sockMut.Unlock() 112 113 if s.socket != nil { 114 return nil 115 } 116 117 socket, err := getSocketFromType(s.conf.SocketType) 118 if err != nil { 119 return err 120 } 121 122 // Set timeout to prevent endless lock. 123 if s.conf.SocketType == "PUSH" { 124 if err := socket.SetOption( 125 mangos.OptionSendDeadline, s.timeout, 126 ); err != nil { 127 return err 128 } 129 } 130 131 if s.conf.Bind { 132 for _, addr := range s.urls { 133 if err = socket.Listen(addr); err != nil { 134 break 135 } 136 } 137 } else { 138 for _, addr := range s.urls { 139 if err = socket.Dial(addr); err != nil { 140 break 141 } 142 } 143 } 144 if err != nil { 145 return err 146 } 147 148 if s.conf.Bind { 149 s.log.Infof( 150 "Sending nanomsg messages to bound URLs: %s\n", 151 s.urls, 152 ) 153 } else { 154 s.log.Infof( 155 "Sending nanomsg messages to connected URLs: %s\n", 156 s.urls, 157 ) 158 } 159 s.socket = socket 160 return nil 161 } 162 163 //------------------------------------------------------------------------------ 164 165 // WriteWithContext attempts to write a message by pushing it to a nanomsg 166 // socket. 167 func (s *Nanomsg) WriteWithContext(ctx context.Context, msg types.Message) error { 168 return s.Write(msg) 169 } 170 171 // Write attempts to write a message by pushing it to a nanomsg socket. 172 func (s *Nanomsg) Write(msg types.Message) error { 173 s.sockMut.RLock() 174 socket := s.socket 175 s.sockMut.RUnlock() 176 177 if socket == nil { 178 return types.ErrNotConnected 179 } 180 181 return IterateBatchedSend(msg, func(i int, p types.Part) error { 182 return socket.Send(p.Get()) 183 }) 184 } 185 186 // CloseAsync shuts down the Nanomsg output and stops processing messages. 187 func (s *Nanomsg) CloseAsync() { 188 go func() { 189 s.sockMut.Lock() 190 if s.socket != nil { 191 s.socket.Close() 192 s.socket = nil 193 } 194 s.sockMut.Unlock() 195 }() 196 } 197 198 // WaitForClose blocks until the Nanomsg output has closed down. 199 func (s *Nanomsg) WaitForClose(timeout time.Duration) error { 200 return nil 201 } 202 203 //------------------------------------------------------------------------------