시작하며
안녕하세요! 경민입니다. 오늘은 Kafka를 사용해서 채팅 기능을 분산 서버로 구성한 설계를 소개드리고자합니다.
Kafka는 대표적인 대규모 이벤트 처리 도구인데요. 브로커 기능을 제공하여 분산 서버에서 탁월한 성능을 가진 도구로 평가 받습니다.
우리가 많이 사용하고 있는 카카오톡과 넷플릭스와 같은 대중적인 서비스들도 Kafka를 통한 데이터 처리를 하고 있습니다. 그만큼 Kafka의 우수한 데이터 처리 능력과 활용성은 이미 검증됐다고 보시면 됩니다.
저는 이러한 Kafka를 지금 제가 하고 있는 프로젝트인 '스펙랭킹' 서비스에 적용시켜 구성해봤습니다.
Kafka를 채팅 기능에서 사용하는 이유?
Kafka를 채팅 기능에서 활용하는 이유는 다음과 같습니다.
- 많은 트래픽으로 인해 서버가 늘어났을 경우 유저가 어느 서버에 접속해 있는지 모름
- 장애 대응이 쉽지 않음
- 확장에 불리하고 자유롭게 Auto Scailing 하기 쉽지 않음
채팅 기능 특성상 1 대 1 채팅을 위해서 해당 유저가 접속해 있는 서버를 찾아야되는 소요가 있습니다. 물론 트래픽이 많이 발생하지 않고 단일 인스턴스 1개에서만 돌아가는 서비스라면 상관이 없겠지만 그러한 서비스들은 드물기 마련입니다. 따라서 많은 트래픽과 빠른 장애 대응 및 원인 파악을 위해 새로운 아키텍쳐 고민을 해야했습니다.
Kafka의 브로커 기능과 역할
Kafka에는 브로커 기능이 있습니다. 브로커가 토픽과 파티션의 상태를 확인하고 적절히 코디네이팅 및 리밸런싱과 같은 일을하면서 데이터처리를 조율합니다. 따라서 브로커에게 맡기기만 한다면 브로커가 알아서 데이터를 처리하고 해준다는 것입니다. 그래서 채팅 기능에서 채팅을 보내고 그 채팅을 브로커가 적절히 컨슈머에게 전달해서 데이터를 처리하는 방식을 사용할 수 있습니다. 전체적인 흐름을 프로듀서 => 브로커 => 컨슈머 구조로 가져갈 수 있는 것입니다.
브로커는 기본적으로 3개 이상으로 구성되고 3개가 default value로 알려져있습니다. 왜냐하면 병렬처리로 빠른 데이터를 처리를 하기 위함입니다.
Kafka를 활용한 분산 아키텍쳐 구성은 어떻게 이루졌는가?

기본적으로 3-Tier 아키텍쳐 구성과 함께 프라이빗 존에 Ec2 서버를 여러개 띄웁니다. 기본적으로 브로커 3개와 파티션 개수를 20개로 설정했고 이에 따라 컨슈머 서버의 오토스케일링도 1개부터 ~20개까지 가능하도록 설계를 했습니다. 프로듀서(main 서버)도 마찬가지고 1개부터 ~ N개까지 Auto Scailing 하여 확장할 수 있도록 설계했습니다.
토픽은 chat이라는 토픽을 만들었고 consumer 그룹도 설정하여 컨슈머를 구성했습니다. 중간에 burrow 라고 있는데 컨슈머의 지연에 대한 부분을 모니터링하기 위해 burrow exporter로 추출해서 모니터링할 수 있게 아키텍쳐 설계를 했습니다. 이에 대한 부분은 따로 포스팅하여 다루도록 하겠습니다.
컨슈머에서 레디스와 통신하여 user ID 와 server IP 를 받아 해당 유저가 어디 서버에 있는 지 빠르게 캐싱하여 채팅 기능의 속도를 높였습니다
최종적으로 위와 같이 구성하여 부하가 몰렸을 때 수평확장이 가능하도록 설계하여 높은 메시지 처리량도 감당할 수 있게 했습니다.
consumer에서 채팅 데이터 순서는 어떻게 보장되는가?
채팅을 한다는 보내는 사람이 있고 받는 사람이 존재하기 때문에 채팅방이라는 하나의 단위가 존재하게 된다. 특정 파티션에만 계속해서 특정 채팅방 id의 데이터만 쌓이게하고 컨슈머가 이를 병렬처리해서 채팅 데이터의 순서를 보장하게한다. 같은 채팅방 id 일 경우 해당 파티션에만 채팅 데이터가 쌓이게 되어 레코드를 읽을 때 순서가 보장되는 것이다.
private String generateKeyForSequence(Long senderId, Long receiverId) {
if (senderId < receiverId) {
return senderId + "_" + receiverId;
} else {
return receiverId + "_" + senderId;
}
}
위의 코드로 key를 만들어서 처리하게 된다.
마치며
consumer의 Auto Scailing 간 파티션 리밸런싱은 어떻게 이루어지는가?
다음 시간에는 이 부분에 대한 주제로 심도있게 다뤄보고자한다.
파티션은 한 번 생성 되면 삭제가 안되기 때문에 처음에 설정할 때 partition >= consumer 로 해서 설정을 하는 것이 일반적이다.
사실상 파티션과 컨슈머가 같은 개수로 존재하여 병렬처리가 정확히 일어나게 하는 것이 베스트지만 프로덕션 환경에서 갑작스럽게 파티션을 늘려야하거나 컨슈머를 늘려야할 경우 리밸런싱 처리에 대한 부분을 신경쓰지 않을 수 없다.
따라서 리밸런싱에 대한 방식과 정책들을 알아보면서 다음 주제로 이어나가고자 한다
