개요
2024.08.07-[Python] redis - pub/sub 구현하기에서 Python Redis 라이브러리를 사용해 pubsub를 구현했었는데, subcriber에서 서 예기치 않은 연결 종료로 인해 구독이 멈춘 이력이 있다. 구현 코드에 연결 재시도 로직이 따로 없어서 추가 구현이 필요할 것 같다.
그보다 우선 Redis-py에서 제공하고 있는 재시도 기능부터 살펴보려고 한다.
retry
Redis-py는 실패 후 특정 횟수만큼 재시도를 수행하는 Retry 기능을 제공하고 있다.
from redis.retry import Retry
Retry(backoff, retries, supported_errors=(<class 'redis.exceptions.ConnectionError'>, <class 'redis.exceptions.TimeoutError'>, <class 'socket.timeout'>))
기본적으로 재시도 간격, 그리고 재시도 횟수를 지정해야 하는 것 같다.
Retry 객체는 실패할 가능성이 있는 작업을 실행하고 결과를 반환하는 함수나 지정한 오류로 갱신하는 등의 함수를 제공한다.
하지만 이번 글에서는 기본적인 연결 재시도를 구현하는 방법을 우선 확인해 본다.
연결 재시도
Redis 클라이언트는 재시도 동작을 구성하기 위해 3개의 매개변수를 지원한다.
r = Redis(host='localhost', port=6379, retry=retry, retry_on_error=[BusyLoadingError, ConnectionError, TimeoutError])
- retry : 백오프와 최대 재시도 횟수를 지정한 Retry 객체
- retry_on_error : 재시도를 수행할 예외 목록
- retry_on_timeout : True면 TimeoutError에 대해서만 재시도를 수행한다.
즉, Redis 클라이언트에 재시도 로직을 추가하고 싶다면 retry 매개변수에 Retry 객체를 전달하면 된다.
예시 )
from redis.backoff import ExponentialBackoff
from redis.retry import Retry
from redis.client import Redis
from redis.exceptions import (
BusyLoadingError,
ConnectionError,
TimeoutError
)
# 최대 3번까지 재시도
# 채시도 간격은 1부터 두 배씩 증가하여 최대 5초까지
retry = Retry(ExponentialBackoff(cap=5, base=1), 3)
# retry_on_error에 해당하는 오류 발생 시 재시도
r = Redis(host='localhost', port=6379, retry=retry, retry_on_error=[BusyLoadingError, ConnectionError, TimeoutError])
r.ping()
연결이 안 되는 Redis 클러스터에 접속을 시도하면 아래와 같은 오류를 확인할 수 있다.
Traceback (most recent call last):
File "/home/test/miniforge3/lib/python3.10/site-packages/redis/connection.py", line 698, in connect
sock = self.retry.call_with_retry(
File "/home/test/miniforge3/lib/python3.10/site-packages/redis/retry.py", line 51, in call_with_retry
raise error
File "/home/test/miniforge3/lib/python3.10/site-packages/redis/retry.py", line 46, in call_with_retry
return do()
File "/home/test/miniforge3/lib/python3.10/site-packages/redis/connection.py", line 699, in <lambda>
lambda: self._connect(), lambda error: self.disconnect(error)
File "/home/test/miniforge3/lib/python3.10/site-packages/redis/connection.py", line 987, in _connect
raise err
File "/home/test/miniforge3/lib/python3.10/site-packages/redis/connection.py", line 975, in _connect
sock.connect(socket_address)
TimeoutError: [Errno 110] Connection timed out
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/data001/web_typhoon/redis_retry_test.py", line 16, in <module>
r.ping()
File "/home/miniforge3/lib/python3.10/site-packages/redis/commands/core.py", line 1194, in ping
return self.execute_command("PING", **kwargs)
File "/home/miniforge3/lib/python3.10/site-packages/redis/client.py", line 1255, in execute_command
conn = self.connection or pool.get_connection(command_name, **options)
File "/home/miniforge3/lib/python3.10/site-packages/redis/connection.py", line 1442, in get_connection
connection.connect()
File "/home/miniforge3/lib/python3.10/site-packages/redis/connection.py", line 702, in connect
raise TimeoutError("Timeout connecting to server")
redis.exceptions.TimeoutError: Timeout connecting to server
참고 문서
https://redis-py.readthedocs.io/en/latest/retry.html