开放问答
Redis 连接池里的"僵尸连接":服务端空闲回收后客户端不报错,如何系统性根治?
该问题的正文、回答与评论。
提问者 谢望舒-338 · 状态 open
RedisJedis连接池僵尸连接中间件
【背景】
我们用 Redis 6.2(3 主 3 从 + Sentinel)承载会话与限流计数,Java 8 + Spring Boot 2.7,客户端是 Jedis 3.8(自封装,基于 JedisPool,maxTotal=200、maxIdle=50、minIdle=10),应用侧 QPS 峰值约 3k,平均 RT 1ms 左右。生产 Redis 配了 timeout 300(空闲 5 分钟服务端主动断开),网络是阿里云内网 VPC。
【现象】
凌晨低峰跑了一夜之后,白天业务上来时偶发 JedisConnectionException: java.net.SocketException: Connection reset by peer,频率大约每天 20~50 次,集中在业务从低峰拉起后的前 1~2 分钟。我猜的是:minIdle=10 的常驻连接在服务端被 timeout 300 回收了,但客户端 pool 里那 10 个连接对象还在,业务第一个请求抽到"僵尸连接"就炸。用打比方说,就像餐厅里桌子被服务员收走了,客人以为还在,一坐下才发现没有。
【已尝试的排查与结果】
- 开
testOnBorrow=true+testWhileIdle=true(minEvictableIdleTimeMillis=60s,timeBetweenEvictionRunsMillis=30s),错误明显减少但没归零,怀疑 evict 线程跑得不够快,业务线程还是抢到了死连接; - 把
timeout 300调大到 3600,错误基本消失,但这是"绕"不是"治",而且担心长连接被中间设备(LB / NAT)静默断掉后问题换个姿势再来; - 翻过 Jedis 源码,发现 JedisPool 默认没有类似 Lettuce 的 ping 自动重连机制,也没有
testOnReturn的合理默认值。
【期望得到的答案形态】 希望有经验的同学给出:
- 一套可落地的参数组合(JedisPool 的 testOnBorrow / testWhileIdle / minEvictableIdleTimeMillis / timeBetweenEvictionRunsMillis,配合服务端 timeout),并解释每个参数为什么这么配、边界在哪里;
- 如果切 Lettuce,
validatePipelinedResponses、ping间隔、shutdownGracefully这些和 Jedis 的对应关系; - 有没有更"治本"的方案,比如客户端主动发 PING 保活、或者用 Redis 的
CLIENT LIST对账? 最好能贴你线上跑过的配置 + 数据(错误率从多少降到多少),有具体数字我会采纳。
评论
暂无评论。