카프카가 높은 처리량을 갖는 이유, High throughput

카프카는 메시지들을 디스크에 저장한다.

하둡의 맵리듀스 시대가 가고 스파크의 시대가 온 것은 바로 디스크 읽기/쓰기 속도보다 메모리에서 읽기/쓰기를 수행하는 속도가 빨라서인데, 카프카는 어떻게 디스크를 사용하면서도 메시지들에 대해 높은 처리량을 가질 수 있는 것일까?


페이지 캐시

OS는 여러 애플리케이션들에게 할당해주고 남은 메모리의 일부를 페이지 캐시라는 것으로 활용함으로써 성능을 높인다.

페이지 캐시 공간은 파일의 I/O 성능을 향상시키기 위해, 사용되었던 파일들의 내용을 캐싱해놓았다가 다시 동일한 접근 발생할 시 디스크를 또 읽지 않고 캐시에서 읽는다.

카프카는 자체적으로 캐싱하는 레이어를 갖지 않고, 이렇게 운영체제에서 사용하고 있는 페이지 캐시에 캐싱을 전적으로 맡긴다.

즉, 메시지들을 디스크에 저장함으로써 영속성을 보장했지만, 실제로는 OS의 페이지 캐시를 공유함으로써 많은 데이터를 메모리 상에서 처리한다.

대용량의 데이터가 카프카 애플리케이션이 사용하는 메모리와 OS의 페이지 캐시에 중복하여 캐싱되는 것을 방지하며, 캐싱이 OS 측에서 이루어지는 것이기 때문에 프로세스를 다시 시작시키더라도 캐시를 워밍업 할 필요가 없다는 장점들이 있다.

카프카는 JVM을 사용하는 애플리케이션이라, 프로세스가 구동될 때 자바 애플리케이션에서 사용할 힙 영역을 메모리에서 할당 받는다.

그러나 메시지에 대한 읽기/쓰기는 따로 OS 에서 사용하는 페이지 캐시를 이용하기 때문에, 메모리 전체를 카프카 프로세스가 힙으로 사용하게 된다면 오히려 성능에 저하가 일어날 수 있다.

OS가 페이지 캐시를 위해 사용할 수 있는 메모리 영역을 감안하여 힙 영역을 할당받아야 하는데, 이는 KAFKA_HEAP_OPTS라는 환경변수에서 설정할 수 있다.

기본적으로는 1GB 의 힙을 할당 받지만, Confluent에서는 5GB 정도를 사용하는 것을 추천한다.


통신 프로토콜

기존 메시징 시스템들은 AMQP 라는 프로토콜을 사용하는데, 카프카는 TCP를 통한 바이너리 프로토콜을 사용함으로써 프로토콜 상에서의 오버헤드를 감소시킨다.

카프카의 TCP 기반의 자체 프로토콜은 소켓을 열어 메시지를 쓰고 응답을 받아오는데, 이러한 통신 과정에서 TCP에서의 handshake을 생략한다.

Handshake 을 하는 비용이 아깝지 않을 만큼 한 번의 커넥션을 유지할 때 많은 요청을 처리하지 않기 때문이다.


배치 전송

서로 다른 호스트들 간에 데이터 통신이 이루어지는 과정에서는 필수적인 I/O 들이 발생하고, 이런 통신이 많으면 많아질수록 네트워크에서 오버헤드가 일어난다.

만약에 클라이언트에게든 다른 브로커에게든 메시지를 보낸다고 할 때, 수많은 작은 메시지들을 하나하나 보낼 경우 네트워크 오버헤드가 심해진다.

카프카에서는 이런 작은 메시지들을 여러개를 묶어서 한번에 보내는 배치 전송 방식을 택함으로써 오버헤드를 줄인다.


분산 파티션

카프카는 여러 대의 브로커로 이루어진 클러스터로 구성이 된다.

메시지들은 토픽을 단위로 구분되어 저장되는데, 이 토픽들은 내부적으로 여러 파티션들로 나뉘며 여러 브로커들에 파티션들이 분배된다.

컨슈머 그룹에 존재하는 여러 컨슈머들이 각 파티션에 할당되어 데이터를 컨슘해온다. 즉, 컨슈머들이 여러 브로커에 병렬적으로 데이터를 요청해서 가져온다는 것이다.

이렇게 클러스터 내의 여러 브로커들에 파티션들이 분배될 경우, 컨슈머의 데이터 요청이 여러 서버에 분산 되므로 부하가 덜하고, 병렬적으로 데이터를 가져옴으로써 처리량도 높아진다.