redis connect time out /connection refused

出现redis的“Could not get a resource from the pool”真实原因目前有几种:
1、Caused by: redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: connect timed out------------------------------redis与redis服务端连接超时,导致创建连接失败
2、Caused by: redis.clients.jedis.exceptions.JedisConnectionException: java.net.ConnectException: Connection refused -----------------------------------redis连接拒绝
3、Caused by: java.util.NoSuchElementException: Timeout waiting for idle object-----------------------------------------------------------------------------redis连接池的最大连接数太小(生产没有出现过)

 

二、Caused by: redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: connect timed out

2.1、分析和怀疑:
   业务端一般认为redis出现问题,就是redis云有问题,人的“正常”思维:看别人错误容易,发现自己难,扯多了, 出现这个有很多原因:
   (1). 网络原因:比如是否存在跨机房、网络割接等等。
   (2). 慢查询,因为redis是单线程,如果有慢查询的话,会阻塞住之后的操作。 
   (3). value值过大?比如value几十兆,当然这种情况比较少,其实也可以看做是慢查询的一种
   (4). aof重写/rdb fork发生?瞬间会堵一下Redis服务器。
   (5). 其他..................
 
2.2、 查询原因
   演讲者一开始怀疑是网络问题,但是并未发现问题,观察各种对比图表,tcp listenOverFlow和timeout经常周期出现。(赞一下这个监控,我们监控现在还没有这个层面的)
   有关listenOverFlow:
查看现有的连接数是否大于设置的backlog,如果大于就丢弃,并相应的参数值加1。其中backlog是由程序和系统参数net.core.somaxconn共同设置,当backlog的值大于系统设置的net.core.somaxconn时则取net.core.somaxconn的值,否则取程序设置的backlog值。这种出错的方式也被记录在TcpListenOverflows中(其只记录了连接个数不足而产生溢出错误的次数!)。
   觉得可能和TCP相关,于是分析了Tcp三次握手:最后一次握手客户端的请求会进入服务器端的一个队列(可以认为是下三图)中,如果这个队列满了,就会发生上面的异常。(accept)
  (1) TCP三次握手: 
  (2) redis客户端与redis服务器交互的过程(本质就是TCP请求)

 

 

  (3) I/O 多路复用程序通过队列向文件事件分派器传送套接字的过程
    
   (4) 和redis有什么关系呢?
        由于Redis的单线程模型(对命令的处理和连接的处理都是在一个线程中),如果存在慢查询的话,会出现上面的这种情况,造成新的accept的连接进不了队列。
    
    如果上面的图没法理解的话,看看这张图:
      
 
  2.3、 解决方法:
    (1) 对慢查询进行持久化,比如定时存放到mysql之类。(redis的慢查询只是一个list,超过list设置的最大值,会清除掉之前的数据,也就是看不到历史)
    (2) 对慢查询进行报警(频率、数量、时间)等等因素
    (3) 其实应该做的是:对业务端进行培训,告诉他们一下redis开发的坑,redis不是万金油,这个和Mysql DBA要培训Mysql使用者一样,否则防不胜防。
      比如他执行了 monitor, keys *, flushall, drop table, update table set a=1; 这种也是防不胜防的(当然也可以做限制),但是提高工程师的水平才是关键。
   
 
3. Read timed out 常见可能成因是网路问题,客户端服务器太忙,redis太忙。由您提供的2020-07-18 02:27的时间点,客户端资源用量低,redis资源用量也不高。查看slowlog都被此命令占满:
1) 1) (integer) 18718 2) 2020-07-22 02:22:32 UTC 3) (integer) 19968 4) 1) "KEYS"
2) "CHANNEL_ALERT_QUEUE*" 2) 1) (integer) 18717 2) 2020-07-22 02:12:32 UTC 3) (integer) 19991 4) 1) "KEYS" 2) "CHANNEL_ALERT_QUEUE*"
3) 1) (integer) 18716 2) 2020-07-22 02:02:32 UTC 3) (integer) 20417 4) 1) "KEYS" 2) "CHANNEL_ALERT_QUEUE*"
4) 1) (integer) 18715 2) 2020-07-22 01:52:32 UTC 3) (integer) 20249 4) 1) "KEYS" 2) "CHANNEL_ALERT_QUEUE*" ...
由于keys命令 [1]原本就是个比较花效能的命令,因此是正常的,且花的时间约20ms,应该不是1.5s超时的原因。但由于slowlog最高纪录128笔,无法确认read timeout时有无命令阻塞redis。 检查网路问题,这个比较难查了,我查看了底层网路监控,但没发现掉包率增加,不过有些暂时网路问题底层监控也不一定能抓到。以当前网路技术,网路设备有时有中断其实也是正常的,此时也多是从应用端重试请求来处理。因此对于Read timed out建议如下:
2.a 先优化服务,完成连接池长连接设定,增加效能,降低连接资源用尽风险。减少timeout可能因素
2.b 在应用中有合理的重试机制
2.c 持续收集有read timeout的时间点及实例资料,如果过于频繁,我将上报给底层网路团队作深入调查
代码重连示例:
//所以要从根本上解决自动重连问题,需要捕获redisTemplate操作的异常,然后重新初始化redisTemplate的连接工厂connectionFactory,如下:

        try {
            String result = redisTemplate.execute(new RedisCallback<String>() {
                @Override
                public String doInRedis(RedisConnection redisConnection) throws DataAccessException {
                    RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
                    byte[] value = redisConnection.get(serializer.serialize(key));
                    return serializer.deserialize(value);
                }
            });
            return result;
        } catch (RedisException e) {
            LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(); // 注:这里使用的是Lettuce而非Jedis客户端
            connectionFactory.setHostName("10.20.39.223");
            connectionFactory.setPort(6379);
            connectionFactory.setValidateConnection(true);
            connectionFactory.initConnection();
            redisTemplate.setConnectionFactory(connectionFactory);
            throw e;
        }

 

aa
 
posted on 2015-11-30 23:36  duanxz  阅读(2678)  评论(0编辑  收藏  举报