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
更多关于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可能变化,可以设置合适的缓存过期时间。

