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  }