Kafkaコンシューマーグループのパーティション割り当てアルゴリズム解説

Kafkaにおけるパーティション配分の基本概念

Kafkaのコンシューマーグループ内で複数のインスタンスが稼働している場合、対象トピックのパーティションをどのコンシューマーが担当するかを決定する割り当てプロセスが発生します。この処理はPartitionAssignorインタフェースによって定義されており、デフォルトではスティッキーベースのロジックが採用されています。ブローカー内のコーディネータが割り当て計算を実行し、結果をグループ内の全メンバーに配布する仕組みです。

主要な割り当て戦略の詳細

1. RangeAssignor(範囲ベース配分)

このアルゴリズムは、トピックごとに独立してパーティションの分割を行います。対象トピックのパーティションIDとコンシューマーリストをそれぞれ昇順にソートした後、パーティション总数をコンシューマー数で割り算します。割り切れない余りが出た場合、ソート順で上位のコンシューマーに追加割り当てが行われます。例えば、パーティションが7つでコンシューマーが3つの場合、基本割当数は2となり、余りの1つは最初のコンシューマーに付与されます。結果として、コンシューマー間での担当パーティション数の差は最大で1つに収まりますが、トピックごとに計算が行われるため、複数のトピックをまたいだ全体バランスには注意が必要です。

2. RoundRobinAssignor(ラウンドロビン配分)

グループ全体のコンシューマーと、それらが購読している全てのパーティションをリスト化し、辞書順に並べ替えた上で輪転式に割り当てていきます。すべてのコンシューマーが同じトピック集合を購読しており、かつスレッド数が均等である場合、パーティションは完全に均等に分散されます。この方式は、単一トピックまたは同一サブスクリプションを持つ複数トピックにおいて、負荷を公平に分散させるのに適しています。ただし、コンシューマー間の購読トピックやスレッド構成に偏りがあると、配分が不均一になる可能性がある点には留意が必要です。

3. StickyAssignor(スティッキー配分)

再バランス発生時のパーティション移動コストを最小化することを主目的とした戦略です。従来の手法は再割り当て時に全パーティションの配置をゼロから再計算しますが、Sticky方式は前回の割り当て結果を粘着性を持って維持しようとしつつ、新たに発生した要件(コンシューマーの増減など)に対応します。主な設計目標は以下の2点です。

  • 各コンシューマーの担当パーティション数の最大差を1以下に保つ(負荷バランスの維持)
  • 再割り当て時に、可能な限り既存の割り当て状態を維持する(処理の中断時間短縮)

両者の要件が衝突する場合は、負荷バランスが最優先されます。このアルゴリズムは、頻繁にコンシューマーの追加・削除が発生する動的な環境や、再バランスによるストリーム処理の中断を避けたいケースで特に有効です。

実装例と設定方法

各戦略を有効にするには、コンシューマー起動時のプロパティに該当クラス名を指定します。Java環境では、Propertiesオブジェクト経由で設定を構築するのが一般的です。

import org.apache.kafka.clients.consumer.ConsumerConfig;
import java.util.Properties;

public class KafkaConsumerConfigBuilder {

    public static Properties buildRangeAssignerConfig() {
        Properties props = new Properties();
        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
        props.put(ConsumerConfig.GROUP_ID_CONFIG, "analytics-group");
        props.put(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG, 
                  "org.apache.kafka.clients.consumer.RangeAssignor");
        return props;
    }

    public static Properties buildRoundRobinAssignerConfig() {
        Properties settings = new Properties();
        settings.setProperty("bootstrap.servers", "kafka-broker-01:9092");
        settings.setProperty("group.id", "log-processing");
        settings.setProperty(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG, 
                             "org.apache.kafka.clients.consumer.RoundRobinAssignor");
        return settings;
    }

    public static Properties buildStickyAssignerConfig() {
        Properties clientProps = new Properties();
        clientProps.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.1.10:9092");
        clientProps.setProperty(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
        clientProps.put(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG, 
                        "org.apache.kafka.clients.consumer.StickyAssignor");
        return clientProps;
    }
}

複数の割り当て戦略を組み合わせて使用したい場合、カンマ区切りでクラス名を指定することも可能です。Kafka 2.4以降ではStickyAssignorがデフォルトとして推奨されており、特に設定を変更しない限りはこの挙動が採用されます。

タグ: Kafka consumer-group partition-assignment rebalancing Java

6月7日 20:58 投稿