生产环境中流量高峰期会出现短时间的redis异常,主要报错如下:
根据redisson官方所述,RedisTimeoutException可能是多种原因造成的:
其中1,5,6点很容易确认,可以排除。接下来要考虑的就是2,3,4这几点。
在redisson中,Netty 线程负责发送命令到 Redis 服务器并接收响应。
它们处理底层的网络 I/O 操作,包括建立连接、读取和写入数据等。Netty 线程使用非阻塞的 I/O 模型,可以高效地处理多个并发连接和请求。
Redisson 通过配置参数 nettyThreads 来控制 Netty 线程的数量。增加 nettyThreads 的值可以提供更多的线程来处理并发的网络请求,从而增加 Redisson 与 Redis 之间的通信能力。然而,过多的线程数量可能会增加系统资源的消耗,因此需要根据实际情况进行适当的调整。
尝试将以下值作为 nettyThreads 的设置:32、64、128、256。
查看redisson客户端集群配置参数发现,生产环境中nettyThreads配置为32,而线上流量确实比较高,因此考虑将其调整为64。
而redis连接池最大为64,正常是够的。
根据github上redisson的#4381问题讨论,还进行了以下参数的优化:
1. 移除了fst解码器,因为此解码器是旧版本使用的,新版本使用默认的解码器就可以了
2. 设置keepAlive: true,该参数不指定的话默认为false
3. 调整了重试相关的参数,如超时时间和重试次数等
优化上线后,发现错误数量确实减少了,但还是存在少量报错。说明以上的优化是有一定效果的,但不是根本原因。最终经过多番排查发现,其实是第四点,也就是服务器CPU限制导致的。
生产环境是部署在k8s上,hpa扩容策略是根据cpu来扩容的。每次扩容后,新增的pod在刚开始启动的几分钟内,因为各种资源和配置项加载需要消耗较多的cpu,经过几分钟之后才会恢复到正常水平。在此期间,进入到该pod的请求就会由于cpu负载太高导致出现redis访问超时的问题。
出现错误日志的host和时间刚好与扩容的主机和扩容时间能对应上,这也证明了确实是此问题导致的。
想要判断pod的cpu是否达到了瓶颈,可以通过Prometheus的container_cpu_cfs_throttled_periods_total
和container_cpu_cfs_periods_total
这两个指标来计算。
CFS是linux系统默认的CPU调度器,用于公平地分配CPU时间片给运行在容器中的进程。当容器的CPU使用超过其资源限制时,CPU CFS会对容器进行限制。
container_cpu_cfs_throttled_periods_total
指标表示容器在 CPU CFS 中发生 CPU 限制的总周期数。每个周期的持续时间取决于 CPU CFS 的配置和容器的限制情况。该指标可以用于监控容器是否经历了 CPU 限制,并可以帮助评估容器的 CPU 使用情况和性能。如果这个值较高或持续增长,说明容器的 CPU 使用可能接近或超出了其资源限制,可能需要调整容器的资源配置或进行性能优化。
而container_cpu_cfs_periods_total
指标表示容器在 CPU CFS 中获得的总周期数。
注意,这两个指标均是针对单个容器的
通过统计一段时间内CPU受限周期数占总调度周期数的比例,可以判断出在这段时间内容器的cpu使用是否正常。
这也是上文中判断新启的pod在刚开始的几分钟内CPU被打满的依据。
分析了原因之后,那就可以想办法来优化了。
思路有两种,一是增加pod申请的CPU资源,保证新增的pod在系统初始化时有足够CPU使用。二是调整startUp探针的初始化时间,保证在刚开始的几分钟内请求不会进入到pod中(startUp探针的机制参见k8s工作负载(1))。
但这两种方案也会带来负面影响,增加CPU资源虽然会满足应用初始化时的CPU消耗,但系统平稳后太大的CPU就比较浪费了,而且会对根据CPU利用率来进行扩缩容的HPA策略有影响。增大startUp探针的初始化时间虽然可以让流量晚一点进入,但是也会降低扩容的速率。举例来说,在某个时间点需要扩容3个pod,原来经过2分钟时间就扩好了,但现在可能需要5分钟才能扩好。
总之,具体的优化措施需要结合实际的应用场景来考虑。
[1].https://blog.csdn.net/xiaoyi52/article/details/133277904
[2].https://github.com/redisson/redisson/issues/4381