Redis sentinel 连接数>限制导致业务异常
/ / 阅读数:3310摘要 :使用 redis-py 访问 Redis Sentinel 时,要随机打乱 Sentinel 地址,否则客户端所有连接都集中到相同 Sentinel,很容易触 > 发 maxclients 限制引起业务异常。
问题表现
- 业务日志出现
raise MasterNotFoundError ("No master found for % r" % (service_name,))
异常; - 使用
redis-cli
连接 Redis Sentinel 地址提示reset by peer
错误。
查证过程
在 Sentinel 机器上执行ss -ant |grep 26379
,发现有大量连接。通过查看 Redis 源码发现,
默认情况下 Redis Sentinel 限制最多接受 10000 个连接(maxclients
配置),
而且无法使用 CONFIG SET
动态修改该配置,只能通过修改配置文件,然后重启进程的方式来修改。
问题原因
我们业务使用的是 Python redis-py 模块来操作 Redis,它支持传递多个 Sentinel 地址来初始化:
import redis.sentinel sentinels_addrs = [('127.0.0.1', 26379), ('127.0.0.1', 26380), ('127.0.0.1', 26381)] sentinel = redis.sentinel.Sentinel(sentinels_addrs, socket_timeout=1) master = sentinel.master_for('test') |
是什么原因导致 Sentinel 连接数过大呢?
通过查看 redis-py 源码,原来其内部在 Sentinel 获取 master 或 slave 时, 会按照给定的 Sentinel 列表顺序依次连接尝试获取,也就是说,几乎所有进程都只会连接到第一个可用的 Sentinel:
class Sentinel(object): def __init__(self, sentinels, min_other_sentinels=0, sentinel_kwargs=None, **connection_kwargs): ... self.sentinels = [Redis(hostname, port, **self.sentinel_kwargs) for hostname, port in sentinels] ... def discover_slaves(self, service_name): "Returns a list of alive slaves for service ``service_name``" for sentinel in self.sentinels: # 按传入的Sentinel顺序依次尝试 try: slaves = sentinel.sentinel_slaves(service_name) except (ConnectionError, ResponseError, TimeoutError): continue slaves = self.filter_slaves(slaves) if slaves: return slaves return [] |
这就会导致所有客户端都集中连接到第一个 Sentinel,进而触发了 Sentinelmaxclients=10000
的默认限制,导致连接异常。
解决办法
随机打乱 Sentinel 列表地址
在业务层随机打乱 Sentinel 列表地址,避免所有客户端都连接到相同的 Sentinel:
import random import redis.sentinel sentinels_addrs = [('127.0.0.1', 26379), ('127.0.0.1', 26380), ('127.0.0.1', 26381)] random.shuffle(sentinels_addrs) # 打乱顺序,避免都连到第一个Sentinel sentinel = redis.sentinel.Sentinel(sentinels_addrs, socket_timeout=1) master = sentinel.master_for('test') |
Sentinel 和 Redis-Server 开启 keepalive 配置
Redis 服务端也需要开启 keepalive 配置 (Redis>=3.2.1 已默认开启),避免网络异常情况下,一直保持大量无效的连接:
# TCP keepalive. # # If non-zero, use SO_KEEPALIVE to send TCP ACKs to clients in absence # of communication. This is useful for two reasons: # # 1) Detect dead peers. # 2) Take the connection alive from the point of view of network # equipment in the middle. # # On Linux, the specified value (in seconds) is the period used to send ACKs. # Note that to close the connection the double of the time is needed. # On other kernels the period depends on the kernel configuration. # # A reasonable value for this option is 300 seconds, which is the new # Redis default starting with Redis 3.2.1. tcp-keepalive 300 |
完善 Sentinel Server 监控
之前线上只对 Redis Server 的连接数、CPU、内存、其他指标(可通过redis-cli info
获取)进行了监控,需要 Sentinel Server 监控也补全。