sentinel 连接数异常

本文记录了一次生产环境中Redis Sentinel集群出现连接数异常增加的问题排查过程。通过分析代码及网络配置,最终定位到防火墙设置及keepalive配置不合理导致的连接重连问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

生产使用redis一段时间后,生产配置为sentinel方式的集群,为三台,有一天部署项目上线出现问题如下jedis.exceptions.JedisDataException: ERR max number of clients reached

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'redisTemplate' defined in class path resource [application.xml]: Cannot resolve reference to bean 'jedisConnectionFactory' while setting bean property 'connectionFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jedisConnectionFactory' defined in class path resource [application.xml]: Invocation of init method failed; nested exception is redis.clients.jedis.exceptions.JedisDataException: ERR max number of clients reached
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328) ~[spring-beans-3.1.0.RELEASE.jar:3.1.0.RELEASE]
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:106) ~[spring-beans-3.1.0.RELEASE.jar:3.1.0.RELEASE]

到redis服务器查看连接数,发现连接数为10004,重启sentinel,问题解决,但是sentinel连接数依然在增加

netstat -nap|grep 26379|wc -l

到weblogic服务器查看连接数 发现为15个,查看代码,redistemplate初始化一个JedisSentinelPool,JedisSentinelPool创建三个线程订阅了sentinel,每个线程与sentinel创建一个连接,weblogic部署了三个应用, 应该为9个连接,发现进程为27710的与sentinel 建立了9个连接

    for (String sentinel : sentinels) {
      final HostAndPort hap = toHostAndPort(Arrays.asList(sentinel.split(":")));
      MasterListener masterListener = new MasterListener(masterName, hap.getHost(), hap.getPort());
      masterListeners.add(masterListener);
      masterListener.start();
    }

接着查 在weblogic服务器上面使用命令,将test.hprof导入到jvisualvm.exe查看类有两个JedisSentinelPool类和实例三个

jrcmd 27710  hprofdump filename=test.hprof
27710:

返回查看项目代码发现单独另外初始化了一个JedisSentinelPool,并且没有加入destroy-method,weblogic 重新部署的时候MasterListener 线程没有关闭,所以没重新部署一次都会增加三个连接,加入destroy-method后重部署客户端sentinel连接正常喂9个每个应用三个连接

<bean id="jedisSentinelPool"
class="redis.clients.jedis.JedisSentinelPool" destroy-method="destroy">

至此问题似乎解决,但是sentinel连接数依然增加,使用命令,查看进程文件描述符发现有一段时间有很多文件描述符,意味着sentinel连接客户端被回收后又新建了很多连接,似乎是sentinel连接异常后客户端重新建立了连接,但是服务器没有释放,查看sentinel没有心跳检测,出现异常连接不是自动释放。

lsof -i:26379
ll /proc/{进程ID}/fd |grep {文件描述符}

理论上JedisSentinelPool只有初始化时才和sentinel建立连接查看JedisSentinelPool源码



      running.set(true);

      while (running.get()) {

        j = new Jedis(host, port);

        try {
          j.subscribe(new JedisPubSub() {
            @Override
            public void onMessage(String channel, String message) {
              log.fine("Sentinel " + host + ":" + port + " published: " + message + ".");

              String[] switchMasterMsg = message.split(" ");

              if (switchMasterMsg.length > 3) {

                if (masterName.equals(switchMasterMsg[0])) {
                  initPool(toHostAndPort(Arrays.asList(switchMasterMsg[3], switchMasterMsg[4])));
                } else {
                  log.fine("Ignoring message on +switch-master for master name "
                      + switchMasterMsg[0] + ", our master name is " + masterName);
                }

              } else {
                log.severe("Invalid message received on Sentinel " + host + ":" + port
                    + " on channel +switch-master: " + message);
              }
            }
          }, "+switch-master");

        } catch (JedisConnectionException e) {

          if (running.get()) {
            log.severe("Lost connection to Sentinel at " + host + ":" + port
                + ". Sleeping 5000ms and retrying.");
            try {
              Thread.sleep(subscribeRetryWaitTimeMillis);
            } catch (InterruptedException e1) {
              e1.printStackTrace();
            }
          } else {
            log.fine("Unsubscribing from Sentinel at " + host + ":" + port);
          }
        }
      }

果然当订阅失败后,会重新建立sentinel连接,但是为什么会订阅失败,查看weblogic日志,发现每隔7875s重新建立sentinel连接。至此进入死胡同,测试JedisSentinelpool的监听线程,发现订阅sentinel只要没有切换,一直就不会有消息,也就是说sentinel连接是不活动的,猜测是否有防火墙,导致senintel连接异常,果然生产使用了juniper防火墙,缺省情况下,Juniper防火墙对每一个会话的连接保持时间是30分钟(TCP)和1分钟(UDP),超时后状态表项将会被清除。顿时脑洞大开,修改防火墙策略,问题解决。
剩下最后一个问题,为什么是7875s,查看tcp,发现linux有个keepalive设置

sysctl -a |grep keep

net.ipv4.tcp_keepalive_intvl = 75
net.ipv4.tcp_keepalive_probes = 9
net.ipv4.tcp_keepalive_time = 7200

这个意思是tcp连接存活时间是7200s,然后每隔75发送一个keepalive包,重发9次,时间刚好7875s,Jedis在创建连接的时候设置keepalive=true,但是redis默认keepalive为0没有开启,sentinel是特殊的redis,启动时使用了redis keepalive参数,所以sentinel不会向客户端发送keepalive心跳包,客户端两小时会向服务端发送心跳包,但是此时连接已经被被防火墙设置为失效,然后客户端的连接设置为time_wait状态,服务器端连接依然为established,不会释放

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值