一、背景
在今天上午的时候,突然收到大量的sentry报错,都是关于redis连接超时的警告。
首先想到的是去查看redis的监控,发现那个时间段,redis的请求数剧增,cpu使用率和带宽都陡增双倍。

下面的是redis监控的cpu情况
 
 最后贴一张redis的流量
 
到目前为止,可以看到redis的压力确实上来了。
随之,阿里云也给我们发来告警,说redis连接超时,导致主从切换。
于是,我们推测是程序的访问量剧增,接口中都又依赖redis,导致访问redis的请求等陡增。
当然,至于为什么会发生,是不是就是redis出问题了呢?最后又应该怎么调整?
是调整程序,还是加大redis的配置?
二、监控
从监控大盘能看到的信息有:httpq qps高达17k~18k,jvm节点的内存和gc等没有任何异常,毫无压力。但是redis访问却超时。(程序设置连接redis的超时时间为3秒)
1、http qps

2、arms

3、cloudDBA
缺少对redis客户端的连接监控,依赖cloudDBA的实例会话。
 
 而一般情况下,redis客户端有多少呢?1或2个,见下:

4、sentry

QueryTimeoutException
Redis command timed out; nested exception is io.lettuce.core.RedisCommandTimeoutException: 
Command timed out after 3 second(s)
 
三、redis连接数
1、lettuce

 源码见类LettuceConnectionFactory
 其中的构建函数中,可以看到,默认this.shareNativeConnection = true; 表示共享本地线程。
 
 下面,看看关于连接池的配置项:
详见类org.springframework.boot.autoconfigure.data.redis.RedisProperties,其中Pool类是跟线程池相关的配置。
 
 那么,是在哪个地方用到的呢?
 org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration.PoolBuilderFactory
把RedisProperties.Pool赋值给GenericObjectPoolConfig,详见下:
 
 如果需要池化技术,你需要额外引入线程池框架。(因为我这项目里没有引入,所以看到是标红的,编译不通过)
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.7.0</version>
</dependency
 
总结:lettuce连接redis,只会创建一个连接。
具体推荐一篇文章:https://www.cnblogs.com/throwable/p/11601538.html#%E8%BF%9E%E6%8E%A5redis
2、jedis
由于,我们没有使用jedis了,相信很多人也不会弃用lettuce而用它。
 所以,这里不打算对其连接进行很细的描述。
当我们使用springboot框架的时候,你只要看spring-boot-autoconfigure.jar的实现。
org.springframework.boot.autoconfigure.data.redis.JedisConnectionConfiguration

    private void applyPooling(RedisProperties.Pool pool,
			JedisClientConfiguration.JedisClientConfigurationBuilder builder) {
		builder.usePooling().poolConfig(jedisPoolConfig(pool));
	}
 
配置RedisProperties.Pool赋值给JedisPoolConfig。

3、redisson
Config config = new Config();
config.useSingleServer()
        .setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);
 
顺着代码往后看:
public SingleServerConfig useSingleServer() {
        return this.useSingleServer(new SingleServerConfig());
    }
 
所以,只需要看类org.redisson.config.SingleServerConfig的成员变量以及构造函数。
    private int connectionMinimumIdleSize = 24;
    private int connectionPoolSize = 64;
 

 所以,需要将最前的代码,稍加修改如下:
Config config = new Config();
config.useSingleServer()
        .setAddress("redis://127.0.0.1:6379")
        .setConnectionMinimumIdleSize(5)
        .setConnectionPoolSize(10)
        .setThreads(10)
        .setNettyThreads(2);
RedissonClient redisson = Redisson.create(config);
 
- setConnectionMinimumIdleSize(5) 设置了连接池的最小空闲连接数为 5
 - setConnectionPoolSize(10) 设置了连接池的最大连接数为 10
 - setThreads(10) 设置了 Redission 使用的线程数
 - setNettyThreads(2) 设置了 Netty 使用的线程数
 
Redission 使用线程池来处理异步操作,其中的线程数由 threads 配置项控制。较多的线程可能导致较多的连接。
总结:由于我们在使用redission的时候,采用的是默认值,所以连接池的最小连接数为24,这也趋近前文redis的客户端实例监控的数量(27)。
也可以说,之所以和其他服务相比,占用更多的连接,就是redission配置项使用的默认值所导致。
四、总结
jvm程序的内存和gc没有变化,在接口访问量陡增的情况下,我们根据目前得到的信息,决定修改程序代码redission的配置。
也就是说,减少程序对redis服务器的并发请求,至少不会让redis服务器的压力陡增。
一味地增加redis配置当然不可取,因为我们的redis配置已经是很高了。

 另外,我很怀疑阿里云在今天的表现,说实话,业务在没有变化非常大的情况下,不应该使得redis一下子就卡机了。
服务只是让redis的压力上升了,并不是会让得redis连接超时且切换了主从。
再说下去就是阴谋论了,水平有限,从目前获取到的信息看,只能把程序本来存在的旧问题给修复好,后期再看监控对比吧。


















