クリーム色の背景、色付き表示、高コントラスト、大きな文字のほうが読みやすいですか?配色と文字を選ぶと、すべてのページに一度で反映されます。

無料レッスン

メッセージの再試行と冪等性

このレッスンのあとで: 再試行されても二重に処理しない、メッセージの受け取り方を設計できます。

55分のレッスンコマンド5件Kafka ラボ:ブラウザーでトピック、プロデューサー、コンシューマー、パーティション、オフセットを学ぶ

同じ請求が2回届いた

冪等性とは、同じ操作を何回行っても、結果が1回行ったときと同じになる性質です。分散システムでは、メッセージの送り直し(再試行)が日常的に起きるので、受け取る側にこの性質が欠かせません。

お客様が3000円を払ったのに、決済のメッセージが2回届いて、3000円が2回引き落とされた。これを防ぐ設計を学びます。

このレッスンのゴール

メモ

再試行されても二重に処理しない、メッセージの受け取り方を設計できます。

キーワード

ことばいみ
再試行返事が来ないとき、同じ操作をもう一度行うこと
少なくとも1回1回は必ず届くが、重複するかもしれない配送
最大1回重複はしないが、失われるかもしれない配送
冪等性何回行っても、結果が1回と同じになる性質
メッセージIDメッセージを見分けるための、決まった名前
コミットどこまで読んだかを、確かな形で記録すること

学生が入力するセルは、このプレビューでは空白です。

返事が来ないから、送り直す

送る側は、メッセージを送ったあと、受け取った合図を待ちます。合図が来ないとき、メッセージが届かなかったのか、合図だけが届かなかったのかは、分かりません。

分からないときの安全な選択は、再試行です。送り直せば、メッセージは失われません。ただし、本当は届いていたなら、受け取る側には同じメッセージが2件並びます。

1パーティションのトピックを作る

コマンド
curl -s -X POST -H "Content-Type: application/json" -d '{"name":"receipts","partitions":1,"replicationFactor":1,"configs":{}}' http://kafka-ui:8080/api/clusters/lab/topics -o /dev/null -w "%{http_code}\n"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

決済のメッセージr1001を送る

コマンド
curl -s -X POST -H "Content-Type: application/json" -d '{"partition":0,"key":"r1001","value":"paid3000","headers":{},"keySerde":"String","valueSerde":"String"}' http://kafka-ui:8080/api/clusters/lab/topics/receipts/messages -o /dev/null -w "%{http_code}\n"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

合図が来なかったつもりで、送り直す

コマンド
curl -s -X POST -H "Content-Type: application/json" -d '{"partition":0,"key":"r1001","value":"paid3000","headers":{},"keySerde":"String","valueSerde":"String"}' http://kafka-ui:8080/api/clusters/lab/topics/receipts/messages -o /dev/null -w "%{http_code}\n"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

ログに何件並んだか見る

コマンド
curl -s http://kafka-ui:8080/api/clusters/lab/topics/receipts | grep -o '"partition":[0-9]*\|"offsetMax":[0-9]*'

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

配送の約束は3つある

約束失われるか重複するか使いどころ
最大1回失われ得るしない温度の記録など、1件抜けてもよいもの
少なくとも1回失われないし得る決済など、抜けては困るもの
ちょうど1回失われないしない少なくとも1回と冪等な処理で作る

学生が入力するセルは、このプレビューでは空白です。

IDを覚えれば、重複は怖くない

重複を防ぐ鍵は、冪等性です。各メッセージにメッセージIDをつけ、処理したIDを覚えておきます。同じIDが来たら、飛ばします。

送る側は「少なくとも1回」届けて、受け取る側が重複を捨てる。この組み合わせで、結果は「ちょうど1回」と同じになります。

同じIDを飛ばして合計する

コード: python
# 同じIDのメッセージは、2回目から飛ばします。IDは、処理したものを覚えておく目印です。
messages = [["r1001", 3000], ["r1001", 3000], ["r1002", 1200]]
done = []
total = 0
for message in messages:
    if message[0] in done:
        print(message[0], "は処理済みなので、飛ばします")
    else:
        done.append(message[0])
        total = total + message[1]
print("合計:", total, "円")
# 2つ目の "r1001" を "r1003" に変えると、合計はいくらになるでしょうか。

ログを最初から読み直す

コマンド
curl -s -N "http://kafka-ui:8080/api/clusters/lab/topics/receipts/messages/v2?seekType=BEGINNING&partitions=0&limit=10" | grep -o '"partition":[0-9]*,"offset":[0-9]*\|"value":"[a-z0-9]*"'

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

オフセットの記録は、処理のあとにする

コンシューマーは、どこまで読んだかをコミット(確かな形で記録)します。処理のあとにコミットすれば、途中で止まっても、次はコミットの位置から読み直せます。

ただし、処理が終わってからコミットするまでの間に止まると、同じメッセージをもう一度読みます。これが「少なくとも1回」の重複の正体です。

よくある間違い

注意

「ちょうど1回」の配送は、ネットワークだけで実現できると考えがちです。送り直しがある限り、重複は起こります。受け取る側が重複を捨てて、はじめて、結果が1回になります。

このラボとソースについて: Kafka ラボ:ブラウザーでトピック、プロデューサー、コンシューマー、パーティション、オフセットを学ぶ

授業で使いますか?このモジュールを学生に割り当てると、教員用ガイド、自動採点つきの課題、学生ごとのラボを使えます。