一次redis集群连接数占满问题的排查

现象

某系统的tomcat,收不到上游系统请求,日志停止不滚动,进程还存活(俗称的tomcat假死)。

分析排查

应用排查

  1. 登录应用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)
  1. 登录监控平台,发现一个redis节点已经监控不到。在应用上telnet该节点,返回超过最大连接数。于是基本定位故障:某redis节点超过最大连接数,导致无法访问。
一次redis集群连接数占满问题的排查_第1张图片
redis监控.png

分析问题

应用程序

  1. 应用程序在生成流水号时,强依赖redis,没有做降级方案。
  2. 应用端jedispool设置了连接超时时间,但是过大,造成请求阻塞。
  3. 此redis集群为公用集群,至少有60个子系统在使用,每个子系统都有设置了最大连接数,理论上,是不会超出10000的。

redis集群排查

  1. 登录redis-cli,list连接清单,发现了某应用集群有大量的连接,并处于establish状态,断定来源IP为问题应用,并且应用使用了redis的发布订阅功能:subscribe。
一次redis集群连接数占满问题的排查_第2张图片
连接信息.png
  1. 登录问题应用服务器,使用netstat查看未发现到redis服务器的连接。由此定位问题为redis连接未及时释放。

长连接问题排查

  1. 查看网络拓扑为应用->防火墙->redis。登录防火墙,连接数依然为0-1个,确认问题为redis未及时回收连接。
  2. 查看操作系统tcp keepalive设置为7200s(2小时),未生效,推测此配置被redis配置覆盖。(在测试环境使用相同配置,进行tcpdump抓包测试,确认无误。)
  3. 查看redis配置tcp_keepalive为0,代表关闭tcp连接状态检查,与现象一致。
  4. 查看防火墙长连接配置为30分钟无流量主动断开连接,与现象一致。

结论

  1. 应用与redis之间有jupiter防火墙,防火墙30分钟无数据通信,会拆连接,拆的时候不会通知两端回收连接。
  2. redis-server设置了keepalive=0,此配置覆盖了操作系统的keepalive=7200s,导致redis-server不主动检测连接状态,所以不会主动回收连接。
  3. 客户端应该是jvm有默认设置,或者走了操作系统的配置(待验证),所以客户端机器上可以正常回收连接。

解决方案

  1. redis设置tcp_keepalive=60s
  2. 应用程序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还是搞缓存吧

你可能感兴趣的:(一次redis集群连接数占满问题的排查)