spring boot的redis连接数过多导致redis服务器压力过大的一次问题排查

一、背景

在今天上午的时候,突然收到大量的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

bash 复制代码
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,详见下:

如果需要池化技术,你需要额外引入线程池框架。(因为我这项目里没有引入,所以看到是标红的,编译不通过)

java 复制代码
<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#连接redis

2、jedis

由于,我们没有使用jedis了,相信很多人也不会弃用lettuce而用它。

所以,这里不打算对其连接进行很细的描述。

当我们使用springboot框架的时候,你只要看spring-boot-autoconfigure.jar的实现。

org.springframework.boot.autoconfigure.data.redis.JedisConnectionConfiguration

java 复制代码
    private void applyPooling(RedisProperties.Pool pool,
			JedisClientConfiguration.JedisClientConfigurationBuilder builder) {
		builder.usePooling().poolConfig(jedisPoolConfig(pool));
	}

配置RedisProperties.Pool赋值给JedisPoolConfig。

3、redisson

java 复制代码
Config config = new Config();
config.useSingleServer()
        .setAddress("redis://127.0.0.1:6379");

RedissonClient redisson = Redisson.create(config);

顺着代码往后看:

java 复制代码
public SingleServerConfig useSingleServer() {
        return this.useSingleServer(new SingleServerConfig());
    }

所以,只需要看类org.redisson.config.SingleServerConfig的成员变量以及构造函数。

java 复制代码
    private int connectionMinimumIdleSize = 24;
    private int connectionPoolSize = 64;

所以,需要将最前的代码,稍加修改如下:

java 复制代码
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连接超时且切换了主从。

再说下去就是阴谋论了,水平有限,从目前获取到的信息看,只能把程序本来存在的旧问题给修复好,后期再看监控对比吧。

相关推荐
wanhengidc3 分钟前
云手机的硬件依赖性如何?
运维·服务器·智能手机·云计算
VekiSon14 分钟前
Linux系统编程——标准IO
linux·运维·服务器
期待のcode33 分钟前
springboot热部署
java·spring boot·后端
Somehow00742 分钟前
Spring Boot 集成 ElasticSearch 的简单示例
spring boot·设计
Evan芙1 小时前
DNS服务器类型,解析答案,正反解析域,资源记录定义总结
运维·服务器
wanhengidc1 小时前
巨椰 云手机办公便利性高
运维·服务器·安全·智能手机·云计算
Saniffer_SH1 小时前
【每日一题】PCIe答疑 - 接大量 GPU 时主板不认设备或无法启动和MMIO的可能关系?
运维·服务器·网络·人工智能·驱动开发·fpga开发·硬件工程
Saniffer_SH1 小时前
【每日一题】讲讲PCIe链路训练和枚举的前后关系
运维·服务器·网络·数据库·驱动开发·fpga开发·硬件工程
2401_861786181 小时前
linux修改ip地址(有详细步骤)kali
linux·运维·服务器
vx_bisheyuange2 小时前
基于SpringBoot的老年一站式服务平台
java·spring boot·后端·毕业设计