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