|
1. メッセージ損失の最初の問題を解決: 注文システムからのプッシュ メッセージの損失。 MQベースの転送中にメッセージが失われる可能性のある箇所をいくつか特定したので、各段階でメッセージ損失の問題を段階的に解決する方法を検討する必要があります。最初に解決すべき問題は、注文システムがメッセージをMQにプッシュするプロセス中にメッセージが失われる可能性があることです。 前述の通り、注文システムがMQにメッセージをプッシュするプロセス中に、一般的なネットワーク障害によりメッセージが失われる場合があります。下図をご覧ください。 RocketMQには、トランザクションメッセージングという非常に強力な機能があります。このトランザクションメッセージングメカニズムにより、注文システムから送信されたメッセージがMQに確実に書き込まれ、途中で失われることがないことを保証できます。 今日は、RocketMQ のトランザクション メッセージ メカニズムの原理を体系的に分析します。 2. メッセージ キュー (MQ) に半分のメッセージを送信して、MQ が正しく機能しているかどうかをテストします。 まず、当社の注文システムに関して言えば、注文が正常に支払われたという通知を受け取ったと仮定すると、注文ステータスの更新など、独自の注文データベースで何らかの追加、削除、または変更操作を実行する必要が必然的に生じます。 注文システムとは、単に独自のデータベースでいくつかの CRUD 操作を実行し、メッセージをメッセージ キュー (MQ) に送信して、注文の支払い成功ステータスを監視している他のシステムが MQ からメッセージを取得してそれに応じて処理できるようにするだけのものだと考える人もいるかもしれません。 実際のところ、それはそれほど単純ではありません。 RocketMQのトランザクションメッセージングメカニズムでは、まず注文システムからMQにハーフメッセージを送信する必要があります。このハーフメッセージは、本質的には注文の支払いが成功したことを示すメッセージですが、ハーフステートにあると考えることができます。この時点では、レッドエンベロープシステムはこのハーフメッセージを認識できません。その後、ハーフメッセージが正常に書き込まれたことを示す応答通知の受信を待ちます。 次の画像を見てみましょう。 ここまで読んで、読者の中には混乱し始めている方もいるかもしれません。「なぜ意味もなく、半分のメッセージをメッセージキュー(MQ)に送信するのか?」と疑問に思う方もいるかもしれません。 慌てないでください。注文システムにローカルデータベース操作(例えば、すべての注文ステータスを「完了」に更新するなど)をすぐに実行させたと想像してみてください。その後、メッセージキュー(MQ)にメッセージを送信したところ、MQがダウンしていて、一連の例外がスローされました。 この時点で、メッセージ通知経由で紅包システムへ紅包を送信することができなくなります。ユーザーは注文代金を支払ったにもかかわらず、紅包を受け取っていないことに気づくことになります。 したがって、ここでの最初のステップは、注文システムに CRUD 操作を実行させることではなく、MQ に半分のメッセージを送信して成功応答を受信し、MQ との最初の接続と通信を確立することです。 これは基本的に、メッセージ キュー (MQ) がまだ実行中であることを確認していることを意味します。また、MQ は、失いたくない重要なメッセージを送信する必要がある可能性があることを認識しています。 3. メッセージの半分の書き込みに失敗した場合はどうなりますか? 最初のシナリオを分析してみましょう。注文システムがメッセージ キュー (MQ) にメッセージの半分を書き込むのに失敗した場合はどうなるでしょうか。 エラーメッセージが表示された、MQがダウンしている、またはネットワーク障害が発生している可能性があり、その結果、メッセージの半分が送信に失敗する可能性があります。つまり、現在MQと通信できない状態です。 この時点で、注文システムは一連のロールバック処理を実行する必要があります。例えば、注文ステータスを「取引完了」に更新し、同時に決済システムに返金処理を自動で実行するよう通知するなどです。これが正しいアプローチです。 注文代金は支払済みですが、紅包やクーポンの送付といった後続業務は実行できません。そのため、ユーザーに返金し、取引が失敗した旨を伝える必要があります。 私自身に起こった出来事について少しお話します。以前、コンビニで買い物をしていた時、QRコード決済はすでに完了していたのですが、店員さんは「システム確認待ちです」と言っていました。 しばらく待つと、バックエンドシステムにエラーが発生し、取引が失敗したというメッセージが表示されました。その後しばらくして、Alipayから自動的に返金されました。 これは実際に同様の例です。 4. 半分のメッセージが正常に受信されると、注文システムはタスクを完了します。 次に、2つ目のシナリオを考えてみましょう。メッセージの半分が正常に書き込まれたとします。その場合、どうすればよいでしょうか? この時点で、注文システムはローカルデータベースに対して追加、削除、および変更操作を実行する必要があります。これは、メッセージの半分が正常に書き込まれたということは、MQが確実にメッセージを受信したこと、MQがまだ有効であること、そしてMQと正常に通信できることを意味するためです。 下の図は、次のステップでは注文システムが独自の追加、削除、および変更操作を実行することを示しています。 5. 注文システムのローカルトランザクションが失敗した場合はどうなりますか? 次に、次のシナリオを見てみましょう。注文システムが独自のデータベースの更新に失敗した場合はどうなるでしょうか? 例えば、注文システムのデータベースでネットワークエラーやクラッシュが発生している可能性があります。つまり、注文を「完了」ステータスに更新できない可能性があります。 この時点では、実際には非常に簡単です。注文システムがロールバック要求をメッセージ キュー (MQ) に送信するだけです。 つまり、私が先ほど送信したメッセージの半分を削除していただいても構いません。私自身も問題を抱えており、あなたとのやり取りをこれ以上続けることができないからです。 説明のために示した次の図を見てみましょう。 もちろん、その半分のメッセージを削除するために MQ にロールバック要求を送信した後、注文システムは後続のロールバック プロセスを実行して支払いシステムに払い戻しを発行するように通知する必要があります。 もちろん、注文システム自体の高可用性低下メカニズムを考慮する必要がある場合もあります。例えば、データベースを更新できない場合、注文の決済失敗の記録をマシンのローカルディスクファイルに書き込む必要があるかもしれません。 MySQLデータベースの復元後、バックグラウンドスレッドを開始して注文ステータスを「closed」に更新することもできます。ただし、これは今回のコラムの対象外です。 6. 注文システムがローカルトランザクションを完了すると、次に何が起こりますか? 注文システムが、注文ステータスを「完了」に更新するなどのローカル トランザクション操作を正常に完了した場合は、コミット要求をメッセージ キュー (MQ) に送信して、支払いシステムが注文支払い成功メッセージを確認できるように、MQ に前半のメッセージをコミットするように要求できます。 次の画像を見てみましょう。 前述したように、いわゆる「半分メッセージ」は、実際には注文の支払いが成功したことを示すメッセージであり、そのステータスのみが「半分」になっています。 つまり、彼が半状態にある場合、レッドエンベロープシステムは彼を認識できず、このメッセージを取得できません。レッドエンベロープシステムは、注文システムがコミット要求を実行し、メッセージがコミットされた後にのみ、このメッセージを確認し、更なる処理のために取得することができます。 7. プロセスをより厳密にする: 半分のメッセージが正常に送信されたが、応答が受信されない場合はどうなるでしょうか? トランザクション メッセージの一般的なフローについて説明しましたが、より厳密な分析に進みましょう。 半分のメッセージをメッセージ キュー (MQ) に送信し、MQ がそれを保存したが、MQ からの応答を受信しなかった場合、この状況では何が起こるでしょうか? この時点で応答がない場合、ネットワークタイムアウトエラーやその他の異常なエラーが報告される可能性があります。この場合、注文システムはMQへの半分のメッセージの送信に失敗したと誤って判断し、返金処理を直接実行し、注文ステータスを「closed」とマークします。 下の図を見てみましょう。 しかし、この時点でメッセージキュー(MQ)にはすでに半分のメッセージが保存されています。では、このメッセージはどのように処理すればよいのでしょうか? RocketMQには実は補正プロセスがあり、中間状態にあるメッセージをスキャンします。このメッセージに対して一定時間コミット/ロールバック操作が実行されない場合、RocketMQは注文システムからインターフェースをコールバックし、このメッセージに何が起こったかを問い合わせます。 このメッセージをコミットまたはロールバックする予定ですか? 以下の図を参照してください。 この時点で、注文システムはデータベースをチェックして注文の現在のステータスを確認する必要があります。注文ステータスが「closed」であることがわかった場合、メッセージキュー(MQ)にロールバック要求を送信して、前の半分のメッセージを削除する必要があることがわかります。 下の画像を見てみましょう。 8. ロールバックまたはコミットが失敗した場合はどうなりますか? 別のシナリオを考えてみましょう。注文システムが正常な半分のメッセージ書き込み応答を受信し、独自のデータベースの更新を試み、成功または失敗に基づいてロールバックまたはコミット要求を実行し、それをメッセージ キュー (MQ) に送信します。 ネットワーク障害によりロールバックまたはコミット要求の送信に失敗した場合はどうなりますか? この時点では、実は非常にシンプルです。MQ内のメッセージは常に中間状態にあるため、一定のタイムアウトが経過すると、この中間メッセージに問題があることが検出され、注文システムのインターフェースにコールバックされます。 この時点で、注文ステータスが「完了」に更新されているかどうかを確認する必要があります。完了している場合はコミットリクエストを再度実行し、完了していない場合はロールバックリクエストを再度実行する必要があります。 本質的に、このMQのコールバックは補償メカニズムです。これは、半分のメッセージのレスポンスが受信されなかったり、ロールバックまたはコミット要求が正常に送信されなかったりした場合に備え、その後の半分のメッセージの処理方法を問い合わせるものです。 9. スクリプトを一時停止し、上記のプロセスの重要性について考えます。 ここで少し立ち止まって、上で説明したプロセスの重要性について考えてみましょう。 実はとても簡単です。メッセージキュー(MQ)またはネットワークに問題がある場合、半分のメッセージは送信できません。この場合、半分のメッセージは確実に失敗し、注文システムは後続の処理を実行できません。 半分のメッセージを送信したにもかかわらず応答が受信されず、返金処理が開始された場合、メッセージキュー(MQ)には補償メカニズムが備わっており、コミットするかロールバックするかを尋ねるコールバックが行われます。この場合、ロールバックを選択してメッセージを削除すれば、後続の処理は実行されません。 注文システムが半分のメッセージを受信してデータベースの更新に失敗した場合、ロールバックされ、後続のプロセスは実行されません。 注文システムが半分のメッセージを受信し、自身のデータベースを正常に更新し、注文ステータスが「完了」に設定された場合、必然的にメッセージキュー(MQ)にコミット要求が送信されます。メッセージがコミットされると、赤い封筒システムはそれを確実に受信します。 さらに、コミット要求の送信に失敗した場合でも、メッセージ キュー (MQ) には、API をコールバックしてコミット要求を再送信するかどうかを決定できる補正メカニズムがあります。 つまり、注文システムが成功したら、MQ 内のメッセージがコミットされ、レッド エンベロープ システムがそれを確認できるようにする必要があります。 したがって、上記のプロセスを図と併せて検討することができます。このトランザクションメッセージメカニズムにより、注文システムがデータベース操作を正常に実行した場合、確実に紅包システムに紅包を配布するよう通知することが保証されます。少なくとも、注文システムとメッセージキュー(MQ)間の転送においてメッセージ損失の問題は発生しません。 |