一次redis集群连接数占满问题的排查
2019-01-15 本文已影响90人
fantuanjiaozi
现象
某系统的tomcat,收不到上游系统请求,日志停止不滚动,进程还存活(俗称的tomcat假死)。
分析排查
应用排查
- 登录应用jstack -l pid > jstack.txt,查看堆栈信息,发现大量的连redis集群的BLOCKED信息:
"http-nio-9080-exec-200" #287 daemon prio=5 os_prio=0 tid=0x00002aaad4716800 nid=0x47db waiting for monitor entry [0x000000005287e000]
java.lang.Thread.State: BLOCKED (on object monitor)
at redis.clients.jedis.JedisClusterInfoCache.getSlotPool(JedisClusterInfoCache.java:151)
- waiting to lock <0x0000000648ecf770> (a redis.clients.jedis.JedisClusterInfoCache)
at redis.clients.jedis.JedisSlotBasedConnectionHandler.getConnectionFromSlot(JedisSlotBasedConnectionHandler.java:54)
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:47)
at redis.clients.jedis.JedisClusterCommand.run(JedisClusterCommand.java:32)
at redis.clients.jedis.JedisCluster.get(JedisCluster.java:97)
- 登录监控平台,发现一个redis节点已经监控不到。在应用上telnet该节点,返回超过最大连接数。于是基本定位故障:某redis节点超过最大连接数,导致无法访问。

分析问题
应用程序
- 应用程序在生成流水号时,强依赖redis,没有做降级方案。
- 应用端jedispool设置了连接超时时间,但是过大,造成请求阻塞。
- 此redis集群为公用集群,至少有60个子系统在使用,每个子系统都有设置了最大连接数,理论上,是不会超出10000的。
redis集群排查
- 登录redis-cli,list连接清单,发现了某应用集群有大量的连接,并处于establish状态,断定来源IP为问题应用,并且应用使用了redis的发布订阅功能:subscribe。

- 登录问题应用服务器,使用netstat查看未发现到redis服务器的连接。由此定位问题为redis连接未及时释放。
长连接问题排查
- 查看网络拓扑为应用->防火墙->redis。登录防火墙,连接数依然为0-1个,确认问题为redis未及时回收连接。
- 查看操作系统tcp keepalive设置为7200s(2小时),未生效,推测此配置被redis配置覆盖。(在测试环境使用相同配置,进行tcpdump抓包测试,确认无误。)
- 查看redis配置tcp_keepalive为0,代表关闭tcp连接状态检查,与现象一致。
- 查看防火墙长连接配置为30分钟无流量主动断开连接,与现象一致。
结论
- 应用与redis之间有jupiter防火墙,防火墙30分钟无数据通信,会拆连接,拆的时候不会通知两端回收连接。
- redis-server设置了keepalive=0,此配置覆盖了操作系统的keepalive=7200s,导致redis-server不主动检测连接状态,所以不会主动回收连接。
- 客户端应该是jvm有默认设置,或者走了操作系统的配置(待验证),所以客户端机器上可以正常回收连接。
解决方案
- redis设置tcp_keepalive=60s
- 应用程序jedispool设置连接检测。
核心技术原理与知识点
- TCP连接报活机制
ftp://ftp.wayne.edu/ldp/en/TCP-Keepalive-HOWTO/TCP-Keepalive-HOWTO.pdf - jvm thread stack
- redis server配置
- jedispool配置
相关场景建议
- 所有redis集群对tcp_keepalive配置进行review,保证连接可主动回收
- 所有使用tcp长连接的通信方式(Oracle,Mysql,Redis,MQ等),CS两端应配置合理tcp keepalive时间,严谨配置为0,C端应提供重连机制。
- 发布订阅的功能,建议使用rabbitmq等实现,redis还是搞缓存吧