开发高性能server的时候,不可避免的需要对原生socket做一些配置调优,包括设置io复用、接收发送缓存大小等

如果使用io复用,必须要设置监听socket为SO_REUSEADDR和SO_REUSEPORT。设置复用端口和地址还有个好处,就是程序崩溃后,端口监听有可能没有释放,必须要等两分钟才能再次启动程序。如果是端口复用,就是不在乎启动几个进程,所以程序崩溃后可以立马启动新的。

	lc := net.ListenConfig{
		Control: func(network, address string, c syscall.RawConn) error {
			return c.Control(func(fd uintptr) {
                //上面都是默认写法,这里是设置地址复用
				err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
				if err != nil {
					fmt.Println(err)
				}
                //这里是设置端口复用
				err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
				if err != nil {
					fmt.Println(err)
				}
                //这里是设置接收缓冲区大小为10M
                err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_RCVBUF, 10*1024*1024)
				if err != nil {
				    fmt.Println(err)
				}
			})
		},
	}
    //udp的例子
	lp, err := lc.ListenPacket(context.Background(), "udp", "0.0.0.0:1234")
	if err != nil {
		fmt.Println(err)
		return
	}
	udpconn := lp.(*net.UDPConn)
	wg := sync.WaitGroup{}
	wg.Add(1)
	go func() {
		defer wg.Done()
		var data [1024]byte
		for true {
			_, _, err := udpconn.ReadFromUDP(data[:])
			if err != nil {
				fmt.Println(err)
				return
			}
		}
	}()


    //TCP的例子
    l, err := lc.Listen(context.Background(), "tcp", "0.0.0.0:9000")
    if err != nil {
        log.Printf("Could not start TCP listener: %s", err)
        return
    }

    for {
        c, err := l.Accept()
        if err != nil {
        log.Printf("Listener returned: %s", err)
        break
    }

    go func() {
        defer c.Close()
        log.Printf("New connection created")

        _, err := c.Write([]byte("Hello World"))
       if err != nil {
           log.Printf("Unable to write on connection: %s", err)
       }
    }()
}

除此之外,golang还针对UDPConn等提供了一些常见的设置api,比如SetWriteBuffer可以通过官方文档查询。

有很多配置选项,也可以通过官方文档确认。
https://madflojo.medium.com/socket-options-go-multiple-listeners-one-port-7e5257044bb1
https://github.com/libp2p/go-reuseport
https://pkg.go.dev/syscall
https://pkg.go.dev/net