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