github.com/unionj-cloud/go-doudou/v2@v2.3.5/toolkit/memberlist/net_transport.go (about) 1 package memberlist 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "log" 8 "net" 9 "sync" 10 "sync/atomic" 11 "time" 12 13 "github.com/armon/go-metrics" 14 sockaddr "github.com/hashicorp/go-sockaddr" 15 ) 16 17 const ( 18 // udpPacketBufSize is used to buffer incoming packets during read 19 // operations. 20 udpPacketBufSize = 65536 21 22 // udpRecvBufSize is a large buffer size that we attempt to set UDP 23 // sockets to in order to handle a large volume of messages. 24 udpRecvBufSize = 2 * 1024 * 1024 25 ) 26 27 // NetTransportConfig is used to configure a net transport. 28 type NetTransportConfig struct { 29 // BindAddrs is a list of addresses to bind to for both TCP and UDP 30 // communications. 31 BindAddrs []string 32 33 // BindPort is the port to listen on, for each address above. 34 BindPort int 35 36 // Logger is a logger for operator messages. 37 Logger *log.Logger 38 } 39 40 // NetTransport is a Transport implementation that uses connectionless UDP for 41 // packet operations, and ad-hoc TCP connections for stream operations. 42 type NetTransport struct { 43 config *NetTransportConfig 44 packetCh chan *Packet 45 streamCh chan net.Conn 46 logger *log.Logger 47 wg sync.WaitGroup 48 tcpListeners []*net.TCPListener 49 udpListeners []*net.UDPConn 50 shutdown int32 51 } 52 53 var _ NodeAwareTransport = (*NetTransport)(nil) 54 55 // NewNetTransport returns a net transport with the given configuration. On 56 // success all the network listeners will be created and listening. 57 func NewNetTransport(config *NetTransportConfig) (*NetTransport, error) { 58 // If we reject the empty list outright we can assume that there's at 59 // least one listener of each type later during operation. 60 if len(config.BindAddrs) == 0 { 61 return nil, fmt.Errorf("At least one bind address is required") 62 } 63 64 // Build out the new transport. 65 var ok bool 66 t := NetTransport{ 67 config: config, 68 packetCh: make(chan *Packet), 69 streamCh: make(chan net.Conn), 70 logger: config.Logger, 71 } 72 73 // Clean up listeners if there's an error. 74 defer func() { 75 if !ok { 76 t.Shutdown() 77 } 78 }() 79 80 // Build all the TCP and UDP listeners. 81 port := config.BindPort 82 for _, addr := range config.BindAddrs { 83 ip := net.ParseIP(addr) 84 85 tcpAddr := &net.TCPAddr{IP: ip, Port: port} 86 tcpLn, err := net.ListenTCP("tcp", tcpAddr) 87 if err != nil { 88 return nil, fmt.Errorf("Failed to start TCP listener on %q port %d: %v", addr, port, err) 89 } 90 t.tcpListeners = append(t.tcpListeners, tcpLn) 91 92 // If the config port given was zero, use the first TCP listener 93 // to pick an available port and then apply that to everything 94 // else. 95 if port == 0 { 96 port = tcpLn.Addr().(*net.TCPAddr).Port 97 } 98 99 udpAddr := &net.UDPAddr{IP: ip, Port: port} 100 udpLn, err := net.ListenUDP("udp", udpAddr) 101 if err != nil { 102 return nil, fmt.Errorf("Failed to start UDP listener on %q port %d: %v", addr, port, err) 103 } 104 if err := setUDPRecvBuf(udpLn); err != nil { 105 return nil, fmt.Errorf("Failed to resize UDP buffer: %v", err) 106 } 107 t.udpListeners = append(t.udpListeners, udpLn) 108 } 109 110 // Fire them up now that we've been able to create them all. 111 for i := 0; i < len(config.BindAddrs); i++ { 112 t.wg.Add(2) 113 go t.tcpListen(t.tcpListeners[i]) 114 go t.udpListen(t.udpListeners[i]) 115 } 116 117 ok = true 118 return &t, nil 119 } 120 121 // GetAutoBindPort returns the bind port that was automatically given by the 122 // kernel, if a bind port of 0 was given. 123 func (t *NetTransport) GetAutoBindPort() int { 124 // We made sure there's at least one TCP listener, and that one's 125 // port was applied to all the others for the dynamic bind case. 126 return t.tcpListeners[0].Addr().(*net.TCPAddr).Port 127 } 128 129 // See Transport. 130 func (t *NetTransport) FinalAdvertiseAddr(ip string, port int) (string, int, error) { 131 var advertiseAddr string 132 var advertisePort int 133 if ip != "" { 134 advertiseAddr = ip 135 advertisePort = port 136 } else { 137 if t.config.BindAddrs[0] == "0.0.0.0" { 138 // Otherwise, if we're not bound to a specific IP, let's 139 // use a suitable private IP address. 140 var err error 141 ip, err = sockaddr.GetPrivateIP() 142 if err != nil { 143 return "", 0, fmt.Errorf("failed to get interface addresses: %v", err) 144 } 145 if ip == "" { 146 return "", 0, fmt.Errorf("no private IP address found, and explicit IP not provided") 147 } 148 advertiseAddr = ip 149 } else { 150 // Use the IP that we're bound to, based on the first 151 // TCP listener, which we already ensure is there. 152 advertiseAddr = t.tcpListeners[0].Addr().(*net.TCPAddr).IP.String() 153 } 154 155 // Use the port we are bound to. 156 advertisePort = t.GetAutoBindPort() 157 } 158 159 return advertiseAddr, advertisePort, nil 160 } 161 162 // See Transport. 163 func (t *NetTransport) WriteTo(b []byte, addr string) (time.Time, error) { 164 a := Address{Addr: addr, Name: ""} 165 return t.WriteToAddress(b, a) 166 } 167 168 // See NodeAwareTransport. 169 func (t *NetTransport) WriteToAddress(b []byte, a Address) (time.Time, error) { 170 addr := a.Addr 171 172 udpAddr, err := net.ResolveUDPAddr("udp", addr) 173 if err != nil { 174 return time.Time{}, err 175 } 176 177 // We made sure there's at least one UDP listener, so just use the 178 // packet sending interface on the first one. Take the time after the 179 // write call comes back, which will underestimate the time a little, 180 // but help account for any delays before the write occurs. 181 _, err = t.udpListeners[0].WriteTo(b, udpAddr) 182 return time.Now(), err 183 } 184 185 // See Transport. 186 func (t *NetTransport) PacketCh() <-chan *Packet { 187 return t.packetCh 188 } 189 190 // See IngestionAwareTransport. 191 func (t *NetTransport) IngestPacket(conn net.Conn, addr net.Addr, now time.Time, shouldClose bool) error { 192 if shouldClose { 193 defer conn.Close() 194 } 195 196 // Copy everything from the stream into packet buffer. 197 var buf bytes.Buffer 198 if _, err := io.Copy(&buf, conn); err != nil { 199 return fmt.Errorf("failed to read packet: %v", err) 200 } 201 202 // Check the length - it needs to have at least one byte to be a proper 203 // message. This is checked elsewhere for writes coming in directly from 204 // the UDP socket. 205 if n := buf.Len(); n < 1 { 206 return fmt.Errorf("packet too short (%d bytes) %s", n, LogAddress(addr)) 207 } 208 209 // Inject the packet. 210 t.packetCh <- &Packet{ 211 Buf: buf.Bytes(), 212 From: addr, 213 Timestamp: now, 214 } 215 return nil 216 } 217 218 // See Transport. 219 func (t *NetTransport) DialTimeout(addr string, timeout time.Duration) (net.Conn, error) { 220 a := Address{Addr: addr, Name: ""} 221 return t.DialAddressTimeout(a, timeout) 222 } 223 224 // See NodeAwareTransport. 225 func (t *NetTransport) DialAddressTimeout(a Address, timeout time.Duration) (net.Conn, error) { 226 addr := a.Addr 227 228 dialer := net.Dialer{Timeout: timeout} 229 return dialer.Dial("tcp", addr) 230 } 231 232 // See Transport. 233 func (t *NetTransport) StreamCh() <-chan net.Conn { 234 return t.streamCh 235 } 236 237 // See IngestionAwareTransport. 238 func (t *NetTransport) IngestStream(conn net.Conn) error { 239 t.streamCh <- conn 240 return nil 241 } 242 243 // See Transport. 244 func (t *NetTransport) Shutdown() error { 245 // This will avoid log spam about errors when we shut down. 246 atomic.StoreInt32(&t.shutdown, 1) 247 248 // Rip through all the connections and shut them down. 249 for _, conn := range t.tcpListeners { 250 conn.Close() 251 } 252 for _, conn := range t.udpListeners { 253 conn.Close() 254 } 255 256 // Block until all the listener threads have died. 257 t.wg.Wait() 258 return nil 259 } 260 261 // tcpListen is a long running goroutine that accepts incoming TCP connections 262 // and hands them off to the stream channel. 263 func (t *NetTransport) tcpListen(tcpLn *net.TCPListener) { 264 defer t.wg.Done() 265 266 // baseDelay is the initial delay after an AcceptTCP() error before attempting again 267 const baseDelay = 5 * time.Millisecond 268 269 // maxDelay is the maximum delay after an AcceptTCP() error before attempting again. 270 // In the case that tcpListen() is error-looping, it will delay the shutdown check. 271 // Therefore, changes to maxDelay may have an effect on the latency of shutdown. 272 const maxDelay = 1 * time.Second 273 274 var loopDelay time.Duration 275 for { 276 conn, err := tcpLn.AcceptTCP() 277 if err != nil { 278 if s := atomic.LoadInt32(&t.shutdown); s == 1 { 279 break 280 } 281 282 if loopDelay == 0 { 283 loopDelay = baseDelay 284 } else { 285 loopDelay *= 2 286 } 287 288 if loopDelay > maxDelay { 289 loopDelay = maxDelay 290 } 291 292 t.logger.Printf("[ERR] memberlist: Error accepting TCP connection: %v", err) 293 time.Sleep(loopDelay) 294 continue 295 } 296 // No error, reset loop delay 297 loopDelay = 0 298 299 t.streamCh <- conn 300 } 301 } 302 303 // udpListen is a long running goroutine that accepts incoming UDP packets and 304 // hands them off to the packet channel. 305 func (t *NetTransport) udpListen(udpLn *net.UDPConn) { 306 defer t.wg.Done() 307 for { 308 // Do a blocking read into a fresh buffer. Grab a time stamp as 309 // close as possible to the I/O. 310 buf := make([]byte, udpPacketBufSize) 311 n, addr, err := udpLn.ReadFrom(buf) 312 ts := time.Now() 313 if err != nil { 314 if s := atomic.LoadInt32(&t.shutdown); s == 1 { 315 break 316 } 317 318 t.logger.Printf("[ERR] memberlist: Error reading UDP packet: %v", err) 319 continue 320 } 321 322 // Check the length - it needs to have at least one byte to be a 323 // proper message. 324 if n < 1 { 325 t.logger.Printf("[ERR] memberlist: UDP packet too short (%d bytes) %s", 326 len(buf), LogAddress(addr)) 327 continue 328 } 329 330 // Ingest the packet. 331 metrics.IncrCounter([]string{"memberlist", "udp", "received"}, float32(n)) 332 t.packetCh <- &Packet{ 333 Buf: buf[:n], 334 From: addr, 335 Timestamp: ts, 336 } 337 } 338 } 339 340 // setUDPRecvBuf is used to resize the UDP receive window. The function 341 // attempts to set the read buffer to `udpRecvBuf` but backs off until 342 // the read buffer can be set. 343 func setUDPRecvBuf(c *net.UDPConn) error { 344 size := udpRecvBufSize 345 var err error 346 for size > 0 { 347 if err = c.SetReadBuffer(size); err == nil { 348 return nil 349 } 350 size = size / 2 351 } 352 return err 353 }