사전 Apache Kafka
구현체

Apache Kafka

gabury1고친 사람 github-actions[bot]

Apache Kafka 는 여러 서비스가 주고받는 이벤트를 한곳에 모아 두는 서버입니다. 보내는 쪽은 받는 쪽을 몰라도 기록만 남기면 됩니다. 받는 쪽은 저마다 제 속도로 그 기록을 읽어 갑니다. 읽힌 기록도 곧바로 지우지 않고 정해진 기간 동안 디스크에 남겨 둡니다.

쉽고 빠른 이해

서비스끼리 주고받을 일을 가운데서 받아 두었다가 읽고 싶은 쪽이 가져가게 하는 서버입니다. 주문 서비스가 「주문이 들어왔다」를 한 번 적으면 결제·재고·알림 서비스가 저마다 그 기록을 읽어 갑니다.

왜 이렇게 하나. 주문 서비스가 셋을 직접 부르면 하나만 멈춰도 주문이 막힙니다. 받는 쪽이 하나 늘 때마다 보내는 쪽 코드도 고쳐야 합니다. 가운데에 기록장을 두면 보내는 쪽은 적기만 합니다. 받는 쪽은 알아서 읽습니다.

어떻게 도나.

  1. 보내는 쪽이 주제별로 이름을 붙인 기록장의 맨 끝에 새 기록을 덧붙입니다.
  2. 그 기록장을 여러 조각으로 나눠 서버 여러 대에 흩어 둡니다.
  3. 받는 쪽은 어디까지 읽었는지 번호로 기억하고 그다음부터 읽습니다.

대가. 조각과 조각 사이에서는 순서가 지켜지지 않습니다. 같은 기록을 두 번 받을 수 있어 받는 쪽이 중복을 견뎌야 합니다. 서버 여러 대를 굴리는 운영 부담이 따라옵니다.

언제 쓰나. 한 일을 여러 서비스가 저마다 받아야 할 때 씁니다. 답을 곧바로 받아야 하는 호출에는 쓰지 않습니다. 한 건씩 성공과 실패를 따로 표시해야 하는 작업에도 맞지 않습니다.

상세

Kafka 는 내려받아 서버 여러 대에 띄우는 메시지 브로커입니다. 메시지 브로커는 보내는 프로그램과 받는 프로그램 사이에 서서 메시지를 받아 두었다가 넘겨주는 서버입니다.

Kafka 가 받아 두는 것은 대개 이벤트입니다. 이벤트는 「주문이 들어왔다」·「회원이 가입했다」처럼 이미 일어난 일 한 건을 적은 기록입니다. 아래에서는 주문 서비스 하나가 이벤트를 적고 결제·재고·알림 서비스가 읽어 가는 장면 하나를 끝까지 따라갑니다.

보내는 쪽과 받는 쪽을 떼어 놓는 까닭

주문이 들어오면 결제·재고·알림 서비스에 저마다 할 일이 생깁니다. 가장 단순한 방법은 주문 서비스가 셋을 차례로 직접 부르는 것입니다.

이 방법은 보내는 쪽을 받는 쪽 사정에 묶습니다. 알림 서비스가 멈추면 주문 서비스의 호출이 실패합니다. 재고 서비스가 늦게 답하면 주문 응답도 그만큼 늦어집니다. 받는 서비스가 하나 늘 때마다 주문 서비스 코드를 고쳐야 합니다.

Kafka 를 가운데 두면 주문 서비스는 「주문이 들어왔다」를 Kafka 에 한 번 적고 끝냅니다. 결제·재고·알림 서비스는 그 기록을 저마다 읽어 갑니다. 받는 쪽이 멈춰 있어도 기록은 Kafka 에 남습니다. 그 서비스가 되살아나면 밀린 기록부터 읽습니다.

flowchart TD
    subgraph DIRECT["직접 부를 때"]
        O1["주문 서비스"] --> P1["결제 서비스"]
        O1 --> S1["재고 서비스"]
        O1 --> N1["알림 서비스"]
    end
    subgraph MIDDLE["Kafka 를 가운데 둘 때"]
        O2["주문 서비스"] -->|한 번 적는다| K["Kafka"]
        P2["결제 서비스"] -->|가져간다| K
        S2["재고 서비스"] -->|가져간다| K
        N2["알림 서비스"] -->|가져간다| K
    end

위쪽에서는 주문 서비스가 받는 쪽 셋을 모두 알아야 합니다. 아래쪽에서는 주문 서비스가 Kafka 하나만 압니다. 서로를 모르고도 일이 이어지는 이 상태를 느슨한 결합이라고 합니다.

아래쪽 화살표가 전부 Kafka 를 향하는 데도 까닭이 있습니다. Kafka 는 받는 쪽에 기록을 밀어 보내지 않습니다. 받는 쪽이 Kafka 에 물어 새 기록을 가져갑니다. 이렇게 받는 쪽이 주기적으로 묻는 방식이 폴링입니다. 받는 쪽은 감당할 만큼만 가져가므로 기록이 몰려도 넘치지 않습니다.

읽어도 지우지 않는 로그

Kafka 가 기록을 담는 모양은 로그입니다. 이 로그는 프로그램이 남기는 오류 기록이 아닙니다. 들어온 순서대로 끝에만 덧붙이고 중간은 고치지 않는 기록의 줄입니다.

줄에 선 기록마다 붙는 번호를 오프셋이라고 합니다. 첫 기록이 0번, 다음 기록이 1번입니다. 오프셋은 로그 안에서 그 기록이 몇 번째인지를 가리킵니다.

보통의 큐는 누가 꺼내 간 메시지를 지웁니다. Kafka 는 읽혀도 지우지 않습니다. 대신 받는 쪽마다 「어디까지 읽었나」를 오프셋 하나로 따로 기억합니다. 그래서 한 로그를 여러 서비스가 저마다 다른 곳에서 읽습니다.

flowchart TD
    subgraph LOG["주문 로그 · 위가 오래된 기록"]
        R0["오프셋 0 · 주문 A"] --> R1["오프셋 1 · 주문 B"]
        R1 --> R2["오프셋 2 · 주문 C"]
        R2 --> R3["오프셋 3 · 주문 D"]
        R3 --> R4["오프셋 4 · 주문 E"]
    end
    R4 --> NEW["새 기록은 맨 끝에 붙는다"]
    PAY["결제 서비스 · 다음 차례는 오프셋 2"] -.-> R2
    NOTI["알림 서비스 · 다음 차례는 오프셋 4"] -.-> R4

결제 서비스는 오프셋 2 를 읽을 차례입니다. 알림 서비스는 벌써 오프셋 4 까지 왔습니다. 둘은 서로를 기다리지 않습니다. 결제 서비스가 뒤처져도 알림 서비스는 제 속도로 읽습니다.

지우지 않으니 되돌아가 다시 읽을 수도 있습니다. 결제 서비스 코드에 버그가 있어 기록을 잘못 처리했다고 해 봅시다. 코드를 고친 뒤 읽을 위치를 앞으로 돌리면 같은 기록을 다시 처리합니다. 나중에 새로 생긴 서비스도 쌓인 기록을 처음부터 읽어 따라잡습니다.

토픽과 파티션

기록은 주제별로 나눠 담습니다. 주문 이벤트는 주문끼리, 회원 이벤트는 회원끼리 모읍니다. 이렇게 이름을 붙인 기록 묶음이 토픽입니다. 앞의 예에서는 orders 라는 토픽에 주문 이벤트가 쌓입니다.

Kafka 를 띄운 서버 한 대를 브로커라고 부릅니다. 브로커는 받은 기록을 제 디스크에 적어 둡니다. 읽으러 온 쪽에는 그 기록을 내줍니다.

토픽 하나를 로그 하나로만 두면 브로커 한 대의 디스크와 처리 능력을 넘지 못합니다. 그래서 토픽 하나를 로그 여러 개로 나눕니다. 나뉜 로그 하나하나가 파티션입니다.

파티션들은 여러 브로커에 흩어져 놓입니다. 파티션을 나눠 든 브로커 여러 대는 묶여서 하나의 Kafka 로 돕니다. 이 무리가 클러스터입니다.

기록이 늘면 파티션과 브로커를 늘려 받아 냅니다. 기계 한 대를 키우지 않고 대수를 늘려 버티는 이 방식이 수평 확장입니다.

오프셋은 파티션마다 따로 셉니다. 파티션 0 에도 오프셋 7 이 있고 파티션 1 에도 오프셋 7 이 있습니다. 그래서 기록 하나를 가리키려면 토픽·파티션·오프셋 셋을 함께 대야 합니다.

보내는 쪽은 기록마다 키라는 값을 하나 붙일 수 있습니다. 이 문서에서는 주문 번호를 키로 붙입니다. 새 기록이 어느 파티션으로 갈지는 이 키가 정합니다.

키를 해시 함수에 넣어 나온 값으로 파티션을 고릅니다. 해시 함수는 같은 입력에 언제나 같은 값을 돌려줍니다. 키가 같으면 언제나 같은 파티션으로 가는 것도 이 덕분입니다. 한 주문의 이벤트는 전부 한 파티션에 모입니다.

flowchart TD
    PR["주문 서비스"] --> H["키를 해시해 파티션을 고른다"]
    subgraph T["토픽 orders"]
        P0["파티션 0 · 브로커 1"]
        P1["파티션 1 · 브로커 2"]
        P2["파티션 2 · 브로커 3"]
    end
    H -->|주문 1001 접수| P0
    H -->|주문 1002 접수| P2
    H -->|주문 1001 결제 완료| P0

주문 1001 의 두 이벤트가 둘 다 파티션 0 으로 갔습니다. 키가 같기 때문입니다. 주문 1002 는 키가 달라 파티션 2 로 갔습니다.

순서는 파티션 안에서만 지켜집니다. 파티션 0 안에서는 먼저 적힌 기록이 먼저 읽힙니다. 파티션 0 과 파티션 2 사이에는 순서가 없습니다. 순서가 중요한 이벤트끼리는 같은 키를 붙여 한 파티션에 모아야 합니다.

프로듀서와 컨슈머 그룹

Kafka 에 기록을 적는 프로그램이 프로듀서입니다. 기록을 읽어 가는 프로그램은 컨슈머입니다. 앞의 예에서 주문 서비스는 프로듀서입니다. 결제·재고·알림 서비스는 컨슈머입니다.

컨슈머 한 대로 읽는 속도가 모자라면 여러 대를 띄웁니다. 같은 일을 하는 컨슈머 여러 대를 한 무리로 묶은 것이 컨슈머 그룹입니다. 결제 서비스를 두 대 띄우면 그 둘이 결제 그룹 하나가 됩니다.

그룹 안에서는 파티션을 나눠 맡습니다. 파티션 하나는 그룹 안의 컨슈머 한 대만 읽습니다. 그래서 같은 주문을 결제 서비스 두 대가 겹쳐 처리하지 않습니다. 한 파티션을 한 대가 차례로 읽으니 파티션 안의 순서도 지켜집니다.

그룹끼리는 서로 상관하지 않습니다. 결제 그룹과 알림 그룹은 같은 토픽을 저마다 처음부터 끝까지 읽습니다. 보내는 쪽이 한 번 내보낸 것을 받겠다고 나선 쪽 모두가 받는 방식을 발행-구독이라고 합니다. Kafka 는 그룹 안에서는 큐처럼 기록을 나눠 갖습니다. 그룹 사이에서는 발행-구독처럼 같은 기록을 각자 받습니다.

flowchart TD
    subgraph T["토픽 orders"]
        P0["파티션 0"]
        P1["파티션 1"]
        P2["파티션 2"]
    end
    subgraph G1["결제 그룹"]
        C1["결제 서비스 1"]
        C2["결제 서비스 2"]
    end
    subgraph G2["알림 그룹"]
        C3["알림 서비스"]
    end
    P0 --> C1
    P1 --> C1
    P2 --> C2
    P0 --> C3
    P1 --> C3
    P2 --> C3

결제 그룹은 파티션 셋을 두 대가 나눠 읽습니다. 알림 그룹은 한 대가 셋을 모두 읽습니다. 두 그룹 모두 주문 이벤트를 빠짐없이 받습니다.

그룹 안의 컨슈머 수를 파티션 수보다 늘려도 읽는 속도는 오르지 않습니다. 파티션 셋에 컨슈머가 넷이면 한 대는 맡을 파티션이 없어 놉니다. 그래서 파티션 수가 한 그룹이 동시에 굴릴 수 있는 컨슈머 수의 상한이 됩니다.

컨슈머가 새로 들어오거나 멈추면 그룹은 파티션을 다시 나눕니다. 이 다시 나누기가 리밸런싱입니다. 나누는 동안 그 그룹은 잠깐 읽기를 멈출 수 있습니다.

오프셋 커밋과 전달 보장

컨슈머가 멈췄다가 다시 뜨면 어디서부터 읽을지 알아야 합니다. 그래서 컨슈머는 처리를 마친 곳의 오프셋을 Kafka 에 적어 둡니다. 이 일이 오프셋 커밋입니다. 다시 뜬 컨슈머는 커밋된 오프셋부터 읽습니다.

커밋을 처리 앞에 하느냐 뒤에 하느냐에 따라 받는 쪽이 겪는 일이 갈립니다. 둘 다 처리하던 도중에 컨슈머가 죽는 경우를 봅니다.

커밋하는 때 도중에 죽으면 이 보장의 이름
처리하기 전 커밋은 됐는데 처리가 안 된 기록을 잃습니다 최대 한 번
처리한 뒤 처리는 됐는데 커밋이 안 된 기록을 다시 받습니다 적어도 한 번

잃은 기록은 되찾을 길이 없습니다. 두 번 받은 기록은 받는 쪽이 걸러 낼 수 있습니다. 그래서 대개 처리한 뒤에 커밋합니다. 아래 그림이 그때 생기는 중복입니다.

sequenceDiagram
    participant 결제 as 결제 서비스
    participant K as Kafka
    결제->>K: 오프셋 2 부터 달라
    K-->>결제: 오프셋 2 · 3 · 4
    Note over 결제: 세 건을 결제 처리한다
    Note over 결제: 커밋하기 전에 죽는다
    결제->>K: 다시 떠서 오프셋 2 부터 달라
    K-->>결제: 오프셋 2 · 3 · 4
    Note over 결제,K: 이미 처리한 세 건을 한 번 더 받는다

결제 서비스는 오프셋 2 부터 4 까지를 처리했습니다. 그런데 커밋이 남지 않아 Kafka 는 결제 서비스가 아직 오프셋 2 에 있다고 압니다. 그래서 같은 세 건을 다시 내줍니다.

컨슈머는 이런 중복을 견디도록 짭니다. 같은 기록을 두 번 받아도 한 번 받은 것과 결과가 같아야 합니다. 이 성질이 멱등성입니다. 결제 서비스라면 주문 번호로 이미 결제한 주문인지 먼저 확인하고 넘어갑니다.

읽은 결과를 다시 Kafka 에 쓰는 컨슈머도 있습니다. orders 토픽에서 주문을 읽어 분당 주문 수를 센 뒤 그 값을 다른 토픽에 쓰는 일이 그렇습니다. 읽기도 쓰기도 Kafka 안에서 끝납니다.

이런 일에는 Kafka 가 정확히 한 번 처리를 줍니다. 결과 쓰기와 오프셋 커밋을 트랜잭션 하나로 묶습니다. 트랜잭션으로 묶인 쓰기는 모두 남거나 모두 사라집니다. 컨슈머가 도중에 죽어도 결과가 두 번 쓰이지 않습니다.

결제 대행사를 부르는 일처럼 Kafka 밖으로 나가는 일은 이 묶음에 들지 않습니다. 그런 일은 앞에서 본 멱등성으로 중복을 견딥니다.

복제와 리더

브로커 한 대가 멈추면 그 브로커에 있던 파티션을 못 읽습니다. 그래서 파티션마다 똑같은 사본을 여러 브로커에 둡니다. 이 사본이 복제본입니다. 몇 벌을 둘지는 복제 계수로 정합니다.

복제본 가운데 한 벌이 리더입니다. 프로듀서의 쓰기와 컨슈머의 읽기는 대개 리더가 받습니다. 나머지 복제본인 팔로워는 리더에게서 새 기록을 가져가 똑같이 적습니다.

flowchart TD
    subgraph BR1["브로커 1"]
        A0["파티션 0 · 리더"]
        A1["파티션 1 · 팔로워"]
    end
    subgraph BR2["브로커 2"]
        B0["파티션 0 · 팔로워"]
        B1["파티션 1 · 리더"]
    end
    subgraph BR3["브로커 3"]
        C0["파티션 0 · 팔로워"]
        C1["파티션 1 · 팔로워"]
    end
    B0 -.->|리더에게서 가져간다| A0
    C0 -.->|리더에게서 가져간다| A0
    A1 -.->|리더에게서 가져간다| B1
    C1 -.->|리더에게서 가져간다| B1

파티션 0 의 리더는 브로커 1 에, 파티션 1 의 리더는 브로커 2 에 있습니다. 파티션마다 리더를 다른 브로커에 두어 쓰기가 한 브로커에 몰리지 않게 합니다.

리더를 끝까지 따라잡고 있는 복제본들의 목록을 Kafka 가 따로 관리합니다. 이 목록이 동기화된 복제본 목록입니다. 팔로워가 너무 뒤처지면 이 목록에서 빠집니다.

리더가 있던 브로커가 멈추면 동기화된 복제본 가운데 하나가 새 리더가 됩니다. 새 리더를 고르는 이 일이 리더 선출입니다. 목록에 든 복제본은 리더와 같은 기록을 갖고 있어서 새 리더가 되어도 기록이 빠지지 않습니다.

프로듀서는 쓰기가 끝났다고 칠 때를 고릅니다. 리더 한 벌만 적으면 끝으로 칠 수도 있습니다. 동기화된 복제본이 전부 적어야 끝으로 칠 수도 있습니다. 이 선택은 프로듀서의 acks 설정이 정합니다.

sequenceDiagram
    participant 프로 as 프로듀서
    participant 리더 as 리더 · 브로커 1
    participant F1 as 팔로워 · 브로커 2
    participant F2 as 팔로워 · 브로커 3
    프로->>리더: 주문 1001 접수를 적어라
    리더->>리더: 자기 로그 끝에 덧붙인다
    F1->>리더: 새 기록을 가져간다
    F2->>리더: 새 기록을 가져간다
    Note over 리더,F2: 동기화된 복제본이 전부 적었다
    리더-->>프로: 적었다

리더 한 벌만 기다리면 답이 일찍 옵니다. 대신 리더가 곧바로 죽으면 팔로워가 아직 못 가져간 기록을 잃을 수 있습니다. 전부를 기다리면 답이 늦게 오는 대신 그 손실을 막습니다.

클러스터 정보를 들고 있는 곳

클러스터는 기록 말고도 들고 있어야 할 정보가 있습니다. 어떤 토픽이 있는지, 파티션마다 리더가 어느 브로커인지 같은 정보입니다. 이렇게 데이터를 설명하는 정보가 메타데이터입니다.

예전의 Kafka 는 이 메타데이터를 ZooKeeper 라는 별도 서버 무리에 맡겼습니다. 그래서 Kafka 를 굴리려면 ZooKeeper 도 함께 띄우고 관리해야 했습니다.

지금의 Kafka 는 메타데이터를 스스로 관리합니다. 컨트롤러 역할을 맡은 서버 몇 대가 메타데이터를 함께 들고 있습니다. 컨트롤러만 하는 서버를 따로 띄울 수 있습니다. 한 서버가 브로커와 컨트롤러를 겸할 수도 있습니다.

어느 쪽이든 컨트롤러는 Kafka 프로그램 안의 역할입니다. ZooKeeper 같은 다른 프로그램을 따로 굴리지 않아도 됩니다.

컨트롤러들은 합의로 값을 맞춥니다. 합의는 여러 서버가 한 값에 함께 동의하는 절차입니다. 일부가 멈춰도 동의된 값은 남습니다.

컨트롤러들이 쓰는 합의 방식을 KRaft(Kafka Raft)라고 부릅니다. 널리 쓰이는 합의 절차인 Raft 를 Kafka 에 맞게 옮긴 것입니다.

디스크에 쓰면서도 버티는 방식

Kafka 는 받은 기록을 모두 디스크에 씁니다. 디스크는 메모리보다 읽고 쓰는 데 오래 걸립니다. Kafka 는 쓰는 방식을 골라 이 차이를 줄입니다.

첫째는 끝에만 덧붙이는 것입니다. 디스크는 여기저기 옮겨 다니며 쓸 때보다 한 방향으로 이어 쓸 때 훨씬 많은 양을 씁니다. Kafka 의 로그는 끝에만 덧붙이므로 쓰기가 언제나 이어 쓰기입니다. 이렇게 이어 쓰는 것이 순차 쓰기입니다.

둘째는 운영체제가 잡아 두는 메모리를 쓰는 것입니다. 방금 파일에 쓴 내용은 운영체제가 한동안 메모리에 들고 있습니다. 이 메모리가 페이지 캐시입니다. 뒤따라 읽는 컨슈머는 대개 디스크까지 가지 않고 이 메모리에서 기록을 받습니다.

셋째는 묶어 보내는 것입니다. 프로듀서는 기록을 한 건씩 보내지 않고 여러 건을 모아 한 번에 보냅니다. 네트워크 왕복과 디스크 쓰기 횟수가 줄어듭니다. 대신 모으는 동안 조금 기다립니다.

넷째는 복사를 건너뛰는 것입니다. 컨슈머에게 기록을 보낼 때 파일 내용을 Kafka 프로그램의 메모리로 옮겨 오지 않습니다. 운영체제가 페이지 캐시에서 네트워크로 바로 넘기게 합니다. 중간 복사를 없애는 이 방법이 제로 카피입니다.

flowchart TD
    PR["프로듀서 · 여러 건을 묶어 보낸다"] --> BK["브로커 · 로그 끝에 덧붙인다"]
    BK --> PC["페이지 캐시 · 운영체제가 잡아 둔 메모리"]
    PC -->|나중에 내려 쓴다| DK["디스크의 로그 파일"]
    PC -->|복사 없이 네트워크로 넘긴다| CS["뒤따라 읽는 컨슈머"]

오래된 기록을 치우는 두 방법

로그를 끝없이 쌓으면 디스크가 찹니다. 그래서 토픽마다 기록을 얼마나 둘지 정합니다. 정한 기간이 지나거나 정한 크기를 넘으면 오래된 기록부터 지웁니다. 이 기간이 보존 기간입니다.

파티션의 로그는 파일 하나가 아니라 여러 파일로 잘려 있습니다. 잘린 파일 하나가 세그먼트입니다. 보존 기간이 지나면 기록을 한 건씩 지우지 않습니다. 오래된 세그먼트 파일을 파일째 버립니다.

두 번째 방법은 로그 컴팩션입니다. 시간으로 자르지 않고 키마다 가장 최근 기록 하나만 남깁니다. 회원별 주소처럼 마지막 값만 알면 되는 데이터에 씁니다. 새 컨슈머가 처음부터 읽으면 키마다 최신 값을 모두 얻습니다.

flowchart TD
    subgraph BEFORE["컴팩션 전"]
        E0["오프셋 0 · 회원 1 · 서울"] --> E1["오프셋 1 · 회원 2 · 부산"]
        E1 --> E2["오프셋 2 · 회원 1 · 대전"]
        E2 --> E3["오프셋 3 · 회원 3 · 광주"]
        E3 --> E4["오프셋 4 · 회원 2 · 인천"]
    end
    subgraph AFTER["컴팩션 뒤"]
        F2["오프셋 2 · 회원 1 · 대전"] --> F3["오프셋 3 · 회원 3 · 광주"]
        F3 --> F4["오프셋 4 · 회원 2 · 인천"]
    end
    BEFORE -->|키마다 마지막 기록만 남긴다| AFTER

회원 1 과 회원 2 의 옛 주소가 빠졌습니다. 남은 기록의 오프셋은 바뀌지 않습니다. 오프셋 0 과 1 이 빠져 번호 사이에 틈이 생길 뿐입니다.

포기한 것

Kafka 는 많은 기록을 오래 두고 여러 쪽이 나눠 읽게 하려고 몇 가지를 내려놓았습니다.

포기한 것 무엇이 곤란해지나
토픽 전체의 순서 순서는 파티션 안에서만 지켜집니다. 토픽 전체 순서가 필요하면 파티션을 하나로 둡니다. 그러면 그룹마다 읽는 컨슈머도 한 대뿐입니다
기록 한 건 단위의 확인 컨슈머는 오프셋 하나로 위치를 기억합니다. 한 건만 실패로 빼 두고 뒤를 먼저 처리하는 일은 받는 쪽이 따로 만들어야 합니다
골라 꺼내기 우선순위를 매기거나 조건에 맞는 기록만 꺼내는 기능이 없습니다. 파티션 순서대로 읽습니다
파티션 수 줄이기 파티션은 늘릴 수만 있습니다. 늘리면 같은 키가 다른 파티션으로 가기 시작해 키별 순서가 흐트러질 수 있습니다
한 번만 받기 처리한 뒤 커밋하면 중복이 생길 수 있습니다. 받는 쪽이 중복을 견디게 짜야 합니다
간단한 운영 브로커 여러 대와 복제, 디스크 용량, 파티션 배치를 사람이 챙겨야 합니다

쓰는 곳과 안 쓰는 곳

Kafka 는 한 번 일어난 일을 여러 곳이 저마다 받아야 할 때 씁니다. 받는 쪽이 나중에 늘어나거나, 지난 기록을 다시 읽어야 할 때도 씁니다.

쓰임 무엇을 싣나
서비스 사이 이벤트 전달 주문 접수·결제 완료처럼 여러 서비스가 알아야 하는 일입니다
로그와 지표 모으기 서버 여러 대가 남긴 기록을 한곳에 모읍니다. 검색 시스템과 저장소로 흘려보냅니다
변경 데이터 캡처 데이터베이스에 생긴 변경 한 건 한 건입니다. 다른 시스템이 읽어 제 데이터를 맞춥니다
스트림 처리 들어오는 대로 집계할 이벤트입니다. 분당 주문 수 같은 값을 쉬지 않고 갱신합니다

Kafka 에는 둘레 도구가 함께 딸려 옵니다. 다른 시스템과 Kafka 사이에서 데이터를 옮기는 Kafka Connect 가 있습니다. 토픽을 읽어 가공한 결과를 다시 토픽에 쓰는 라이브러리 Kafka Streams 도 있습니다.

요청을 보내고 답을 곧바로 받아야 하는 호출에는 쓰지 않습니다. Kafka 는 적어 두고 나중에 읽히는 구조라 답이 돌아오는 길이 따로 없습니다.

작업 한 건마다 성공과 실패를 따로 표시해야 하는 작업 큐에도 잘 맞지 않습니다. 실패한 것만 다시 시도하거나 몇 분 뒤로 미뤄야 하는 일은 RabbitMQ 나 Amazon SQS(Simple Queue Service, 아마존의 관리형 큐 서비스) 같은 큐가 받습니다. 이런 큐는 메시지를 한 건 단위로 확인합니다. 확인한 메시지는 지웁니다.

한 번 발행한 것을 여러 곳에 뿌리는 일은 Amazon SNS(Simple Notification Service, 아마존의 관리형 알림 서비스) 같은 발행-구독 서비스도 합니다. 그런 서비스는 받는 쪽에 밀어 보냅니다. 기록을 오래 쌓아 두지 않습니다. Kafka 는 기록을 남겨 둡니다. 받는 쪽이 와서 가져갑니다.

관련 항목

Kafka 를 이루는 구성 요소

브로커 · 클러스터 · 토픽 · 파티션 · 오프셋 · 세그먼트 · 프로듀서 · 컨슈머 · 컨슈머 그룹 · 메타데이터

Kafka 가 기록을 쓰고 치우는 방식

로그 · 순차 쓰기 · 페이지 캐시 · 제로 카피 · 보존 기간 · 로그 컴팩션 · 컴팩션 · 툼스톤

Kafka 가 복제본을 맞추고 리더를 고르는 수단

복제 · 복제 계수 · 리더 선출 · 동기화된 복제본 · Kafka KRaft · ZooKeeper · Raft · 합의

Kafka 가 받는 쪽에 주는 전달 보장

오프셋 커밋 · 최대 한 번 · 적어도 한 번 · 정확히 한 번 · 멱등성 · 트랜잭션 · 확인 응답 · 리밸런싱

Kafka 에 붙여 데이터를 옮기고 가공하는 도구

Kafka Connect · Kafka Streams · Debezium · 스키마 레지스트리 · Apache Flink · Apache Spark

Kafka 와 같은 역할을 두고 겨루는 메시징 시스템

RabbitMQ · Amazon SQS · Amazon SNS · Amazon Kinesis · Apache Pulsar · Redpanda · NATS · Redis Streams

Kafka 를 쓰는 설계와 처리 방식

이벤트 기반 아키텍처 · 변경 데이터 캡처 · 스트림 처리 · 이벤트 소싱 · 로그 수집 · 데이터 파이프라인 · 마이크로서비스

Kafka 가 다루는 확장과 결합의 성질

느슨한 결합 · 수평 확장 · 파티셔닝 · 메시지 키 · 해시 함수 · 처리량 · 지연 · 백프레셔 · 폴링

Kafka 가 속하는 상위 분류

메시지 브로커 · 메시지 큐 · 발행-구독 · 분산 시스템 · 데이터 엔지니어링 · 미들웨어 · 이벤트 · 메시지

다른 이름: Kafka · 카프카 · 아파치 카프카