Redis pubsub 使用 keepalive 保活问题
/ / 阅读数:6023摘要 :Redis 通过长连接 block 方式订阅事件通知,如果连接异常断开导致半开连接,那么客户端将无法感知,永远不会收到事件通知。Redis-py 可以通过设置 keepalive 选项避免类 > 似问题。
问题描述
使用 Redis pubsub 做事件订阅时,偶尔会遇到因网络异常,导致服务断开连接,但是客户端没有感知,以为连接还正常,以至一直收不到事件推送。
复现方法
在 Redis 订阅成功后,在客户端机器上添加iptables
规则,把与服务端通信报文丢弃。然后重启 Redis 并去掉iptables
规则。
这时客户端会一直保持一个旧的 Established 连接,永不断开,也收不到任何事件。
# iptables -nL INPUT --line-numbers # 查看iptables规则 Chain INPUT (policy ACCEPT) num target prot opt source destination # # 等客户端订阅成功后 # iptables -I INPUT 1 -p tcp -s 127.0.0.1 --sport 6379 -j DROP # 丢弃服务端发来的任何报文 # iptables -nL INPUT --line-numbers Chain INPUT (policy ACCEPT) num target prot opt source destination 1 DROP tcp -- 127.0.0.1 0.0.0.0/0 tcp spt:6379 # supervisorctl restart redis_6379 # 重启redis-server # iptables -D INPUT 1 # 删除刚才添加的iptables规则 |
问题原因
上面遇到的这种情况叫做 TCP 半开连接 ,主要有以下原因导致:
连接长时间不活动而被代理或防火墙断连(主要原因)
HAProxy
代理默认 60s 后断开非活动连接。- 服务停止、网络波动、宕机、应用重启等导致连接异常断开
当出现半开连接后,除非使用该连接收发消息,否则无法检测到连接已经失效。
解决办法
升级 redis-py 版本 >=2.10.0
redis-py 版本 >=2.10.0 版本开始引入socket-keepalive
参数。
添加keepalive
相关参数
可以参考以下示例代码:
import platform import redis import socket import time from redis.exceptions import ConnectionError, TimeoutError if platform.system() == 'Linux': socket_keepalive_options = { socket.TCP_KEEPIDLE: 120, socket.TCP_KEEPCNT: 3, socket.TCP_KEEPINTVL: 5 } else: # 其他平台有些option不支持 socket_keepalive_options = None rd = redis.StrictRedis(host='127.0.0.1', port=6379, socket_connect_timeout=5, # 避免connect时卡住 socket_keepalive=True, # 启用TCP keepalive socket_keepalive_options=socket_keepalive_option ) ps = rd.pubsub(ignore_subscribe_messages=True) while True: try: ps.subscribe('event.test') for msg in ps.listen(): print(msg) except (ConnectionError, TimeoutError) as exc: # redis可能异常挂掉,keepalive检测到断线后会自动重连 print(exc) time.sleep(1) # sleep一会再试 continue |
添加完以上keepalive
配置后,客户端会以 block 方式等待事件,也会在需要的时候发送keepalive
探测包(probe packet)判断连接是否有效:
- 如果在 120s(TCP_KEEPIDLE)内没有收到任何网络数据
- 则会每 5s(TCP_KEEPINTVL)发送一个空包
- 如果连续 3 次(TCP_KEEPCNT)都没有收到 ACK 回复,则认为连接已经失效,抛出异常
- 业务端捕获异常,然后重试订阅
扩展资料
- Redis 服务端也要配置 keepalive 处理半开连接的问题
- 尽量使用 RabbitMQ 来替代 Redis 做事件发布订阅
keepalive
检测周期比较长,一般在业务层添加心跳逻辑来及时发现网络连接问题