Kafkaの分散処理と信頼性設計

クラスタ構成の基本構造

Kafkaでは、Topicという論理的な単位が複数のPartitionに分割され、各Brokerに分散配置されます。各Partitionにはリーダーとフォロワーが存在し、リーダーがデータの読み書きを担当します。フォロワーはデータの複製を保持することで可用性を確保します。

メッセージの書き込みはリーダーに集中し、その後フォロワーに複製されます。この構造により、障害発生時でもデータの損失を最小限に抑えつつ継続運用が可能です。

メッセージの処理とOffset管理

ProducerはメッセージをPartitionに送信し、ConsumerはOffsetという位置情報をもとにメッセージの消費位置を管理します。OffsetはConsumerグループごとに管理され、Consumerが処理完了後にKafkaに送信して記録します。

Offsetのコミット方法には2種類があります:

  • 同期コミット: メッセージ処理後明示的にコミットするためデータ損失のリスクが少ないが、パフォーマンスが低下する。
  • 非同期コミット: 処理と同時にバックグラウンドでコミットするため高速だが、処理失敗時にメッセージが失われる可能性がある。

インターセプターの利用

ProducerやConsumerにはインターセプターを設定でき、メッセージ処理の前後で共通処理を挟むことが可能です。以下はインターセプターの例です:


public class CustomInterceptor implements ConsumerInterceptor<String, String> {
    @Override
    public ConsumerRecords<String, String> onConsume(ConsumerRecords<String, String> records) {
        // メッセージ受信時の処理
        return records;
    }

    @Override
    public void onCommit(Map<TopicPartition, OffsetAndMetadata> offsets) {
        // Offsetコミット時の処理
    }
}

シリアライズと通信

ネットワーク経由でのデータ送受信のため、Kafkaではシリアライズとデシリアライズが必要です。KafkaはStringやJSONなど基本的な型をサポートしますが、カスタムのシリアライザーも実装可能です。

以下はYAMLでの設定例です:


spring:
  kafka:
    producer:
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.apache.kafka.common.serialization.StringSerializer
    consumer:
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer

パーティション割り当て戦略

Consumerグループ内の各インスタンスがどのPartitionを処理するかは、以下の戦略によって決まります:

  • Range: Partitionを連続区間で割り当てます。
  • Round-robin: パーティションをインスタンス間で順番に割り当てます。
  • Sticky: 前回の割り当てをできるだけ維持します。

Producerのバッチ送信とバッファ管理

KafkaのProducerはRecordAccumulatorを通じてメッセージをバッチ化し、一定量をまとめて送信します。この仕組みによりネットワーク負荷を軽減しつつ、高スループットを実現します。

メッセージはPartitionごとにバッファリングされ、以下の条件で送信されます:

  • バッチサイズに到達
  • 一定時間(linger.ms)が経過

メッセージの確認応答(acks)

acksパラメータによって、ProducerがBrokerからの応答をどの程度待つかが決まります:

  • acks=0: 応答を待たず送信のみ。
  • acks=1: リーダーに書き込まれたことを確認。
  • acks=all: リーダーとフォロワーの全ノードで書き込み完了を確認。

べき等性の実現

KafkaはProducerごとにPID(Producer ID)を割り当て、Partitionごとにシーケンス番号を管理します。Brokerは受信時にこの番号をチェックし、重複メッセージの処理を回避します。

ZooKeeperとの連携

Kafkaのクラスタ管理にはZooKeeperが利用され、Controller Brokerがクラスタ全体の管理を行います。Controller Brokerは以下の役割を担います:

  • Brokerの追加・削除を検知
  • TopicやPartitionの変更を管理
  • メタデータの更新と配布

リーダー選出とISR管理

Partitionのリーダー選出は、ISR(In-Sync Replica)の中から行われます。ISRにはリーダーと同期が取れているフォロワーのみが含まれます。

リーダーがダウンした場合、ISR内のフォロワーが新たなリーダーに選出されますが、HW(High Watermark)に基づいてデータの一貫性を保ちます。

自動バランス機能

Kafkaは定期的にリーダーの負荷バランスをチェックし、不均衡が発生した場合にリバランスを自動で実行します。この機能は以下のパラメータで制御されます:

  • auto.leader.rebalance.enable: 自動リバランスの有効化
  • leader.imbalance.check.interval.seconds: チェック間隔
  • leader.imbalance.per.broker.percentage: 不均衡の閾値

タグ: Kafka distributed-system messaging ZooKeeper stream-processing

10月7日 02:35 投稿