今天线上生产环境Redis报错: No reachable node in cluster

Redis使用的客户端: jedis:3.0.0

首先看一下报错位置,位于redis.clients.jedis.JedisSlotBasedConnectionHandler#getConnection()

java
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
  • 09
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
@Override public Jedis getConnection() { List<JedisPool> pools = cache.getShuffledNodesPool(); for (JedisPool pool : pools) { Jedis jedis = null; try { jedis = pool.getResource(); if (jedis == null) { continue; } String result = jedis.ping(); if (result.equalsIgnoreCase("pong")) return jedis; jedis.close(); } catch (JedisException ex) { if (jedis != null) { jedis.close(); } } } throw new JedisNoReachableClusterNodeException("No reachable node in cluster"); }

大致这段代码的意思是找到Redis集群中的所有Master结点,并对redis结点发起一次ping请求,如果返回pong就代表该结点是可用的,如果没有任何一个redis结点可用,就会报No reachable node in cluster异常

这里还有一个条件是jedis==null的时候continue,但是pool.getResource()代码如下:

java
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
@Override public Jedis getResource() { Jedis jedis = super.getResource(); jedis.setDataSource(this); return jedis; }

明显jedis为空的时候就会报空指针异常,所以可以忽略这个条件

因此先检查一下pod的状态

shell
    kubectl get pods | grep redis

    发现结点都是1/1的状态,那就可以进redis ping 一下试试

    shell
    • 01
    127.0.0.1:6379 > ping (error) ERR max number of clients reached

    这里的报错大致意思是redis的客户端连接数到达了上线,因此拒绝了更多的请求。redis默认的配置最大客户端数是10000,可以通过maxclients修改。

    但显然仅仅增加最大连接数是治标不治本,如果redis可用,我们还可以通过info clients查看当前连接以及通过client list查看连接列表,这里我们只能通过linux的网络连接来看了

    shell
      netstat -na | grep current_ip:6379 | awk '{print $5}' | awk -F ':' '{count[$1]++;} END {for(i in count) {print i,count[i]}}'

      通过上面这个命令,可以打印出哪个ip与当前客户端建立了多少个请求,结果就发现有一个ip,建立了9k多个请求,立马就确定是当前这个服务出现了异常。

      由于这个服务与业务无关,因此先暂时停止该服务,过一段时间后,redis连接恢复,业务正常执行。

      那到底是为啥这个服务建立了9k多个请求呢。

      看了一下,里面有四五个定时任务,比如判断redis是否存活,响应时间等等,每个定时任务做判断的时候都会去new一个Jedis客户端。

      逆天,jedis不是有自带的JedisPool不用。。。虽然每处代码都close了,但如果由于网络原因close不及时或出现问题(这里还用的是IOUtils.closeQuitely,报错都不会显示)Redis连接数久而久之就占满了。

      以上。