github.com/m3db/m3@v1.5.0/src/msg/consumer/consumer.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package consumer
    22  
    23  import (
    24  	"net"
    25  	"sync"
    26  	"time"
    27  
    28  	"go.uber.org/zap"
    29  
    30  	"github.com/m3db/m3/src/msg/generated/proto/msgpb"
    31  	"github.com/m3db/m3/src/msg/protocol/proto"
    32  	"github.com/m3db/m3/src/x/clock"
    33  	xio "github.com/m3db/m3/src/x/io"
    34  	xtime "github.com/m3db/m3/src/x/time"
    35  
    36  	"github.com/uber-go/tally"
    37  )
    38  
    39  type listener struct {
    40  	net.Listener
    41  
    42  	opts    Options
    43  	msgPool *messagePool
    44  	m       metrics
    45  }
    46  
    47  // NewListener creates a consumer listener.
    48  func NewListener(addr string, opts Options) (Listener, error) {
    49  	if opts == nil {
    50  		opts = NewOptions()
    51  	}
    52  	lis, err := net.Listen("tcp", addr)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	mPool := newMessagePool(opts.MessagePoolOptions())
    57  	mPool.Init()
    58  	return &listener{
    59  		Listener: lis,
    60  		opts:     opts,
    61  		msgPool:  mPool,
    62  		m:        newConsumerMetrics(opts.InstrumentOptions().MetricsScope()),
    63  	}, nil
    64  }
    65  
    66  func (l *listener) Accept() (Consumer, error) {
    67  	conn, err := l.Listener.Accept()
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  
    72  	return newConsumer(conn, l.msgPool, l.opts, l.m, NewNoOpMessageProcessor()), nil
    73  }
    74  
    75  type metrics struct {
    76  	messageReceived    tally.Counter
    77  	messageDecodeError tally.Counter
    78  	ackSent            tally.Counter
    79  	ackEncodeError     tally.Counter
    80  	ackWriteError      tally.Counter
    81  	// the duration between the producer sending the message and the consumer reading the message.
    82  	receiveLatency tally.Histogram
    83  	// the duration between the consumer reading the message and sending an ack to the producer.
    84  	handleLatency tally.Histogram
    85  }
    86  
    87  func newConsumerMetrics(scope tally.Scope) metrics {
    88  	return metrics{
    89  		messageReceived:    scope.Counter("message-received"),
    90  		messageDecodeError: scope.Counter("message-decode-error"),
    91  		ackSent:            scope.Counter("ack-sent"),
    92  		ackEncodeError:     scope.Counter("ack-encode-error"),
    93  		ackWriteError:      scope.Counter("ack-write-error"),
    94  		receiveLatency: scope.Histogram("receive-latency",
    95  			// 10ms, 20ms, 40ms, 80ms, 160ms, 320ms, 640ms, 1.2s, 2.4s, 4.8s, 9.6s
    96  			tally.MustMakeExponentialDurationBuckets(time.Millisecond*10, 2, 11)),
    97  		handleLatency: scope.Histogram("handle-latency",
    98  			// 10ms, 20ms, 40ms, 80ms, 160ms, 320ms, 640ms, 1.2s, 2.4s, 4.8s, 9.6s
    99  			tally.MustMakeExponentialDurationBuckets(time.Millisecond*10, 2, 11)),
   100  	}
   101  }
   102  
   103  type consumer struct {
   104  	sync.Mutex
   105  
   106  	opts    Options
   107  	mPool   *messagePool
   108  	encoder proto.Encoder
   109  	decoder proto.Decoder
   110  	w       xio.ResettableWriter
   111  	conn    net.Conn
   112  
   113  	ackPb            msgpb.Ack
   114  	closed           bool
   115  	doneCh           chan struct{}
   116  	wg               sync.WaitGroup
   117  	m                metrics
   118  	messageProcessor MessageProcessor
   119  }
   120  
   121  func newConsumer(
   122  	conn net.Conn,
   123  	mPool *messagePool,
   124  	opts Options,
   125  	m metrics,
   126  	mp MessageProcessor,
   127  ) *consumer {
   128  	var (
   129  		wOpts = xio.ResettableWriterOptions{
   130  			WriteBufferSize: opts.ConnectionWriteBufferSize(),
   131  		}
   132  
   133  		rwOpts   = opts.DecoderOptions().RWOptions()
   134  		writerFn = rwOpts.ResettableWriterFn()
   135  	)
   136  
   137  	return &consumer{
   138  		opts:    opts,
   139  		mPool:   mPool,
   140  		encoder: proto.NewEncoder(opts.EncoderOptions()),
   141  		decoder: proto.NewDecoder(
   142  			conn, opts.DecoderOptions(), opts.ConnectionReadBufferSize(),
   143  		),
   144  		w:                writerFn(newConnWithTimeout(conn, opts.ConnectionWriteTimeout(), time.Now), wOpts),
   145  		conn:             conn,
   146  		closed:           false,
   147  		doneCh:           make(chan struct{}),
   148  		m:                m,
   149  		messageProcessor: mp,
   150  	}
   151  }
   152  
   153  func (c *consumer) Init() {
   154  	c.wg.Add(1)
   155  	go func() {
   156  		c.ackUntilClose()
   157  		c.wg.Done()
   158  	}()
   159  }
   160  func (c *consumer) process(m Message) {
   161  	c.messageProcessor.Process(m)
   162  }
   163  
   164  func (c *consumer) Message() (Message, error) {
   165  	m := c.mPool.Get()
   166  	m.reset(c)
   167  	if err := c.decoder.Decode(m); err != nil {
   168  		c.mPool.Put(m)
   169  		c.m.messageDecodeError.Inc(1)
   170  		return nil, err
   171  	}
   172  	if m.Metadata.SentAtNanos > 0 {
   173  		c.m.receiveLatency.RecordDuration(xtime.Since(xtime.UnixNano(m.Metadata.SentAtNanos)))
   174  	}
   175  	c.m.messageReceived.Inc(1)
   176  	return m, nil
   177  }
   178  
   179  // This function could be called concurrently if messages are being
   180  // processed concurrently.
   181  func (c *consumer) tryAck(m msgpb.Metadata) {
   182  	c.Lock()
   183  	if c.closed {
   184  		c.Unlock()
   185  		return
   186  	}
   187  	c.ackPb.Metadata = append(c.ackPb.Metadata, m)
   188  	ackLen := len(c.ackPb.Metadata)
   189  	if ackLen < c.opts.AckBufferSize() {
   190  		c.Unlock()
   191  		return
   192  	}
   193  	c.trySendAcksWithLock(ackLen)
   194  	c.Unlock()
   195  }
   196  
   197  func (c *consumer) ackUntilClose() {
   198  	flushTicker := time.NewTicker(c.opts.AckFlushInterval())
   199  	defer flushTicker.Stop()
   200  
   201  	for {
   202  		select {
   203  		case <-flushTicker.C:
   204  			c.tryAckAndFlush()
   205  		case <-c.doneCh:
   206  			c.tryAckAndFlush()
   207  			return
   208  		}
   209  	}
   210  }
   211  
   212  func (c *consumer) tryAckAndFlush() {
   213  	c.Lock()
   214  	if ackLen := len(c.ackPb.Metadata); ackLen > 0 {
   215  		c.trySendAcksWithLock(ackLen)
   216  	}
   217  	c.w.Flush()
   218  	c.Unlock()
   219  }
   220  
   221  // if acks fail to send the client will retry sending the messages.
   222  func (c *consumer) trySendAcksWithLock(ackLen int) {
   223  	err := c.encoder.Encode(&c.ackPb)
   224  	log := c.opts.InstrumentOptions().Logger()
   225  	c.ackPb.Metadata = c.ackPb.Metadata[:0]
   226  	if err != nil {
   227  		c.m.ackEncodeError.Inc(1)
   228  		log.Error("failed to encode ack. client will retry sending message.", zap.Error(err))
   229  		return
   230  	}
   231  	_, err = c.w.Write(c.encoder.Bytes())
   232  	if err != nil {
   233  		c.m.ackWriteError.Inc(1)
   234  		log.Error("failed to write ack. client will retry sending message.", zap.Error(err))
   235  		c.tryCloseConn()
   236  		return
   237  	}
   238  	if err := c.w.Flush(); err != nil {
   239  		c.m.ackWriteError.Inc(1)
   240  		log.Error("failed to flush ack. client will retry sending message.", zap.Error(err))
   241  		c.tryCloseConn()
   242  		return
   243  	}
   244  	c.m.ackSent.Inc(int64(ackLen))
   245  }
   246  
   247  func (c *consumer) tryCloseConn() {
   248  	if err := c.conn.Close(); err != nil {
   249  		c.opts.InstrumentOptions().Logger().Error("failed to close connection.", zap.Error(err))
   250  	}
   251  }
   252  
   253  func (c *consumer) Close() {
   254  	c.Lock()
   255  	if c.closed {
   256  		c.Unlock()
   257  		return
   258  	}
   259  	c.closed = true
   260  	c.Unlock()
   261  
   262  	close(c.doneCh)
   263  	c.wg.Wait()
   264  	c.conn.Close()
   265  }
   266  
   267  type message struct {
   268  	msgpb.Message
   269  
   270  	mPool *messagePool
   271  	c     *consumer
   272  }
   273  
   274  func newMessage(p *messagePool) *message {
   275  	return &message{mPool: p}
   276  }
   277  
   278  func (m *message) Bytes() []byte {
   279  	return m.Value
   280  }
   281  
   282  func (m *message) Ack() {
   283  	m.c.tryAck(m.Metadata)
   284  	if m.mPool != nil {
   285  		m.mPool.Put(m)
   286  	}
   287  }
   288  
   289  func (m *message) reset(c *consumer) {
   290  	m.c = c
   291  	resetProto(&m.Message)
   292  }
   293  
   294  func (m *message) ShardID() uint64 {
   295  	return m.Metadata.Shard
   296  }
   297  
   298  func (m *message) SentAtNanos() uint64 {
   299  	return m.Metadata.SentAtNanos
   300  }
   301  
   302  func resetProto(m *msgpb.Message) {
   303  	m.Metadata.Id = 0
   304  	m.Metadata.Shard = 0
   305  	m.Value = m.Value[:0]
   306  }
   307  
   308  type connWithTimeout struct {
   309  	net.Conn
   310  
   311  	timeout time.Duration
   312  	nowFn   clock.NowFn
   313  }
   314  
   315  func newConnWithTimeout(conn net.Conn, timeout time.Duration, nowFn clock.NowFn) connWithTimeout {
   316  	return connWithTimeout{
   317  		Conn:    conn,
   318  		timeout: timeout,
   319  		nowFn:   nowFn,
   320  	}
   321  }
   322  
   323  func (conn connWithTimeout) Write(p []byte) (int, error) {
   324  	if conn.timeout > 0 {
   325  		conn.SetWriteDeadline(conn.nowFn().Add(conn.timeout))
   326  	}
   327  	return conn.Conn.Write(p)
   328  }