일반적인 Redis Pub/Sub 구성 문제 해결
Redis Pub/Sub 구성 문제를 해결하여 안정적인 실시간 메시징을 보장하세요. 이 가이드는 불안정성의 주요 원인인 느린 소비자를 해결하기 위한 실행 가능한 단계를 제공하며, 중요한 `client-output-buffer-limit` 지시문을 사용합니다. `CLIENT LIST` 명령을 사용하여 메모리 급증을 진단하고, 전용 구독자 연결을 관리하며, 시스템 무결성을 유지하기 위한 대용량 Pub/Sub 격리 모범 사례를 구현하는 방법을 알아보세요.
일반적인 Redis Pub/Sub 구성 문제 해결
Redis Pub/Sub은 팀이 종종 운영할 필요가 없는 작은 메시지 브로커처럼 취급할 정도로 간단합니다. 퍼블리셔가 PUBLISH를 호출하면 구독자가 메시지를 수신하고 모든 것이 즉시 이루어집니다.
문제는 실제 네트워크, 느린 클라이언트, 재연결, 공유 Redis 인스턴스가 개입될 때 시작됩니다. Pub/Sub에는 메시지 기록, 확인, 재시도 대기열이 없습니다. 라이브 브로드캐스트 메커니즘입니다. 구독자가 없으면 해당 구독자에게 메시지가 사라집니다. 구독자가 연결되어 있지만 충분히 빠르게 읽을 수 없는 경우 Redis는 구성된 제한이 연결을 끊을 때까지 해당 클라이언트에 대한 보류 중인 출력을 유지해야 합니다.
이로 인해 Redis Pub/Sub은 캐시 무효화, 상태 업데이트, 라이브 대시보드 및 "최신 값" 알림에 적합합니다. 모든 메시지를 정확히 한 번 처리하거나 중단 후 재생해야 하는 워크플로우에는 적합하지 않습니다. 이러한 경우 Redis Streams, RabbitMQ, Kafka 또는 배송 추적 기능이 있는 다른 브로커를 사용하세요.
증상: Pub/Sub 트래픽이 많을 때 메모리 증가
가장 흔한 Pub/Sub 실패는 느린 구독자입니다. 퍼블리셔는 괜찮습니다. Redis는 처음에는 괜찮습니다. 하나의 소비자가 프로세스가 일시 중지되거나, 네트워크가 느리거나, 메시지 핸들러 내에서 많은 작업을 수행하기 때문에 뒤쳐집니다. Redis는 계속해서 해당 클라이언트의 출력 버퍼에 메시지를 씁니다. 버퍼가 유용한 제한 없이 계속 커지도록 허용되면 메모리 압력이 하나의 잘못된 구독자에서 전체 인스턴스로 퍼집니다.
클라이언트 확인:
redis-cli CLIENT LIST
Pub/Sub 명령과 큰 출력 메모리가 있는 클라이언트를 찾으세요:
id=88 addr=10.0.4.12:51244 flags=P db=0 sub=3 psub=0 omem=13421772 obl=128 oll=64 cmd=subscribe
유용한 필드:
flags=P는 Pub/Sub 클라이언트를 나타냅니다.sub및psub는 채널 및 패턴 구독 수를 표시합니다.omem은 바이트 단위의 출력 버퍼 메모리입니다.obl및oll은 출력 버퍼 백로그 세부 정보를 표시합니다.cmd=subscribe또는cmd=psubscribe는 클라이언트가 수행 중인 작업을 확인합니다.
구독자의 idle에 대해 당황하지 마십시오. Pub/Sub 연결은 명령 관점에서 유휴 상태일 수 있지만 여전히 푸시된 메시지를 수신합니다.
출력 버퍼 제한으로 느린 소비자 수정
Redis는 Pub/Sub 클라이언트에 자체 출력 버퍼 제한 클래스를 제공합니다:
client-output-buffer-limit pubsub 32mb 8mb 60
다음과 같이 읽으세요:
- Pub/Sub 클라이언트의 출력 버퍼가 32MB에 도달하면 즉시 연결을 끊습니다.
- 60초 동안 8MB 이상 유지되면 연결을 끊습니다.
정확한 숫자는 트래픽 및 클라이언트 동작과 일치해야 합니다. 일반적인 실수는 테스트 중에 연결 끊김이 성가시기 때문에 제한을 0 0 0으로 설정하는 것입니다. 이렇게 하면 안전 레일이 제거됩니다. 프로덕션에서는 연결 끊김이 일반적으로 하나의 멈춘 구독자가 Redis가 불안정해질 때까지 메모리를 소비하도록 두는 것보다 낫습니다.
redis.conf를 변경한 후 배포 프로세스에 따라 다시 로드하거나 다시 시작하십시오. 라이브 값을 검사할 수도 있습니다:
redis-cli CONFIG GET client-output-buffer-limit
관리형 Redis 서비스를 사용하는 경우 이 설정은 직접 파일 편집이 아닌 공급자의 매개변수 그룹 또는 구성 UI를 통해 노출될 수 있습니다.
증상: 구독자가 연결되지만 아무것도 수신하지 못함
가장 간단한 테스트부터 시작하십시오. 하나의 터미널을 엽니다:
redis-cli SUBSCRIBE test.channel
다른 터미널을 엽니다:
redis-cli PUBLISH test.channel hello
Redis 호스트에서는 작동하지만 애플리케이션에서는 작동하지 않으면 클라이언트 측 또는 네트워크 문제일 가능성이 높습니다. 모든 곳에서 실패하면 인증, ACL, 클러스터 라우팅 및 실제 채널 이름을 확인하십시오.
채널 이름은 정확한 바이트 문자열입니다. events.user, events:User 및 events:user는 다른 채널입니다. 대규모 시스템에서는 서비스 전체에 흩어져 있는 수동 입력 문자열보다 상수 또는 작은 채널 명명 모듈을 선호합니다.
패턴 구독은 또 다른 혼란의 원인을 추가합니다:
PSUBSCRIBE events:*
이것은 events:user.created와 일치하지만 prod:events:user.created와는 일치하지 않습니다. 애플리케이션이 환경 이름을 접두사로 사용하는 경우 패턴에 접두사를 포함하십시오.
인증 및 ACL 문제
최신 Redis 배포판은 종종 하나의 공유 requirepass 암호 대신 ACL 사용자를 사용합니다. Pub/Sub 클라이언트는 연결, 인증, 채널 패턴 구독 및 때로는 게시할 수 있는 권한이 필요합니다.
빠른 CLI 확인:
redis-cli -u redis://app-user:[email protected]:6379 PING
redis-cli -u redis://app-user:[email protected]:6379 SUBSCRIBE events:test
일반적인 인증 증상:
NOAUTH Authentication required: 클라이언트가 인증 전에 명령을 보냈습니다.WRONGPASS invalid username-password pair: 잘못된 암호, 잘못된 사용자 또는 모든 곳에 배포되지 않은 순환된 비밀입니다.NOPERM this user has no permissions: 사용자가 인증되었지만 명령 또는 채널 권한이 부족합니다.
ACL을 사용하면 채널 권한이 키 권한과 분리됩니다. 사용자는 일반 키 명령을 실행할 수 있지만 예상하는 채널을 구독할 수는 없습니다. 구성된 사용자를 검토하십시오:
redis-cli ACL GETUSER app-user
테스트 중에 프로덕션 비밀을 셸 기록에 붙여넣지 마십시오. 가능하면 환경 변수 또는 임시 자격 증명을 사용하십시오.
Pub/Sub 연결을 일반 명령과 공유하지 마십시오
일단 연결이 구독 모드로 전환되면 더 이상 일반 Redis 명령 연결이 아닙니다. 푸시된 메시지를 수신하고 클라이언트 및 Redis 버전에 따라 SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PING, RESET 또는 종료 스타일 명령과 같은 구독 관련 명령만 실행할 수 있습니다.
일반 풀에서 연결을 빌려서 SUBSCRIBE를 호출하면 해당 풀링된 연결은 더 이상 GET, SET 또는 기타 일반 명령에 사용할 수 없습니다. 결과는 풀이 구독자 모드에 갇힌 연결로 천천히 채워지기 때문에 무작위 애플리케이션 시간 초과처럼 보일 수 있습니다.
별도의 연결을 사용하십시오:
- 일반 Redis 명령을 위한 하나의 일반 클라이언트 또는 풀.
- 구독자 프로세스 또는 스레드당 하나의 장기 구독자 연결.
- 클라이언트 라이브러리에서 권장하는 경우 하나의 별도 퍼블리셔 연결.
많은 클라이언트 라이브러리는 이러한 이유로 duplicate() 또는 new connection 메서드를 제공합니다. 사용하십시오. 풀이 알아서 처리할 것이라고 추측하지 마십시오.
재연결 동작이 중요합니다
Pub/Sub은 재연결 후 놓친 메시지를 재생하지 않습니다. 구독자가 30초 동안 중단되면 해당 기간 동안 게시된 모든 메시지는 해당 구독자가 놓칩니다.
이는 애플리케이션에 대체 수단이 있는 경우에만 캐시 무효화에 허용됩니다. 예를 들어, 무효화 이벤트를 놓친 서비스는 여전히 TTL, 버전 검사 또는 소스 데이터베이스에서 다시 빌드하는 방법이 있어야 합니다. 대체 수단이 없으면 단일 네트워크 결함으로 인해 오래된 로컬 캐시 항목이 너무 오래 남아 있을 수 있습니다.
재연결 시 클라이언트가 모든 채널과 패턴을 다시 구독하는지 확인하십시오. 일부 라이브러리는 자동으로 수행합니다. 다른 라이브러리는 재연결 후 핸들러를 다시 등록해야 합니다. 구독자 연결을 종료하여 이를 테스트하십시오:
redis-cli CLIENT LIST | grep subscribe
redis-cli CLIENT KILL ID <client-id>
그런 다음 테스트 메시지를 게시하고 재연결 후 애플리케이션이 메시지를 수신하는지 확인하십시오.
높은 팬아웃은 CPU 및 네트워크 압력이 될 수 있습니다
PUBLISH 작업은 일치하는 구독자 및 패턴 수에 따라 증가합니다. 수천 명의 구독자가 있는 채널은 Redis가 메시지를 수천 번 푸시해야 함을 의미합니다. 여전히 괜찮을 수 있지만 무료는 아닙니다.
확인:
redis-cli INFO stats
redis-cli PUBSUB NUMSUB events:test
redis-cli PUBSUB NUMPAT
PUBSUB NUMSUB는 채널에 생각하는 구독자 수가 있는지 확인하는 데 도움이 됩니다. PUBSUB NUMPAT는 광범위한 패턴 구독이 당신을 놀라게 할 수 있기 때문에 유용합니다. PSUBSCRIBE * 또는 PSUBSCRIBE events:*를 사용하는 소수의 서비스는 의도한 것보다 훨씬 더 많은 트래픽을 수신할 수 있습니다.
Pub/Sub 트래픽이 중요하고 볼륨이 높으면 격리하십시오. Pub/Sub 전용 Redis 인스턴스는 구독자 버퍼 문제 및 브로드캐스트 급증으로부터 캐시 및 세션 저장소를 보호합니다. 전용 인스턴스는 Pub/Sub에 맞게 조정된 구성, 더 짧은 지속성 문제 및 클라이언트 및 네트워크 출력에 초점을 맞춘 모니터링을 가질 수 있습니다.
메시지 핸들러를 짧게 유지하십시오
구독자는 메시지를 읽고 최소한의 유효성 검사를 수행한 후 작업을 다른 곳에 넘겨야 합니다. 핸들러가 다음 메시지를 읽기 전에 데이터베이스를 호출하거나, HTTP 요청을 하거나, 템플릿을 렌더링하거나, 긴 계산을 수행하면 피하려는 느린 소비자가 될 수 있습니다.
더 나은 형태:
Redis 메시지 -> 경량 핸들러 -> 로컬 대기열 -> 작업자 풀
로컬 대기열은 프로세스 내 대기열, 스레드 풀, 비동기 작업 대기열 또는 작업이 프로세스 재시작 후에도 유지되어야 하는 경우 내구성 있는 브로커일 수 있습니다. 핵심 아이디어는 Redis에서 읽는 것이 가장 느린 다운스트림 종속성을 기다리지 않아야 한다는 것입니다.
작업이 안정적이어야 하는 경우 Pub/Sub은 잘못된 기본 요소입니다. 소비자 그룹이 있는 Redis Streams는 보류 중인 항목과 확인을 제공합니다. 데드 레터 대기열, 지연된 재시도, 우선 순위 또는 긴 보존이 필요한 경우 전용 브로커가 더 나을 수 있습니다.
실용적인 문제 해결 체크리스트
Pub/Sub이 비정상적으로 보일 때 일반적으로 다음 순서로 작업합니다:
SUBSCRIBE및PUBLISH로 수동으로 채널을 확인합니다.CLIENT LIST에서 높은omem을 가진 Pub/Sub 클라이언트를 확인합니다.client-output-buffer-limit pubsub를 검사합니다.- 인증 및 ACL 채널 권한을 확인합니다.
- 구독자가 전용 연결을 사용하는지 확인합니다.
- 구독자 연결을 종료하고 재연결 및 재구독 동작을 확인합니다.
- 예상치 못한 팬아웃에 대해
PUBSUB NUMSUB및PUBSUB NUMPAT를 확인합니다. - 워크로드에 실제로 Streams 또는 전용 브로커가 필요한지 결정합니다.
Redis Pub/Sub은 라이브 메시지를 현재 연결된 구독자에게 브로드캐스트하는 약속을 정확히 이행한다는 의미에서만 안정적입니다. 대부분의 프로덕션 인시던트는 내구성 있는 대기열처럼 동작할 것으로 예상하기 때문에 발생합니다. 버퍼 제한을 구성하고, 많은 트래픽을 격리하고, 메시지를 놓칠 수 있는 것처럼 구독자를 설계하십시오. 때로는 그럴 수 있기 때문입니다.
예시 인시던트: 하나의 대시보드가 캐시를 느리게 함
일반 캐시 키와 metrics:live라는 작은 Pub/Sub 채널에 사용되는 Redis 인스턴스를 상상해 보십시오. 처음에는 내부 대시보드만 구독합니다. 몇 달 후 여러 브라우저 게이트웨이 프로세스도 구독하고 그 중 하나가 느린 WebSocket 연결을 통해 업데이트를 보내기 시작합니다. Redis는 다운스트림 브라우저가 느리다는 것을 알지 못합니다. 충분히 빠르게 읽지 않는 구독자 연결만 볼 뿐입니다.
첫 번째 증상은 Pub/Sub을 전혀 언급하지 않을 수 있습니다. 일반 GET 및 SET 호출을 사용하는 애플리케이션 요청이 시간 초과되기 시작합니다. 메모리가 증가합니다. Redis 호스트가 바쁘게 보이지만 SLOWLOG에는 명백한 비용이 많이 드는 명령이 표시되지 않습니다.
유용한 단서는 CLIENT LIST에 있습니다:
flags=P sub=1 omem=25165824 cmd=subscribe
이는 Pub/Sub 클라이언트에 큰 출력 버퍼가 있음을 알려줍니다. 구성된 하드 제한이 32MB인 경우 해당 클라이언트는 연결이 끊어지기 직전입니다. 제한이 비활성화되면 Redis는 전체 인스턴스가 메모리 압력에 빠질 때까지 버퍼링을 계속할 수 있습니다.
해결책은 단순히 제한을 높이는 것이 아닙니다. 높이면 문제를 숨기고 다음 번에 더 심각한 메모리 급증을 허용할 수 있습니다. 더 나은 대응은 다음과 같습니다:
addr,name또는 클라이언트 메타데이터에서 구독자 프로세스를 식별합니다.- 메모리가 위험한 경우 최악의 구독자를 종료합니다.
client-output-buffer-limit pubsub를 추가하거나 강화합니다.- 느린 다운스트림 전달을 Redis 읽기 루프 외부의 내부 대기열로 이동합니다.
- 대용량 Pub/Sub을 전용 Redis 인스턴스로 이동하는 것을 고려합니다.
클라이언트 이름을 사용하면 훨씬 쉬워집니다. 많은 Redis 라이브러리에서 연결 이름을 설정할 수 있습니다. web-1:pubsub-cache-invalidations 또는 dashboard:metrics-live와 같은 이름을 사용하여 CLIENT LIST가 IP 및 포트만 표시하는 대신 소유자를 가리키도록 하십시오.
CLIENT SETNAME dashboard:metrics-live
이 작은 위생 조치는 모호한 메모리 인시던트를 구독자를 소유한 팀과의 직접적인 대화로 바꿉니다.