Kafka: a Distributed Messaging System for Log Processing (2011)
August 22, 2022Kafkaは、LinkedInで開発された分散メッセージングサービスである。 配信モデルはpullベースであり、consumerが、メッセージをリクエストし、メッセージを消費するスループットを制御する。 consumerは、関心のあるメッセージのストリームであるトピックを作る。 producerは、トピックつきのメッセージをborkerに送り、brokerがメッセージを保持する。 consumerは、brokerのトピックからメッセージを取得する。
consumer group単位でconsumerをグループ化できる。 1つのグループは複数のトピックを購読でき、グループ内の1つのconsumerが別々のメッセージを受信できる。 これにより、重複を避けながら複数のConsumerで同じトピックを購読できる。
配信のat-least-onceは保証されているが、exactly onceではない。 正常状態では、メッセージはちょうど一回だけconsumer groupに配信される。 一方、あるconsumerが正常に停止しなかった場合、フェールオーバーしたconsumerが、停止したconsumerで未処理だったメッセージを受信しうる。
brokerは、負荷分散のために、トピックを複数のパーティションに分け、1つ以上のパーティションをもつ。 producerはpartitionに対してメッセージを送る。 あるパーティションからあるconsumerへのメッセージの順序は、パーティションに到着したメッセージの順序と等しいことが保証される。 パーティンションは、1つの論理的なログに相当し、複数のセグメントファイルに分割されて保存される。 brokerはメッセージをバッファに貯め、一定時間すぎたときか、バッファが満杯になるときに、セグメントファイルにメッセージを書き込む。 メッセージはセグメントファイルに書き込まれるまで、consumerには公開されない。
メッセージの消費状況を、brokerではなく、consumerが管理をするところが特徴的である。 メッセージには一意に特定するIDはないため、consumerは、ログ上の相対位置でメッセージを指定し、brokerにリクエストを送る。 メッセージを受信したら、次のメッセージの相対位置を計算し、次のリクエストを送る。 そのため、consumerは、相対位置を調整すれば、以前のメッセージを再取得できる。 しかし、そのままでは、メッセージがbrokerに貯まり続けるので、brokerはproducerから受信して一定期間過ぎたメッセージを破棄する。
Kafkaのキャッシュはファイルシステムのページキャッシュにまかせ、アプリケーションレイヤーではキャッシュをもたない。 これにより、brokerが再起動した場合も継続してキャッシュを利用でき、また、メモリの節約できるためガベージコレクションも短くなる。 また、brokerは、sendfileシステムコールを使い、セグメントファイルからネットワークソケットに直接メッセージを書き込み、readやwriteのオーバーヘッドを減らしている。
雑記
Kafkaはbrokerやconsumerの追加や削除、consumerが次に取得するべきメッセージの相対位置をZookeeperで管理するとあるが、 現在、Zookeeperを使わずKafka自体で管理するための開発が進められている。
セグメントファイルに書き込まれる前にbrokerが停止したらProducerが送信したメッセージは消失しそう。 セグメントファイルに書き込まれてから、ACKメッセージを送るのだろうか。 実験ではRabbitMQと性能を比較しているが、ablation studyではないので、sendfileやページキャッシュによる個別の工夫が処理性能にどれだけ貢献しているかは、実験から分からない。
論文へのリンク