github.com/m3db/m3@v1.5.0/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 }