Golang预绑定端口规避端口抢占问题

文章目录

      • 0. 背景介绍
      • 1. 方案描述
      • 2. 代码展示


0. 背景介绍

接上篇文章 记time_wait状态引起的端口占用排查 介绍的排查 time_wait 的方法,并不能从根本上解决客户端随机分配的端口抢占本应分配给服务器的端口的问题

1. 方案描述

一般在服务器上都存在一些需要预留的端口,除了上篇介绍的 net.ipv4.ip_local_port_range参数以外,没有很好的预留非连续端口的方式,只能提前绑定需要的端口

如果直接绑定或监听需要预分配的端口的话,当真正需要使用该端口时,还需要经历释放端口、重新绑定,并且还可能出现 Address is in use 的问题

解决方案是利用 SO_REUSEADDR和SO_REUSEPORT 参数的特性,在预绑定时设置这两个参数,后续再往该端口上绑定服务不会存在冲突,且客户端也无法进行抢占

2. 代码展示

  1. 根据网卡获取IP地址
    要选取正确的IP地址,否则绑定无效
func GetLocalIPByEthName(eth ...string) (string, error) {
	interfaces, err := net.Interfaces()
	if err != nil {
		return "", err
	}
	for _, i := range interfaces {
		for _, ethName := range eth {
			if ethName == i.Name {
				byname, err := net.InterfaceByName(i.Name)
				if err != nil {
					return "", err
				}
				addresses, err := byname.Addrs()
				for _, v := range addresses {
					if n, ok := v.(*net.IPNet); ok {
						return n.IP.String(), nil
					}
				}
			}
		}
	}

	return "", nil
}
  1. 绑定端口
    Golang 中包含 syscall包,可以调用系统函数。需要注意的是这里只用绑定,不需要 Listen,如果进入 Listen 状态后就无法再进行绑定了
func BindPorts(serverList []string) error {
	for _, server := range serverList {
		if len(server) <= 0 {
			continue
		}
		port := SplitPort(server)
		if fd, err := syscall.Socket(syscall.AF_INET, syscall.O_NONBLOCK|syscall.SOCK_STREAM, 0); err == nil && port > 0 {
			syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEADDR, 1) // 设置复用端口
			syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1)
			addr := syscall.SockaddrInet4{Port: port}
			copy(addr.Addr[:], net.ParseIP("0.0.0.0").To4())
			syscall.Bind(fd, &addr)
		}
	}
	return nil
}



我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=3fbaklghqlgk0

你可能感兴趣的:(服务器专题,golang,服务器,预绑定端口,syscall)