github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/x/server/server.go (about)

     1  // Copyright (c) 2017 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 server implements a network server.
    22  package server
    23  
    24  import (
    25  	"net"
    26  	"sync"
    27  	"sync/atomic"
    28  	"time"
    29  
    30  	xnet "github.com/m3db/m3/src/x/net"
    31  	"github.com/m3db/m3/src/x/retry"
    32  
    33  	"github.com/uber-go/tally"
    34  	"go.uber.org/zap"
    35  )
    36  
    37  // Server is a server capable of listening to incoming traffic and closing itself
    38  // when it's shut down.
    39  type Server interface {
    40  	// ListenAndServe forever listens to new incoming connections and
    41  	// handles data from those connections.
    42  	ListenAndServe() error
    43  
    44  	// Serve accepts and handles incoming connections on the listener l forever.
    45  	Serve(l net.Listener) error
    46  
    47  	// Close closes the server.
    48  	Close()
    49  }
    50  
    51  // Handler can handle the data received on connection.
    52  // It's used in Server once a connection was established.
    53  type Handler interface {
    54  	// Handle handles the data received on the connection, this function
    55  	// should be blocking until the connection is closed or received error.
    56  	Handle(conn net.Conn)
    57  
    58  	// Close closes the handler.
    59  	Close()
    60  }
    61  
    62  type serverMetrics struct {
    63  	openConnections tally.Gauge
    64  }
    65  
    66  func newServerMetrics(scope tally.Scope) serverMetrics {
    67  	return serverMetrics{
    68  		openConnections: scope.Gauge("open-connections"),
    69  	}
    70  }
    71  
    72  type addConnectionFn func(conn net.Conn) bool
    73  type removeConnectionFn func(conn net.Conn)
    74  
    75  type server struct {
    76  	sync.Mutex
    77  
    78  	address                      string
    79  	listener                     net.Listener
    80  	log                          *zap.Logger
    81  	retryOpts                    retry.Options
    82  	reportInterval               time.Duration
    83  	tcpConnectionKeepAlive       bool
    84  	tcpConnectionKeepAlivePeriod time.Duration
    85  
    86  	closed       bool
    87  	closedChan   chan struct{}
    88  	numConns     int32
    89  	conns        []net.Conn
    90  	wgConns      sync.WaitGroup
    91  	metrics      serverMetrics
    92  	handler      Handler
    93  	listenerOpts xnet.ListenerOptions
    94  
    95  	addConnectionFn    addConnectionFn
    96  	removeConnectionFn removeConnectionFn
    97  }
    98  
    99  // NewServer creates a new server.
   100  func NewServer(address string, handler Handler, opts Options) Server {
   101  	instrumentOpts := opts.InstrumentOptions()
   102  	scope := instrumentOpts.MetricsScope()
   103  
   104  	s := &server{
   105  		address:                      address,
   106  		log:                          instrumentOpts.Logger(),
   107  		retryOpts:                    opts.RetryOptions(),
   108  		reportInterval:               instrumentOpts.ReportInterval(),
   109  		tcpConnectionKeepAlive:       opts.TCPConnectionKeepAlive(),
   110  		tcpConnectionKeepAlivePeriod: opts.TCPConnectionKeepAlivePeriod(),
   111  		closedChan:                   make(chan struct{}),
   112  		metrics:                      newServerMetrics(scope),
   113  		handler:                      handler,
   114  		listenerOpts:                 opts.ListenerOptions(),
   115  	}
   116  
   117  	// Set up the connection functions.
   118  	s.addConnectionFn = s.addConnection
   119  	s.removeConnectionFn = s.removeConnection
   120  
   121  	// Start reporting metrics.
   122  	go s.reportMetrics()
   123  
   124  	return s
   125  }
   126  
   127  func (s *server) ListenAndServe() error {
   128  	listener, err := s.listenerOpts.Listen("tcp", s.address)
   129  	if err != nil {
   130  		return err
   131  	}
   132  
   133  	return s.Serve(listener)
   134  }
   135  
   136  func (s *server) Serve(l net.Listener) error {
   137  	s.address = l.Addr().String()
   138  	s.listener = l
   139  	go s.serve()
   140  	return nil
   141  }
   142  
   143  func (s *server) serve() {
   144  	connCh, errCh := xnet.StartForeverAcceptLoop(s.listener, s.retryOpts)
   145  	for conn := range connCh {
   146  		conn := conn
   147  		if tcpConn, ok := conn.(*net.TCPConn); ok {
   148  			tcpConn.SetKeepAlive(s.tcpConnectionKeepAlive)
   149  			if s.tcpConnectionKeepAlivePeriod != 0 {
   150  				tcpConn.SetKeepAlivePeriod(s.tcpConnectionKeepAlivePeriod)
   151  			}
   152  		}
   153  		if !s.addConnectionFn(conn) {
   154  			conn.Close()
   155  		} else {
   156  			s.wgConns.Add(1)
   157  			go func() {
   158  				s.handler.Handle(conn)
   159  
   160  				conn.Close()
   161  				s.removeConnectionFn(conn)
   162  				s.wgConns.Done()
   163  			}()
   164  		}
   165  	}
   166  	err := <-errCh
   167  	s.log.Error("server unexpectedly closed", zap.Error(err))
   168  }
   169  
   170  func (s *server) Close() {
   171  	s.Lock()
   172  	if s.closed {
   173  		s.Unlock()
   174  		return
   175  	}
   176  	s.closed = true
   177  
   178  	close(s.closedChan)
   179  	openConns := make([]net.Conn, len(s.conns))
   180  	copy(openConns, s.conns)
   181  	s.Unlock()
   182  
   183  	// Close all open connections.
   184  	for _, conn := range openConns {
   185  		conn.Close()
   186  	}
   187  
   188  	// Close the listener.
   189  	if s.listener != nil {
   190  		s.listener.Close()
   191  	}
   192  
   193  	// Wait for all connection handlers to finish.
   194  	s.wgConns.Wait()
   195  
   196  	// Close the handler.
   197  	s.handler.Close()
   198  }
   199  
   200  func (s *server) addConnection(conn net.Conn) bool {
   201  	s.Lock()
   202  	defer s.Unlock()
   203  
   204  	if s.closed {
   205  		return false
   206  	}
   207  	s.conns = append(s.conns, conn)
   208  	atomic.AddInt32(&s.numConns, 1)
   209  	return true
   210  }
   211  
   212  func (s *server) removeConnection(conn net.Conn) {
   213  	s.Lock()
   214  	defer s.Unlock()
   215  
   216  	numConns := len(s.conns)
   217  	for i := 0; i < numConns; i++ {
   218  		if s.conns[i] == conn {
   219  			// Move the last connection to i and reduce the number of connections by 1.
   220  			s.conns[i] = s.conns[numConns-1]
   221  			s.conns = s.conns[:numConns-1]
   222  			atomic.AddInt32(&s.numConns, -1)
   223  			return
   224  		}
   225  	}
   226  }
   227  
   228  func (s *server) reportMetrics() {
   229  	t := time.NewTicker(s.reportInterval)
   230  
   231  	for {
   232  		select {
   233  		case <-t.C:
   234  			s.metrics.openConnections.Update(float64(atomic.LoadInt32(&s.numConns)))
   235  		case <-s.closedChan:
   236  			t.Stop()
   237  			return
   238  		}
   239  	}
   240  }