Go动态端口绑定

使用随机端口启动应用是一个很常见的需求,常用于在微服务架构中做负载均衡。在 Go 中生成随机端口相对比较容易,可以通过使用 net 包中的方法就能很快实现。

# 实现原理

当 net.ListenTCP 的参数中指定的端口号为 0 时,操作系统将会随机分配一个可用的端口号。这种方式常被称为动态端口绑定

具体来说,当你将端口号参数设置为 0 时,操作系统会在调用 net.ListenTCP 时自动选择一个未被占用的端口号,并将其绑定到监听器。然后通过 listener.Addr() 方法获取实际分配的端口号。

请注意,由于端口是动态分配的,因此在监听之前无法预先知道所分配的端口号。因此,需要通过 listener.Addr().(*net.TCPAddr).Port 或其他方式来获取实际的端口号。

主要使用 net 包中的的以下两个方法:

ResolveTCPAddr

func ResolveTCPAddr(network, address string) (*TCPAddr, error)
  • network 参数指定网络类型,通常为 “tcp”。
  • address 参数是要解析的地址字符串,可以是 IP 地址或主机名加端口号的形式,例如 “127.0.0.1:8080” 或 “localhost:8080”。
  • 只负责解析地址字符串,而不会尝试建立连接。

ListenTCP

func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error)
  • ListenTCP 在本地 TCP 地址 laddr 上声明并返回一个 *TCPListener,
  • net 可选的值必须是 tcp、tcp4、tcp6 其中一个
  • 如果 laddr 的端口字段为 0,函数将选择一个当前可用的端口
  • 可以用 Listener 的 Addr 方法获得该端口。

# 示例代码

package main  
  
import (  
    "fmt"  
    "net")  
  
// GetFreePort 生成端口号  
func GetFreePort() (port int, err error) {  
    // 解析地址
    addr, err := net.ResolveTCPAddr("tcp", "localhost:0")  
    if err != nil {  
       return 0, err  
    }  
    
    // 监听地址
    l, err := net.ListenTCP("tcp", addr)  
    if err != nil {  
       return 0, err  
    }  
    defer l.Close()
    // 获取动态分配的端口号
    return l.Addr().(*net.TCPAddr).Port, nil  
}  
  
func main() {  
    port, _ := GetFreePort()  
    fmt.Println(port)  
}

代码整理后如下

// GetFreePort asks the kernel for a free open port that is ready to use.
func GetFreePort() (port int, err error) {
	var a *net.TCPAddr
	if a, err = net.ResolveTCPAddr("tcp", "localhost:0"); err == nil {
		var l *net.TCPListener
		if l, err = net.ListenTCP("tcp", a); err == nil {
			defer l.Close()
			return l.Addr().(*net.TCPAddr).Port, nil
		}
	}
	return
}
Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计