Skip to main content Link Menu Expand (external link) Document Search Copy Copied

실무에서 redis stream 으로 사용자에게 알림전송 시스템을 구축하면서 kafka 와 다른점들을 많이 느꼈다. 개인적으로 느낀 4개 주요 차이점 있는데 AI 의 힘을 빌리지 않고 정리해본다. 아래는 redis stream grafana 모니터링 화면이다(message i/o 처리량은 로깅하지 않았다).

  1. kafka 에서는 연결되어있는 consumer 자동관리해주는데 redis stream 은 그런거 없다. consumer 관리를 직접 등록해제 해줘야한다. 사실 consumer 싱크관리 안해줘서 지금 그림에서 보이듯 실제론 9개 conusmer 가 돌아가지만 21개 conusmer 가 등록되어있다. 그리고 리밸런싱도 직접 해줘야한다. kafka 처럼 리밸런싱 될 때 commit offset 이후부터 읽는게 아니라 얘네는 commit 자체가 없다. 아니, 정확히 말하면 kafka 에서의 commit 은 redis stream 에서는 두 단계로 나눠져 있다. 1. 하나는 소비자가 읽었음을 나타내는 단계. 2. 이후 xack 로 메세지를 소비자가 제대로 처리했음을 나타내는 단계.

    stream 에서 새로운 컨슈머가 읽기만 하면(xack 안보내도) 그 시점부터는 새로운 컨슈머가 왔을 때 해당 메세지 이후부터만 읽는게 가능하다(xreadgroup). “Q. 그러면 만약에 읽다가 아예 컨슈머가 죽어버리면 어떻게 해요?” 라는 질문이 나올 수 있는데 kafka 의 경우에는 당연히 commit 안되어있고 리밸런싱 시간 지나면 알아서 새로운 컨슈머에 파티션 배정해주니까 괜찮다. redis stream 은 직접 리밸런싱 로직을 구현해줘야한다. 얘네는 컨슈머한테 전달 시 메세지를 PEL(pending entry list) 에 넣어둔다. 우리는 직접 컨슈머가 주기적으로 pel 내 들어있는 메세지들을 claim 해서 처리한 뒤 xack 전송해서 PEL 에서 제거시키도록 리밸런싱 로직을 “직접” 짜줘야한다. 이렇게 해놓으면 만약에 컨슈머가 읽는 도중에 죽어버리더라도 PEL 에 남아있는 메세지들을 다른 컨슈머가 가져가서 처리할 수 있다.

    이거 멀티파드환경에서 리밸런싱 스케줄러 작성할 때 또 주의할 점이 있는데 더 말하면 너무 길어져서 이전 포스팅 링크를 남긴다.

  2. 또 다른점은 kafka 는 파티션 여러개 써서 동시성 가져갈 수 있는데, redis stream 애네는 파티션이 하나라는 점이다. 동시성을 하나만 가져갈 수 있게 되버리는데 이거 수정할려면 stream 을 여러개 만들고 해싱 + 모듈러 연산 조합으로 stream 여러개 나눠서 처러하도록 해야한다. 예로 뭐 user:stream:0, user:stream:1, user:stream:2 … user:stream:n 이런식으로 여러개 만들고, user id 를 해싱해서 stream 개수로 나눈 나머지 값에 따라 stream 을 골라서 넣고 빼도록 해야한다. HASH(user_id) mod n = stream index 이런식으로! 즉, 샤딩 해줘야함.

    물론 consumer 여러개를 단일 stream 에 붙일 수 있긴하다. 그런데 이러면 메세지 처리 순서문제가 발생한다. a 스레드가 먼저 메세지를 읽고 처리중일 때, b 스레드가 그 다음 메세지를 읽어서 더 빠르게 처리해버리면 순서가 꼬이게 된다. 그래서 단일 stream 에 여러 consumer 붙이는건 순서 보장이 필요한 상황에서는 적합하지 않다.

  3. kafka 는 debezium 이 커넥트 플러그인으로 DB 싱크도 지원해주는데, redis stream 은 그런거 없다. 보통 CQRS 해주기 위해서 읽기 전용 DB 싱크해주는 것을 이 MQ 를 백프레셔로 두고 여러 오픈소스 모듈에서 지원해주는데, kafka 는 커넥트 플러그인으로 DB 싱크도 지원해준다. redis stream 은 그런거 없다ㅜㅜ. debezium 에서 db -> redis stream 지원은 해주지만, redis stream -> sync readOnly db 지원이 없다ㅜㅜ. 그래서 직접 싱크 고성능 모듈을 따로 띄워야 한다. 그래서 직접 띄운다고 고생했다.ㅜ
  4. kafka 는 메세지 segment log 크기나 TTL 설정해서 자동 삭제해주는 옵션을 제공하는데 redis stream 은 XTRIM 으로 그냥 길이로만 제한한다. 직접 add 할 때 maxlen 옵션 줘서 길이 제한 걸어줘야 한다. 특히 메모리라서 꽉차버리면 키 제거해버려서 관리를 더 잘해줘야된다.
  5. kafka 는 exactly-once 기능 제공으로 A 메세지를 딱 한번 producing 보장해주는데, redis stream 은 그런거 없다. 그래서 동일 메세지를 여러번 쏠 수 있다. 이건 뭐 사실 구현이 간단해서 딱히 필요없다. 이벤트 uuid 레디스에 넣어놓고 produce 할 때 중복제거 해주면 된다. 사실 트랜젝션 코디네이터가 애초에 핸드쉐이킹때문에 느려서 당근 tech에서는 카프카 코디네이터 안쓰고 개별 구현으로 바꿨다고 한다. 물론 exactly-once 를 위한 이벤트 id 를 메모리에 저장한다고 했을 때, 사이즈가 꽤 커서 TTL 을 잘 설정해놔야한다.
  6. acl 을 설정하는건 기본 kafka 랑 비슷하다. redis stream 도 acl 설정해줘야한다. 예를들어 stream:domain:events:* 이런식으로 패턴 매칭해서 xadd, xread, xgroup, xreadgroup 권한을 줘야한다. connection, info, scan, xrange 권한도 줘야한다.

     ACL SETUSER user ~domain:events:summary* +xadd +xread +xgroup +xreadgroup
     ACL SETUSER user +@connection
     ACL SETUSER user +info
     ACL SETUSER user +scan +xrange
    

상당히 번거롭다고 볼 수 있다ㅜㅜ. kafka 가 사용하기엔 더 편하고 레퍼런스도 상당히 많아서 좋다. 물론 로우레벨에선 redis stream 으로 직접 조절할 수 있는게 많아서 더 재밌긴 하지만, 운영공수 측면에서는 kafka 가 좀 더 나아보인다.