아파치 카프카는 빠르고 확장 가능한 작업을 위해 데이터 피드의 분산 스트리밍, 파이프 라이닝 및 재생을 위한 실시간 스트리밍 데이터를 처리하기 위한 목적으로 설계
된 오픈 소스 분산형 게시-구독 메시징 플랫폼이다.
메시징 시스템은 Kafka, RabbitMQ, ActiveMQ, AWS SQS, Java JMS 등이 있다. MSA
같은 시스템 같은 경우에 시스템 간 호출이 많아 서비스 결합도를 낮추기 위해 비동기 요청, 성능, 안정성 등을 위해 메시징 시스템을 사용하곤 한다.
그렇다면 메시징 시스템이란 도대체 뭘까? 보통은 로그 데이터, 이벤트 메시지 등을 API로 호출할 때 보내는 데이터들을 처리하는 시스템이라고 생각하면 된다.
이런 메세징 시스템들은 ‘Publish/Subscribe’
형태의 통신을 하며 메세지를 보내고(Publish) 받는(Subscribe)다. 대부분의 발행/구독 시스템은 메세지 큐
나 프로세스 간 통신 채널
을 갖는 형태로 만들어진다.
그렇다면 왜 메세징 시스템이 필요할까?
만약 아래 그림같이 프론트엔드 서버와 백엔드 서버로부터 전체 리소스 사용 데이터를 수집하는 메트릭 서버가 있다고 해보자. 서비스 배포 초기에는 위와 같이 간단하게 구성할 수 있을 것이다.
하지만, 우리는 운영하면서 데이터베이스 서버, 채팅 서버, 인증 서버등의 추가와 분리의 필요성을 느끼게 되었고 이를 아래와 같이 재구성하였다.
서비스 인스턴스가 다음과 같이 늘어나고 추가적인 로그 수집 서버나 모니터링 서버가 추가되면 복잡해지게 된다. 이렇게 된 이유는 무엇일까?
각 인스턴스 서버(프론트엔드, 백엔드, 채팅, 인증 서버 등등, 이하 통칭 publisher)와 각 메트릭 서버(이하 통칭 consumer)들이 직접 연결된 구조이기 때문이다. 1:1의 관계기 때문에 복잡도가 증가하게 되는 것이다. 이는 한 곳에서 수정이 일어나면 다른 곳에 영향을 줄 수 있다는 의미가 된다.
결국은 의존성 문젠데 이는 다른 아키텍처와 마찬가지고 결합도를 줄이는 느슨한 구조로 변경해야 한다.
이를 메세징 시스템을 통해 아래와 바꿔보자.
만약 메트릭 발행/구독 서버(Kafka와 같은)를 추가하게 되면
메세지를 읽어가기만 하면
된다.수평적인 확장이 가능
해진다.그렇다면 왜 Kafka를 써야 하는가?
위 그림은 카프카가 개발되기 전 링크드인의 데이터 시스템 구성도이다. 당시 링크드인이 서비스를 유지하려면 다음과 같은 시스템들이 필수적이었다.
위 시나리오처럼 성장하는 회사들은 이와 같은 과정을 한 번씩은 거치게 된다. 위에서 말한 것 처럼 End-to-End 방식의 아키텍처는 많은 문제점을 발생시킨다.
메세징 시스템의 publisher 역할을 하는 것이 카프카의 producer
이다.
카프카는 하나의 토픽에 여러 프로듀서 또는 컨슈머들이 접근 가능한 구조로 되어있다. 즉, 하나의 프로듀서가 하나의 토픽에만 메세지를 보내는 것이 아니라 하나 또는 하나 이상의 토픽으로 메세지를 보낼 수 있다.(컨슈머도) 멀티 프로듀서와 멀티 컨슈머를 구성할 수 있기 때문에 카프카는 중앙 집중형 구조를 구성할 수 있다.
또한, 여러 클라이언트가 많은 토픽(메세지 저장소)를 사용하거나 같은 토픽을 사용해도 카프카는 무리없이 처리할 수 있다. 여러 프로듀서로부터 데이터를 수집하고 일관성을 유지하는데 이상적인 구조를 가지고 있다.
구독자 역할을 하는 것이 카프카의 consumer이다. 카프카는 상호 간섭없이 어떤 메세지도 읽을 수 있게 지원한다. 다른 메세징 시스템의 경우엔 한 구독자 클라이언트가 특정 메세지를 소비하면 다른 구독자 클라이언트에서는 그 메세지를 사용할 수 없다. 하지만 카프카 컨슈머는 컨슈머 그룹이란 개념을 사용하여 각각의 컨슈머가 메세지를 관리할 수 있다.
카프카가 기존의 메세징 시스템과 가장 다른 특징 중 하나는 디스크에 메세지를 저장하고 유지
할 수 있다는 것이다. 일반적인 메세징 시스템들은 컨슈머가 메세지를 읽어가면 큐에서 바로 메세지를 삭제한다. 하지만, 카프카는 컨슈머가 메세지를 읽어가더라도 정해져 있는 보관 주기 동안 디스크에 메세지를 저장해둔다.
만약, 트래픽이 일시적으로 폭주해 컨슈머의 처리가 늦어지더라도 카프카의 디스크에 안전하게 보관되어 있기 때문에 컨슈머는 손실 없이 메세지를 가져갈 수 있다. 또한, 컨슈머에 버그가 있어 오류가 발생했다면, 컨슈머를 잠시 중단하고 버그를 찾아 해결한 후 컨슈머를 다시 실행할 수 있다.
카프카는 확장이 매우 용이하도록 설계되어있다. 확장 작업은 카프카 서비스의 중단 없이 온라인 상태에서 작업이 가능하다.
Scale-out이 가능하여 시스템 확장이 용이하며 어떤 하나 혹은 몇 개의 서버가 다운되도 서비스 자체가 중단될 일 없이 시스템 운용이 가능하다. 또한, 카프카는 파티션
이라는 개념을 도입하여 여러 개의 파티션을 서버에 분산시켜 처리할 수 있다. (빠른 메세지 처리가 가능)
잔여 메모리를 이용해 디스크 Read/Write를 하지 않고, 페이지 캐시를 통한 Read/Write를 인해 처리 속도가 매우 빠르다.
그럼 카프카를 도입하고 난 후의 링크드인은 어떻게 됐을까? 위 그림처럼 사내에서 발생하는 모든 이벤트/데이터 흐름을 중앙에서 관리하여 깔끔하게 변한 것을 알 수 있다.
기존의 모든 데이터 스토어(1번~6번)가 그대로 있고 여기서 발생하는 데이터/이벤트가 카프카를 중심으로 연결되어있다. 또, 새로운 데이터 스토어(7~9)가 들어와도 서로 카프카가 제공하는 표준포맷으로 연결되어있어 데이터를 주고받는데 부담이 없다.
뿐만 아니라 이전에는 할 수 없었던 다양한 분석(10번~12번)이 가능해져 신뢰성 높은 데이터 분석/실시간 분석이 가능해졌다.
이런 새로운 데이터 처리 시스템에서는 링크드인 사용자가 프로필을 업데이트하면 이 정보가 바로 카프카로 전달된다. 이 정보를 실시간 스트림으로 처리해서 데이터 웨어하우스에 저장된 회사 정보, 위치, 기타 속성들을 변경한다. 그리고 카프카에서 이 데이터의 변경사항을 모니터링하고 있던 검색 인덱스 시스템, 소셜 그래프 시스템, 직업 추천을 위한 추천 시스템으로 이동해 처리하게 된다.
아파치 카프카에서의 쓰기, 읽기 연산은 카프카 클러스터 내의 리더 파티션들에게만 적용된다. 하늘색 표시의 리터 파티션에게 프로듀서들이 쓰기 연산을 진행하며 이렇게 업데이트 된 데이터는 각 파티션들의 복제본들(Replica)들에게 복사된다.
카프카는 데이터를 순차적으로 디스크에 저장하는 특징을 가진다. 따라서 프로듀서는 저장된 데이터 뒤에 붙이는 append 형식으로 write 연산을 진행하게 된다. 이 때 파티션들은 각각의 데이터들의 순차적인 집합인 오프셋(offset)으로 구성되어있다.
컨슈머그룹의 각 컨슈머들은 파티션의 오프셋을 기준으로 데이터를 순차적으로 처리하게 된다. (먼저 들어온 순서부터) 이 때, 컨슈머들은 컨슈머 그룹으로 나뉘어서 데이터를 분산처리하고 같은 컨슈머 그룹 내에 있는 컨슈머끼리 같은 파티션의 데이터를 처리할 수 없다.
파티션에 저장되어있는 데이터들은 순차적으로 데이터가 저장되어 있으며 이 데이터들은 설정값에 따라 데이터를 디스크에 보관하게 된다.
위 그림은 컨슈머 그룹 단위로 그룹 내 컨슈머들이 각각의 파티션의 데이터를 처리하는 모습을 나타낸 것이다.
만일 컨슈머와 파티션의 개수가 같다면 컨슈머는 각 파티션을 1:1로 맡게 된다.(첫 번째 경우)
만일 컨슈머 그룹 안의 컨슈머의 개수가 파티션의 개수보다 적을 경우 컨슈머 중 하나가 남는 파티션의 데이터를 처리하게 된다. (두 번째 경우)
만약 컨슈머의 개수가 파티션의 개수보다 많을 경우는 남는 컨슈머가 파티션 개수가 많아질 때까지 대기한다.(세 번째 경우)