处理redis连接重置

Har*_*gar 5 python redis

我们[Errno 54] Connection reset by peer在应用程序中面临着非常随机的情况,看起来它是由 Redis 服务器而不是客户端触发的。Python的redis客户端有backoff策略实现,但无法处理这种情况。

官方仓库上也有 github 问题,最近很多人评论证实了这个问题。

重现步骤

$ ipython
# in python client
import redis
from redis.retry import Retry
from redis.exceptions import (TimeoutError, ConnectionError)
from redis.backoff import ExponentialBackoff

# connect client with exponential backoff retry
client = redis.StrictRedis(retry=Retry(ExponentialBackoff(cap=10, base=1), 25), retry_on_error=[ConnectionError, TimeoutError, ConnectionResetError], health_check_interval=1)

client.keys()
# print all keys
Run Code Online (Sandbox Code Playgroud)

现在直接从 Redis 服务器重置连接

$ redis-cli
RESET
Run Code Online (Sandbox Code Playgroud)

等待 120 秒或更长时间,然后再次运行客户端

# in client
client.keys()
---------------------------------------------------------------------------
ConnectionResetError                      Traceback (most recent call last)
<ipython-input-91-011ce9f936fc> in <module>
----> 1 client.keys("rq*")

~/path-to-python-env/env/lib/python3.8/site-packages/redis/commands/core.py in keys(self, pattern, **kwargs)
   1386         For more information check https://redis.io/commands/keys
   1387         """
-> 1388         return self.execute_command("KEYS", pattern, **kwargs)
   1389 
   1390     def lmove(self, first_list, second_list, src="LEFT", dest="RIGHT"):

~/path-to-python-env/env/lib/python3.8/site-packages/redis/client.py in execute_command(self, *args, **options)
   1168         pool = self.connection_pool
   1169         command_name = args[0]
-> 1170         conn = self.connection or pool.get_connection(command_name, **options)
   1171 
   1172         try:

~/path-to-python-env/env/lib/python3.8/site-packages/redis/connection.py in get_connection(self, command_name, *keys, **options)
   1315             # closed. either way, reconnect and verify everything is good.
   1316             try:
-> 1317                 if connection.can_read():
   1318                     raise ConnectionError("Connection has data")
   1319             except ConnectionError:

~/path-to-python-env/env/lib/python3.8/site-packages/redis/connection.py in can_read(self, timeout)
    793         if not sock:
    794             self.connect()
--> 795         return self._parser.can_read(timeout)
    796 
    797     def read_response(self, disable_decoding=False):

~/path-to-python-env/env/lib/python3.8/site-packages/redis/connection.py in can_read(self, timeout)
    315 
    316     def can_read(self, timeout):
--> 317         return self._buffer and self._buffer.can_read(timeout)
    318 
    319     def read_response(self, disable_decoding=False):

~/path-to-python-env/env/lib/python3.8/site-packages/redis/connection.py in can_read(self, timeout)
    222 
    223     def can_read(self, timeout):
--> 224         return bool(self.length) or self._read_from_socket(
    225             timeout=timeout, raise_on_timeout=False
    226         )

~/path-to-python-env/env/lib/python3.8/site-packages/redis/connection.py in _read_from_socket(self, length, timeout, raise_on_timeout)
    192                 sock.settimeout(timeout)
    193             while True:
--> 194                 data = self._sock.recv(socket_read_size)
    195                 # an empty string indicates the server shutdown the socket
    196                 if isinstance(data, bytes) and len(data) == 0:

ConnectionResetError: [Errno 54] Connection reset by peer
Run Code Online (Sandbox Code Playgroud)

第二次运行它效果很好,尽管它应该通过重试策略来处理。

client.keys()
# prints all keys
Run Code Online (Sandbox Code Playgroud)

配置

redis server - 6.2.6
python redis - 4.1.0
Run Code Online (Sandbox Code Playgroud)

我们可以围绕 redis 客户端编写自己的 try/catch,但我们正在使用一些库,例如rqflask-cache,它在内部使用 redis 并且没有接口来修改它的流程。

任何帮助深表感谢。

小智 -1

尝试使用连接池,因为它会保持活动状态并自动尝试在后台恢复。在 REPL 中

import redis
pool = redis.ConnectionPool(host="localhost", port=6379)
r = redis.Redis(connection_pool=pool)
r.ping()
Run Code Online (Sandbox Code Playgroud)

在另一个 shell 中,您可以通过运行 redis-cli 进行测试

client LIST
client kill IP:PORT
Run Code Online (Sandbox Code Playgroud)

然后运行

r.ping
Run Code Online (Sandbox Code Playgroud)

您应该在 redis-cli 中看到一个新连接

client LIST
Run Code Online (Sandbox Code Playgroud)