Apache Kafkaのコンシューマーグループとは?~イベントの「複製」と「負荷分散」について

kafka

みなさんこんにちは!本ブログではApache Kafkaの強力なコア概念である「コンシューマーグループ(Consumer Group)」を取り上げていきます。今回はPythonとtmux(4分割ターミナル)を使い、トピックへ100件のイベントを送信した際の挙動と、裏側の分散ロジックについて検証した記録をまとめました。

1. コンシューマーグループ(Consumer Group)とは

Kafkaは、データを受け取る受信アプリを「グループ」という単位で管理します。このグループの組み方によって、1つのトピックに対して2つの異なる挙動を同時に実現できます。

  • 異なるグループ間 ➔ パブサブ型(イベントの複製): 別のグループ同士には、まったく同じデータがそれぞれ全件コピー(複製)されて配信されます。
  • 同じグループ内 ➔ キュー型(負荷分散): 同一グループ内のサーバー同士では、データを重複することなく綺麗に分担して配信されます。

2. 検証デモの実行手順と環境構成

パーティション数を「3」に設定したトピック five を作成し、ターミナルをtmuxで4分割して以下の手順で検証を行いました。

  1. 【左上・左下ペイン】 Group-X(2台起動): 負荷分散を検証するコンシューマー(Consumer-X1 / X2)
  2. 【右上ペイン】 Group-Y(1台起動): イベントの複製(全件受信)を検証するコンシューマー(Consumer-Y1)
  3. 【右下ペイン】 Producer: 送信側から userA 50件、userB 50件(計100件)を連続送信

3. 使用したPythonスクリプト

検証に使用した主要なソースコードです。(事前に pip install confluent-kafka の実行が必要です)

■ 受信側:consumer_flexible.py

from confluent_kafka import Consumer
import sys

group_id = sys.argv[1]
consumer_id = sys.argv[2]

conf = {
    'bootstrap.servers': 'localhost:9092',
    'group.id': group_id,
    'auto.offset-reset': 'earliest'
}

consumer = Consumer(conf)
consumer.subscribe(['five'])

print(f"--- 【{consumer_id}】 グループ【{group_id}】としてデータ待ち中... ---")

try:
    count = 0
    while True:
        msg = consumer.poll(1.0)
        if msg is None: continue
        if msg.error(): continue
        
        count += 1
        print(f"[{count}件目] Key: {msg.key().decode('utf-8')}, Value: {msg.value().decode('utf-8')}")
except KeyboardInterrupt:
    pass
finally:
    consumer.close()
    

■ 送信側:key_producer.py

from confluent_kafka import Producer
import time

p = Producer({'bootstrap.servers': 'localhost:9092'})

print("データの送信を開始します(100件)...")

for i in range(50):
    p.produce('five', key='userA', value=f'data-A-{i}')
    p.produce('five', key='userB', value=f'data-B-{i}')
    p.flush()
    time.sleep(0.05)

print("送信完了")
    

4. デモの実行結果と受信件数

▼ デモ実行画面(プロデューサーからイベント送信中の様子)

Kafkaコンシューマーグループ デモ実行画面(イベント送信中)

イベント送信完了後、各ペインに立ち上げたコンシューマーの受信ログおよび最終的な合計件数は以下の通りになりました。

グループ名サーバーID最終受信件数ログから見えた決定的な特徴
Group-X
(2台構成)
Consumer-X150件userB のイベントのみを100%固定受信(負荷分散)
Consumer-X250件userA のイベントのみを100%固定受信(負荷分散)
Group-Y
(1台構成)
Consumer-Y1100件全件(userA userB)を漏れなく受信(イベントの複製)

なぜ自動的に綺麗に分かれたのか?裏側の仕様

ソースコード側には割り当ての設定を1行も記述していません。それなのにこの結果になったのは、Kafkaのデフォルトの仕様によるものです。

  1. Keyによる「固定配置」: 送信時にユーザー名をKeyに指定したため、Kafkaが自動的にハッシュ値を計算し、userAはパーティション2、userBはパーティション1へと固定配置しました。
  2. 重複なき「自動割り当て」: 3つのパーティションに対して同じグループに2台のサーバーがいたため、Kafkaは裏側で自動的に仕事を割り振りました(X1がパーティション1、X2がパーティション2を担当)。

この2つの標準仕様が合体した結果、「負荷を完全に分散させつつ、同じユーザーのデータは必ず同じサーバーに届く(順序性の保証)」という挙動が完全自動で実現しています。

5. まとめ

今回の実証デモを通して、コンシューマーグループを分けることで「イベントの複製(並列処理)」、グループ内では「負荷分散・順序保証」が完璧に行えることが確認できました。
データ量が増えても、プログラムを1行も変えずにコンシューマーの台数を増やすだけで処理能力を水平スケールできる、Kafkaの洗練された設計の美しさを体感できる検証となりました。

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。
エンジニア募集中!

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です