23.06.15
DEVOTEE를 활성화 시키면
지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.
버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.
| 컨텐츠 유형 | 제목 | 저장일 | 삭제 |
|---|
본인인증 로그인에 실패하였습니다.
회원이 아니시거나 본인인증 등록이
완료되지 않은 사용자입니다.
안녕하세요. SK Hynix의 AI Transformation 팀 김지수입니다.
이번에 업무를 진행하면서 redis sub / pub 구조를 고민하게 된 과정 대해 공유 차 작성하게 되었습니다.
비슷한 고민이 있으시다면 함께 나눠보면 좋을 것 같네요.
우선 서버-인스턴스 간 통신을 통해 특정 작업을 진행하는 과정이 있습니다.
기존엔 SSH를 통해 인스턴스에서 작업을 지시하는 방식으로 구현되었으나 대량의 작업이 필요할 경우,
서비스의 고도화를 위해 다수의 클라이언트 서버와 통신해야 하는 과정에서 병목이 발생하였습니다.
이해를 돕기 위해 간단하게 도식화 해보면 다음과 같습니다.
자 그럼 이 문제를 해결하기 위해 어떤 고려 사항이 있을지 정리해보면..
기존 시스템에 빠르게 변경 가능한 구조
유연한 scale out 구조로 전환
로깅 & 유지 보수 용이
확장 가능한 모듈 형태
Latency가 중요하진 않음
client, worker, pod의 개수가 가변적
Redis DB
정도가 있겠네요
다음으로는 병목의 주 원인이었던 SSH 구조를 대체할 만한 프레임워크를 프롬프트를 통해 정리해 보았습니다.
라이브러리/프레임워크 | 주요 특징 | 장점 | 단점 |
|---|---|---|---|
gRPC | 구글에서 개발한 고성능 RPC 프레임워크, Protocol Buffers 기반 | - 빠르고 효율적인 바이너리 전송 - 다양한 언어 지원 - 양방향 스트리밍 지원 - 자동 코드 생성 | - REST에 비해 진입장벽 높음 - 디버깅/모니터링 어려움 - 브라우저 직접 지원 제한적 |
RESTful API (Flask, Express 등) | HTTP 기반의 표준 API 통신 방식 | - 쉬운 구현과 디버깅 - 브라우저 및 다양한 클라이언트 지원 - 인프라 친화적 | - 텍스트 기반(성능 한계) - 스키마 강제성 약함 - 실시간/양방향 통신에 부적합 |
RabbitMQ (AMQP) | 메시지 브로커, 비동기 메시지 큐 | - 신뢰성 높은 메시지 전달 - 다양한 패턴(큐, pub/sub 등) 지원 - 확장성 우수 | - 운영 복잡성 - 실시간성은 다소 떨어짐 - 추가 인프라 필요 |
Redis Pub/Sub | 인메모리 데이터베이스의 pub/sub 기능 | - 빠른 메시지 전달 - 간단한 구조 - 다양한 언어 지원 | - 메시지 유실 가능성(구독 중단 시) - 메시지 영속성 없음 - 복잡한 라우팅/확장성 한계 |
저희는 Latency가 크게 중요하지 않으며 구성 서버들이 가변적이므로 비동기 방식을 항상 channel을 subscribe 하는 worker를 둔다면 로깅 문제도 해결할 수 있겠네요.
표로 보았을 때, redis Pub/Sub 구조가 가장 적당해 보입니다.
그럼 pub / sub 구조로 구현하는 것으로 하고 한 번 자세한 개념을 알아보죠.
Redis Pub/Sub은 Redis의 기능을 활용한 메시지 브로커 시스템의 일종입니다.
이 시스템은 데이터를 '발행자'(publisher)가 특정 '채널' 또는 '주제'(topic)에 메시지를 보내면,
이 채널을 구독하고 있는 '구독자'(subscriber)들이 메시지를 받아 처리할 수 있도록 해주는 통신 방식입니다.
유튜브 채널 구독과 비슷하게, 크리에이터가 새 글을 발행하면 구독자에게 알림이 오는 원리와 유사하다고 설명될 수 있습니다.
아하~ 위 예를 보면 인스턴스(publisher)가 무수히 많이 늘어나더라도 간단하게 서버와 인스턴스 간 Pub / Sub 으로 비동기 통신을 구현할 수 있겠네요. 아래 그림처럼 말이죠.
cursor로 Mockup code도 요청해 보았습니다.
import redis
import time
REDIS_HOST = 'localhost'
TASK_QUEUE = 'task_queue'
RESULT_CHANNEL = 'task_result'
def main():
r = redis.Redis(host=REDIS_HOST, port=6379, db=0)
pubsub = r.pubsub()
pubsub.subscribe(RESULT_CHANNEL)
# 작업 큐에 작업 넣기
for i in range(5):
task = f"do_something:{i}"
r.rpush(TASK_QUEUE, task)
print(f"[SERVER] Queued: {task}")
print("[SERVER] Waiting for results...")
received = 0
while received < 5:
message = pubsub.get_message()
if message and message['type'] == 'message':
print(f"[SERVER] Result received: {message['data'].decode()}")
received += 1
time.sleep(0.1)
if __name__ == "__main__":
main()import redis
import time
REDIS_HOST = 'localhost'
TASK_QUEUE = 'task_queue'
RESULT_CHANNEL = 'task_result'
def main():
r = redis.Redis(host=REDIS_HOST, port=6379, db=0)
print("[WORKER] Waiting for tasks...")
while True:
task = r.blpop(TASK_QUEUE, timeout=0)
if task:
task_data = task[1].decode()
print(f"[WORKER] Processing: {task_data}")
# 실제 작업 처리 (예시로 2초 sleep)
time.sleep(2)
result = f"{task_data} done"
r.publish(RESULT_CHANNEL, result)
print(f"[WORKER] Published result: {result}")
if __name__ == "__main__":
main()실제 구현은 좀 다르게 되겠지만 의도대로 동작하는 코드가 만들어졌습니다.
읽어주셔서 감사합니다.
DEVOTEE를 활성화 시키면
지금 작성한 댓글에 AI가 댓글을 달아줍니다.