线上服务的日志出现了 conn pool timeout 的错误,刚开始以为是只有我新写的接口出现了这个问题,后面发现是所有有依赖于 redis 的都出现了这个问题,后面又发现 db 操作和接口请求都有出现同样的报错,翻了一下错误日志,报错的原因都是因为 socket: too many open files。
在 google 上搜索了这个问题 ,找到了这篇文章。
如何解决 Linux 上的“打开文件过多”错误 --- How to Solve the "Too Many Open Files" Error on Linux (howtogeek.com)
最后让运维使用了 uimit -n 去修改限制的上限,解决了这个问题。
但是刚开始的思路是有误的,一直以为是 redis 的问题,用的库是 go-redis,所以过程中也对 go-redis 什么情况会出现 connection pool time 进行了模拟。
首先看官网明确说的,可能导致 conn pool timeout 的情况有两种
- 当连接池中没有空闲连接时,可能因为超过了
Options.PoolTimeout时间而收到此错误,如果你在使用 Pub/Sub 或redis.Conn,请确保不再使用它们时正确释放PubSub/Conn占用的网络资源。 - 当 Redis 处理命令的速度太慢并且池中的所有连接被阻塞的时间超过
PoolTimeout持续时间时,也可能会遇到该错误。
我写了两个例子去模拟会导致 conn pool timeout/too many concurrent operations on a single file or socket 的情况
- 测试不断申请连接的情况,但不释放
package main
import (
"context"
"fmt"
"log"
"runtime"
"testing"
"time"
"github.com/go-redis/redis/v8"
)
func main() {
fmt.Println(runtime.GOMAXPROCS(runtime.NumCPU()))
// 创建连接
client := redis.NewClient(&redis.Options{
Addr: ":6379",
})
//测试不断申请连接的情况,但不释放
TestContinueApplyConn(client)
}
func TestContinueApplyConn(client *redis.Client) {
for {
// 申请连接
conn := client.Conn(context.Background())
//打印当前 redis 连接池中的连接数量
fmt.Println(client.PoolStats().TotalConns)
// 执行操作
_, err := conn.Ping(client.Context()).Result()
if err != nil {
log.Fatal(err)
break
}
// 释放连接
// conn.Close()
}
}
使用 go run main.go 运行,控制台输出内容如下
$ go run main.go
16
0
1
2
3
...
160
2024/02/05 10:50:41 redis: connection pool timeout
exit status 1
第一行输出为 16,当连接数到 160 个后,出现 connection pool timeout 的错误,程序退出,这个同时也验证了 go-redis 连接数的连接池大小为 runtime.GOMAXPROCS * 10
- 测试使用 NewClient 出来的 redis client 并发不间断地去执行 redis command,这个出现的是 too many concurrent operations on a single file or socket (max 1048575) 的错误
// 执行这个报错的信息为: panic: too many concurrent operations on a single file or socket (max 1048575)
func TestContinueUseClientWithNewGoRoutine(client *redis.Client) {
for {
//打印当前 redis 连接池中的连接数量
go func() {
fmt.Println(client.PoolStats().TotalConns)
err := client.SetNX(context.Background(), "test-key", 1, 10*time.Second).Err()
if err != nil {
log.Fatal(err)
}
}()
}
}
.....
62
160
160
160
160
160
160
160
62
160
160
160
62
panic: too many concurrent operations on a single file or socket (max 1048575)
goroutine 1099917 [running]:
internal/poll.(*fdMutex).rwlock(0xc000128280, 0x0?)
C:/Program Files/Go/src/internal/poll/fd_mutex.go:147 +0x11b
internal/poll.(*FD).writeLock(...)
这个报错信息时,可以在文件描述符上执行的最大并发操作数是 1048575,该操作太多了。解决方案是重构您的代码,以便您在该 UDP 连接上不会有 1048576 个并发操作