読者です 読者をやめる 読者になる 読者になる

夢とガラクタの集積場

落ちこぼれ三流エンジニアである管理人の夢想=『夢』と、潰えた夢=『ガラクタ』の集積場です。

Apache Kafka概要確認(その5 メッセージの状態管理

こんにちは。とりあえず続きです。

9.エンドツーエンドのバッチ圧縮

多くの場合、ボトルネックはCPUではなくネットワークになる。
特にデータセンター間でメッセージを転送する必要があるネットワークトポロジを組んでいる場合は。

もちろん、ユーザはKafkaの介在なしでもいつでも圧縮されたメッセージを送信することはできるが、
えてしてその圧縮効率はメッセージの中のフィールドの繰り返し等の事情があり、悪い。
JSONのフィールド名称、User Agentの値など)

効率的に圧縮を行うためには個々のメッセージ単位で圧縮を行うのではなく、
複数のメッセージをまとめて圧縮させる必要がある。

理想的にはエンドツーエンド圧縮方式・・・
メッセージはProducerプロセスが送信する前に圧縮され、サーバに圧縮状態で保持される。
その上で各Consumerプロセスで解凍する方式を取るのが望ましい。

Kafkaは再帰的なMessageSetによってこの理想的な圧縮方式を実現している。
メッセージは送信前にメッセージ群をまとめて圧縮し、その後用いるときまで圧縮状態で維持される。
KafkaではGZIPとSnappy圧縮方式をサポートしている。

=====
1メッセージずつを圧縮するのではなくまとめて圧縮、
その上で送信前に圧縮し、圧縮してメッセージを保持、使う場所で解凍・・・
というある意味非常に分かりやすい効率のいい方式を選択しているようですね。
=====

10.メッセージ消費側駆動の状態管理方式

どのメッセージまでが消費されたかを管理する機能はメッセージングシステムの重要機能の1つ。
また、その状態管理方式は直感的ではないかもしれないが、メッセージングシステムの性能に大きく影響する。

状態管理は永続的な状態値(つまりはファイルに同期)を更新する必要があり、ランダムアクセスが発生する。
結果、ストレージシステムのスループットではなく、シーク時間に影響を受ける可能性がある。

ほとんどのメッセージングシステムにおいては、「状態管理」はBrokerプロセス(メッセージを提供する側)が管理する。
メッセージ提供側はConsumerに対してメッセージを送信した後、ローカルでも状態を更新する。
これは非常に分かりやすい方式だが、「実際にどこにメッセージが送信されたか」についてはわからない。

多くのメッセージングシステムはこの状態管理方式のためにあまりスケールしない。
だが、メッセージ提供側は処理が完了したメッセージをすぐ削除できるために保持するデータは小さく保つことができる関係上、
非常に実用的な選択であるのも確か。

実は、メッセージ提供側とメッセージ消費側のメッセージ消費状態の同意は重要な問題、というのは正しくないのかもしれない。(?)
もしメッセージ提供側がネットワークを通じてメッセージを送信するごとにローカルの状態を更新した場合、
メッセージ消費側がメッセージの処理に失敗した場合、メッセージは消失してしまう。

この問題を解決するために多くのメッセージングシステムにおいてはAck機能を搭載している。
これはメッセージ消費側から「このメッセージについては処理が完了した」と通知を受けることで、
最終的なメッセージの削除を行うもの。
それまでは送信したメッセージについてもフラグを設定するだけで削除は行わない。
=====
この辺、KestrelやRabbitMQがとっている方式ではありますね。
=====

この方式はメッセージが消失する問題については対処できるが、また新たな問題を生む。

第1の問題としてメッセージ消費側がメッセージを処理するが、Ackを送信する前に失敗した場合、2回メッセージは消費される。
第2の問題としてパフォーマンス周りの問題がある。
現在メッセージ提供側は1メッセージに対して複数の状態を保持してしまっている。
(未処理、送信済未完了=同じメッセージを違う消費者に送信しないために必要、完了)
また、「送信したが完了していない」が継続するという状態もメッセージ消費側の状態によっては発生するため、
それらの状態への対処も行う必要がある。

=====
メッセージ提供側が状態管理をした場合非常に分かりやすいが、その分問題も発生するよという話。
ただ、個々のメッセージ単位で処理を行う場合相変わらずこの方式が一番な気もしますが・・・はてさて。
=====