カフカストリームのべき等性と1回限りの違い


8

有効にすることで正確に1回のトランザクションを達成できることを理解した文書を調べていました idempotence=true

べき等性:べき等プロデューサーは、単一のトピックに対してプロデューサーに対して一度だけ有効にします。基本的に、各単一メッセージ送信にはより厳しい保証があり、エラーが発生した場合に重複することはありません

それで、すでにべき等性がある場合、カフカストリームで正確に一度だけ別のプロパティが必要なのはなぜですか?べき等と1回だけの違い

正確に1回限りのプロパティが通常のカフカプロデューサーで利用できないのはなぜですか?


2
このブログ投稿は、詳細情報の優れた情報源でもあります。medium.com
Matthias J. Sax

回答:


6

分散環境では、障害はいつでも発生する非常に一般的なシナリオです。Kafka環境では、ブローカーがクラッシュしたり、ネットワーク障害、処理の失敗、メッセージの発行中の失敗、メッセージのコンシュームの失敗などが発生する可能性があります。これらの異なるシナリオにより、異なる種類のデータ損失と重複が発生しました。

障害シナリオ

A(Ack失敗):プロデューサーは再試行> 1でメッセージを正常に発行しましたが、失敗したため確認を受信できませんでした。その場合、プロデューサーは同じメッセージを再試行し、重複が発生する可能性があります。

ここに画像の説明を入力してください

B(プロデューサープロセスはバッチメッセージで失敗しました):プロデューサーが失敗したメッセージのバッチを送信し、公開された成功はほとんどありませんでした。その場合、プロデューサーが再起動すると、バッチからすべてのメッセージが再度再発行され、Kafkaで重複が発生します。 ここに画像の説明を入力してください

C(Fire&Forget Failed)プロデューサーは、retry = 0(fire and forget)でメッセージを公開しました。公開に失敗した場合、認識せずに次のメッセージを送信します。これによりメッセージが失われます。 ここに画像の説明を入力してください

D(コンシューマーはバッチメッセージで失敗しました)コンシューマーはKafkaからメッセージのバッチを受信し、オフセットを手動でコミットします(enable.auto.commit = false)。Kafkaにコミットする前にコンシューマーが失敗した場合、次にコンシューマーは同じレコードを再度消費し、コンシューマー側で複製を再現します。

ここに画像の説明を入力してください

1回限りのセマンティクス

この場合、プロデューサーがメッセージを再送信しようとしても、メッセージがパブリッシュされ、コンシューマーによって1回だけ消費されます。

KafkaでExactly-Onceセマンティックを実現するために、以下の3つのプロパティを使用します

  1. enable.idempotence = true(アドレスa、b、c)
  2. MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION = 5(プロデューサーは、接続ごとに常に1つの処理中の要求を持ちます)
  3. isolation.level = read_committed(アドレスd)

Idempotentを有効にする(enable.idempotence = true)

べき等配信により、プロデューサーは、単一のプロデューサーの存続期間中に、データ損失やパーティションごとの順序なしに、トピックの特定のパーティションにメッセージを1回だけKafkaに書き込むことができます。

「べき等を有効にするには、MAX_IN_FLIGHT_REQUESTS_PER_CONNECTIONが5以下である必要があることに注意してください。RETRIES_CONFIGは0より大きく、ACKS_CONFIGは「すべて」です。これらの値がユーザーによって明示的に設定されていない場合、適切な値が選択されます。互換性のない値の場合は、設定すると、ConfigExceptionがスローされます」

べき等性を実現するために、Kafkaはメッセージの生成中に製品IDまたはPIDと呼ばれる一意のIDとシーケンス番号を使用します。プロデューサーは、一意のPIDでマップされた、発行された各メッセージのシーケンス番号を増やし続けます。ブローカーは常に現在のシーケンス番号を以前のシーケンス番号と比較し、新しいシーケンス番号が以前のシーケンス番号より+1でない場合は拒否します。これにより、重複が回避され、メッセージの表示が失われた場合に同じ時間が繰り返されます。 ここに画像の説明を入力してください

失敗のシナリオでは、ブローカーはシーケンス番号を以前のものと比較し、シーケンスが増加していない場合、+ 1はメッセージを拒否します。 ここに画像の説明を入力してください

トランザクション(isolation.level)

トランザクションは、複数のトピックパーティションのデータをアトミックに更新する機能を提供します。トランザクションに含まれるすべてのレコードは正常に保存されるか、まったく保存されません。これにより、処理したデータとともに同じトランザクションでコンシューマーオフセットをコミットできるため、エンドツーエンドの正確に1回限りのセマンティクスが可能になります。

プロデューサーは、kafkaへのメッセージの書き込みを待機しません。プロデューサーは、beginTransaction、commitTransaction、およびabortTransactionを使用します(失敗した場合)。コンシューマーは、isolation.levelを使用します。read_committedまたはread_uncommitted

  • read_committed:コンシューマーは常にコミットされたデータのみを読み取ります。
  • read_uncommitted:トランザクションがコミットされるのを待たずに、オフセット順にすべてのメッセージを読み取ります

isolation.level = read_committedのコンシューマが完了していないトランザクションの制御メッセージに到達すると、プロデューサがトランザクションをコミットまたはアボートするか、トランザクションのタイムアウトが発生するまで、このパーティションからメッセージは配信されません。トランザクションのタイムアウトは、transaction.timeout.ms(デフォルトは1分)の設定を使用してプロデューサーが決定します。

プロデューサーとコンシューマーで1回だけ

生産者と消費者が分かれている通常の状態。プロデューサーは、べき等であると同時にトランザクションを管理する必要があるため、コンシューマーは、isolation.levelを使用してread_committedのみを読み取り、プロセス全体をアトミック操作として実行できます。これにより、プロデューサーが常にソースシステムと同期することが保証されます。プロデューサーのクラッシュやトランザクションの中止でも、常に一貫しており、メッセージまたはメッセージのバッチを1つの単位として1回公開します。

同じコンシューマは、メッセージまたはメッセージのバッチを1つの単位として1回受信します。

Exactly-Onceでは、ConsumerとともにSemantic Producerが1つのユニットとして動作するアトミック操作として表示されます。パブリッシュして一度に消費されるか、中止されます。

カフカストリームで1回のみ

Kafka StreamはトピックAからのメッセージを消費し、メッセージを処理してトピックBにパブリッシュします。パブリッシュしたら、commit(コミットはほとんどカバーなしで実行)を使用して、すべてのステートストアデータをディスクにフラッシュします。

Kafkaストリームで1回だけは、これらの操作がアトミック操作として扱われることを保証する読み取りプロセス書き込みパターンです。Kafkaストリームはプロデューサー、コンシューマー、トランザクションをすべて一緒に提供するので、Kafkaストリームは特別なパラメーターprocessing.guaranteeを提供します。これにより、すべてのパラメーターを個別に処理するのではなく、人生を簡単にします。

Kafka Streamsは、コンシューマーオフセット、ローカルステートストア、ステートストアの変更ログトピック、プロダクションをアトミックに更新し、トピックをまとめて出力します。これらのステップのいずれかが失敗すると、すべての変更がロールバックされます。

processing.guarantee:厳密に設定する必要のない以下のパラメーターをexact_onceが自動的に提供します

  1. isolation.level = read_committed
  2. enable.idempotence = true
  3. MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION = 5

12

Kafkaストリームは、エンドツーエンドの観点から1回限りのセマンティクスを提供します(あるトピックから消費し、そのメッセージを処理してから、別のトピックに生成します)。ただし、言及したのはプロデューサーのべき等属性のみです。それは全体像のほんの一部です。

質問を言い換えましょう:

生産者側で1回限りの配信セマンティクスがすでに保証されているのに、消費者側で1回限りの配信セマンティクスが必要なのはなぜですか?

回答:正確に1回の配信セマンティックは、生成ステップだけでなく、処理の完全なフローでもあるためです。正確に1回の配信を意味的に実現するために、生産と消費で満たさなければならないいくつかの条件があります。

これは一般的なシナリオです。プロセスAはトピックTへのメッセージを生成します。同時に、プロセスBはトピックTからのメッセージを消費しようとします。プロセスBが1つのメッセージを2回処理しないようにします。

プロデューサーの部分:プロデューサーがメッセージを2回生成しないようにする必要があります。Kafka Idempotent Producerを使用できます

消費者向けの部分: 消費者向けの基本的なワークフローは次のとおりです。

  • ステップ1:コンシューマーは、メッセージMをKafkaのトピックから正常にプルします。
  • ステップ2:コンシューマーがジョブを実行しようとすると、ジョブは正常に戻ります。
  • ステップ3:コンシューマーは、メッセージのオフセットをKafkaブローカーにコミットします。

上記の手順は、ただの幸せな道です。実際には多くの問題が発生します。

  • シナリオ1:ステップ2のジョブは正常に実行されますが、コンシューマーがクラッシュします。この予期しない状況以降、コンシューマはメッセージのオフセットをまだコミットしていません。コンシューマーが再起動すると、メッセージは2回消費されます。
  • シナリオ2:コンシューマーはステップ3でオフセットをコミットしますが、ハードウェア障害(CPU、メモリ違反など)が原因でクラッシュします。再起動すると、コンシューマーはオフセットを正常にコミットしたかどうかを知る方法がありません。

多くの問題が発生する可能性があるため、ジョブの実行とコミットオフセットは、コンシューマ側で1回だけの配信セマンティクスを保証するためにアトミックである必要があります。それができないという意味ではありませんが、正確に1回の配信セマンティクスを確実にするために多くの努力が必要です。Kafka Streamはエンジニアの仕事を支持しています。

注: Kafka Streamは「1回だけのストリーム処理」を提供します。トピックから消費し、カフカトピックの中間状態を具体化して1つにプロデュースすることを指します。アプリケーションが他の外部サービス(データベース、サービス...)に依存している場合、外部依存関係がそれらの場合に1回だけ保証できることを確認する必要があります。

TL、DR:フロー全体で1回限りでは、生産者と消費者の協力が必要です。

参照:


配信とは通常、メッセージの読み取り/送信の頻度を意味するので、私はこれを「配信」とは呼びません。正確に1回の配信(つまり、メッセージが実際にネットワーク経由で送信される頻度)はおそらく不可能です(en.wikipedia.org/wiki/Byzantine_faultおよびen.wikipedia.org/wiki/Two_Generals%27_Problemを参照)
Matthias J. Sax

はい。回答で述べたように、Kafka Streamが一般的に1回だけの配信を提供していないのは事実です。2つの一般的な問題については、分散システムで正確に1度一般的なことはできませんが、いくつかの条件を失うか、システムにいくつかの条件を追加すると、問題を解決できます。例:タイムアウト。ただし、これは別の話です。
hqt

まあ、私は単に「配信」という用語を使用するのではなく、セマンティクスを使用します。
Matthias J. Sax
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.