목차
개요
FastAPI에 Redis를 이용해 비동기 통신에 대한 성능 테스트를 시험 중이었다. locust에 Numbers of users는 1000 spawn rate는 100으로 설정하고 worker는 3개 정도를 주어 테스트 했다. FastAPI의 로그를 보고 있으니 다음과 같은 에러가 발생하고 있는 걸 확인했다.
위에 첨부한 이미지에서 확인할 수 있다시피 redis connection과 관련된 에러이다. 다음은 이 에러의 TraceBack 중 일부이다.
...
File "/Users/jako/private/git-repo/backend-playground-src/coupons/src/repository/redis_repository.py", line 33, in sismember
return self.client.sismember(key, value)
File "/Users/jako/private/git-repo/backend-playground-src/venv/lib/python3.10/site-packages/redis/commands/core.py", line 3391, in sismember
return self.execute_command("SISMEMBER", name, value)
File "/Users/jako/private/git-repo/backend-playground-src/venv/lib/python3.10/site-packages/redis/client.py", line 533, in execute_command
conn = self.connection or pool.get_connection(command_name, **options)
File "/Users/jako/private/git-repo/backend-playground-src/venv/lib/python3.10/site-packages/redis/connection.py", line 1086, in get_connection
connection.connect()
File "/Users/jako/private/git-repo/backend-playground-src/venv/lib/python3.10/site-packages/redis/connection.py", line 270, in connect
raise ConnectionError(self._error_message(e))
redis.exceptions.ConnectionError: Error 49 connecting to 127.0.0.1:6379. Can't assign requested address.
아무래도 Connection 관련 에러이다 보니 어떻게 됐든 해결방법을 알아봤다.
1. 왜 일어나는가?
최근 작성한 글과 같은 맥락으로 접근해 봤다. 아무래도 connection 에러이고 성능 테스트 중에 발생한 것이다 보니 모종의 이유로 연결할 수 있는 connection의 수가 부족해지기 때문이지 않을까 싶었다. 이에 착안점을 두고 구글링을 진행했다.
connection이 부족한 것이다보니 "connection pool을 설정해주면 되는 거 아닌가?" 와 같은 생각이 먼저 떠올랐는데 조금 더 조사해보니 "MSL"이라는 값을 조정해서도 관련 에러를 해결할 수 있단 걸 알게 되었다.
그래서 간단히 Redis Connection Pool을 이용하는 방법과 MSL을 이용하는 방법을 기록하려 한다.
2. Redis Connection Pool을 사용해서 해결하기
"connection pool을 사용해볼까"라는 아이디어는 DBCP에서 힌트를 얻게됐다. connection을 관리하는 매커니즘은 뭔가 비슷하지 않을까..
그러나 Redis가 필요한 상황에 자주 놓여있진 않아서 Redis도 connection pool을 사용할 수 있을지는 의문이었다. 설마 connection pool이 없더라도 이와 비슷한 무언가는 있겠지 싶어 조사를 진행했는데 다행이 Redis도 Connection Pool이 존재했다.
Python에서는 다음과 같은 형태로 Redis Connection Pool을 사용할 수 있다.
import redis
redis_host = "0.0.0.0"
redis_port = 6379
redis_connection_pool = redis.ConnectionPool(
host=redis_host,
port=redis_port,
db=1
)
def redis_client():
return redis.Redis(connection_pool=redis_connection_pool)
Connection Pool을 가지고 rediRedis Lock이 필요하다면 다음과 같이 사용할 수 있다.
from redis_lock import Lock
# Redis Lock
with Lock(name=f"redis_lock_{id}", redis_client=redis_client(), expire=60, auto_renewal=True):
...
3. Maximum Segment Lifetime?
MSL은 Maximum Segment Lifetime의 약자이다.
연결이 종료된 후에 해당 포트가 다시 사용 가능하기까지의 시간을 나타낸다. 즉 연결이 종료된 후에 해당 포트는 지정된 시간만큼 다른 연결에서 사용할 수 없게 되는 것이다.
"개요"에서 언급한 "Can't assign requested address."가 일어나는 이유라 짐작된다.
MSL의 값은 Mac Os에서 다음과 같은 명령을 통해 값을 조정할 수 있다.
$ sudo sysctl -w net.inet.tcp.msl=15000
Mac OS 및 기타 유닉스 기반 시스템에서의 기본적으로 2분이라고 하니 이보다 더 작은 값을 설정하여 관련 에러를 해결할 수 있었다.
local 환경에서는 redis connection error를 해결할 목적으로 MSL을 작게 설정했다. 그러나 이 설정을 실제 운영 중인 서버에 적용한다 가정하면 주의가 필요하다고 한다.
MSL 값을 너무 작게 설정하면 TIME_WAIT 상태의 연결이 충돌할 수 있다고 하니 네트워크 상황 및 기타 여러 특성을 잘 고려하여 조정하도록 하자.
'Server > Redis' 카테고리의 다른 글
Redis, Persistence - AOF (0) | 2022.12.11 |
---|---|
Redis, Collections - Sorted Set (0) | 2022.12.03 |
Redis, Collections - Hashes (0) | 2022.11.27 |
Redis, Collection - Sets (0) | 2022.11.27 |
Redis, Collection - Lists (0) | 2022.11.27 |