Golang中Route ip+net: netlinkrib报错too many open files如何解决

Golang中Route ip+net: netlinkrib报错too many open files如何解决 你好,

我正在尝试获取一个每秒有50-60次API调用的服务器的本地IP / LAN IP。 我使用了下面的代码,但遇到了很多 route ip+net: netlinkrib: too many open files 错误。 我也尝试了注释掉的代码,但遇到了相同的错误。

func GetOutboundIP() string {
	/* conn, err := net.Dial("udp", "8.8.8.8:80")
	if err != nil {
		log.Println(err)
	}
	defer conn.Close()
	localAddr := conn.LocalAddr().(*net.UDPAddr)
	return localAddr.IP */
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		log.Println(err)
		return ""
	}
	for _, a := range addrs {
		if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
			if ipnet.IP.To4() != nil {
				return ipnet.IP.String()
			}
		}
	}
	return ""
}

你能建议一下如何解决这个错误吗?我查阅了在线资料,但没有找到相关的解决方案。

附注:环境运行在代理后面的负载均衡器上。

编辑:

ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 256974
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
**open files                      (-n) 102400**
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
**max user processes              (-u) 256974**
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited


更多关于Golang中Route ip+net: netlinkrib报错too many open files如何解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中Route ip+net: netlinkrib报错too many open files如何解决的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这个错误是由于频繁调用 net.InterfaceAddrs() 导致文件描述符耗尽。每次调用都会创建 netlink socket 连接,在高并发场景下会快速超过限制。

以下是解决方案:

1. 缓存结果(推荐方案)

var (
    cachedIP     string
    ipCacheMutex sync.RWMutex
    lastUpdate   time.Time
    cacheTTL     = 5 * time.Minute
)

func GetOutboundIP() string {
    ipCacheMutex.RLock()
    if time.Since(lastUpdate) < cacheTTL && cachedIP != "" {
        ipCacheMutex.RUnlock()
        return cachedIP
    }
    ipCacheMutex.RUnlock()

    ipCacheMutex.Lock()
    defer ipCacheMutex.Unlock()
    
    // 双重检查
    if time.Since(lastUpdate) < cacheTTL && cachedIP != "" {
        return cachedIP
    }
    
    addrs, err := net.InterfaceAddrs()
    if err != nil {
        log.Printf("获取接口地址失败: %v", err)
        return cachedIP // 返回旧值
    }
    
    for _, a := range addrs {
        if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
            if ipnet.IP.To4() != nil {
                cachedIP = ipnet.IP.String()
                lastUpdate = time.Now()
                return cachedIP
            }
        }
    }
    
    return cachedIP
}

2. 使用连接复用方案

var (
    dialer = &net.Dialer{
        Timeout:   30 * time.Second,
        KeepAlive: 30 * time.Second,
    }
    transport = &http.Transport{
        DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
            return dialer.DialContext(ctx, network, addr)
        },
        MaxIdleConns:        100,
        MaxIdleConnsPerHost: 10,
        IdleConnTimeout:     90 * time.Second,
    }
    httpClient = &http.Client{
        Transport: transport,
        Timeout:   30 * time.Second,
    }
)

func GetOutboundIPWithConn() string {
    conn, err := dialer.Dial("udp", "8.8.8.8:80")
    if err != nil {
        log.Printf("创建连接失败: %v", err)
        return ""
    }
    defer conn.Close()
    
    localAddr := conn.LocalAddr().(*net.UDPAddr)
    return localAddr.IP.String()
}

3. 单次初始化方案

var outboundIP string

func init() {
    addrs, err := net.InterfaceAddrs()
    if err != nil {
        log.Printf("初始化获取IP失败: %v", err)
        return
    }
    
    for _, a := range addrs {
        if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
            if ipnet.IP.To4() != nil {
                outboundIP = ipnet.IP.String()
                break
            }
        }
    }
}

func GetOutboundIP() string {
    return outboundIP
}

4. 使用 netlink 连接池(高级方案)

import (
    "github.com/vishvananda/netlink"
    "sync"
)

type NetlinkPool struct {
    conns chan *netlink.Handle
    mu    sync.Mutex
}

func NewNetlinkPool(size int) (*NetlinkPool, error) {
    pool := &NetlinkPool{
        conns: make(chan *netlink.Handle, size),
    }
    
    for i := 0; i < size; i++ {
        conn, err := netlink.NewHandle()
        if err != nil {
            return nil, err
        }
        pool.conns <- conn
    }
    
    return pool, nil
}

func (p *NetlinkPool) Get() *netlink.Handle {
    return <-p.conns
}

func (p *NetlinkPool) Put(conn *netlink.Handle) {
    select {
    case p.conns <- conn:
    default:
        conn.Close()
    }
}

缓存方案是最直接的解决方法,因为服务器的本地IP通常不会频繁变化。如果IP可能变化,可以设置合适的缓存过期时间。

回到顶部