前回の記事では、Song Ge が Seata の 4 つの分散トランザクション処理ソリューションを紹介しました。これまでの記事を読んだ後、皆さんは Seata の分散トランザクションについて十分に理解できたと思います。前の記事を読んでいない方は、まずこちらをお読みください:
しかし、読んでみると、Seata の分散トランザクションの処理は、コードはシンプルであるものの、ネットワーク上で費やされる内部時間が長すぎると感じた友人が多くいます。同時実行性の高いシナリオでは、これは適切な解決策ではないようです。 どの分散トランザクション処理ソリューションが効率的かといえば、メッセージ ミドルウェアは不可欠です。メッセージ ミドルウェアに基づく 2 フェーズ コミット ソリューションは、通常、同時実行性の高いシナリオで使用されます。この方法では、パフォーマンスが大幅に向上する代わりに、強力なデータ一貫性が犠牲になります。ただし、この方法を実装するにはコストと複雑さが比較的高く、その使用は実際のビジネス状況に依存します。 本日は、Song Ge が簡単なケースを通じて、メッセージ ミドルウェアを介して分散トランザクションを処理する方法についてお話しします。 1. 思考分析まず全体的なアイデアについてお話ししましょう。 メッセージ駆動型マイクロサービスという用語がありますが、皆さんも聞いたことがあると思います。それをどう理解すればいいのでしょうか? マイクロサービス システムでは、サービスは OpenFeign などの HTTP または Dubbo などの RPC を使用して相互に呼び出すことができます。これらのソリューションに加えて、典型的なレスポンシブ システム設計ソリューションであるメッセージ駆動型も使用できます。 メッセージ駆動型マイクロサービスでは、サービスが互いに直接呼び出すことはなくなりました。サービスが通信する必要がある場合、通信コンテンツはメッセージ ミドルウェアに送信され、他のサービスはメッセージ ミドルウェア内のメッセージ キューをリッスンして対応するビジネス ロジック呼び出しを完了します。これは難しいプロセスではありません。どうやってそれをやるのかを引き続き見ていきましょう。 2. ビジネス分析長い間苦労した後、Song Ge さんはインターネット上で他の誰かが書いた例を見つけました。この問題を実証するには特に適していると思うので、自分でケースを書いたのではなく、他の人のコードをそのまま使用しました。前回の分散トランザクション Seata の説明と同じように、1 つずつ分析してみましょう。 まず、ユーザーがチケットを購入する場合の次のフローチャートを見てみましょう。 ユーザーがチケットを購入したい場合:
これは典型的なメッセージ駆動型マイクロサービスであり、典型的なレスポンシブ システムでもあります。このシステムには、次の 3 つのサービスがあります。
これら 3 つのサービス間で直接通話することはできません。すべてのメッセージはメッセージ ミドルウェアに直接送信され、他のサービスはメッセージ ミドルウェアから必要なメッセージを取得して処理します。 具体的には、私たちの実践では、以下に示すように、チケットが十分にあるかどうかを確認するための追加のプロセスがあります。 注文を作成する際、チケット サービスはまずチケットが十分にあるかどうかを確認します。はいの場合は、注文の作成が続行されます。他のプロセスについては話しません。 また、発券システムでは、各チケットに座席などがあるなど、各チケットが異なるため、チケットはデータベース内のレコードとして設計されることが多いことにも注意してください。 3. 練習プロセスをわかりやすく説明しましたので、具体的なコードの実践方法を見てみましょう。 3.1 データベースの準備まず、次の 3 つのデータベースを準備します。
各ライブラリにはそれぞれ対応するテーブルがあります。操作の便宜上、これらのテーブルを自分で作成する必要はありません。将来プロジェクトを開始するときに、JPA を使用してそれらを自動的に作成できます。 3.2 プロジェクトの概要プロジェクト全体を見てみましょう。完全なコードをダウンロードするには、WeChat パブリック アカウントで mq_tran に返信してください。 合計で 5 つのサービスがあります。
以下で一つずつ説明していきましょう。 3.3 登録センターすべてがメッセージ主導なので、なぜ登録センターが必要なのかと言う人もいます。 メッセージ駆動型が使用されるのは正しいです。マイクロサービスがメッセージ駆動型になると、各サービスはメッセージ ミドルウェアにメッセージをスローするだけでよく、各サービスはメッセージ ミドルウェアでメッセージを消費するだけでよくなります。現時点では、サービス登録センターに対するニーズはそれほど強くないようです。ただし、この場合、メッセージ ドライバーは主にトランザクションの問題を処理するために使用されます。他の一般的なニーズを処理するために OpenFeign を引き続き使用しているため、ここでも登録センターが必要です。 ここでは、手間を省くために、一般的な Eureka を登録センターとして選択します。この記事は分散トランザクションがメインなので、スペースをあまり取らずにマイクロサービスに関することも簡単に紹介します。 Spring Cloud の使い方に詳しくない方は、公式アカウントのバックグラウンドで vhr に返信して一連のビデオ紹介を受けることができます。 サービス レジストリを作成するときは、独自のサービス レジストリを保護するために Spring Security を追加することを忘れないでください。 ここで少しお話ししたい小さな詳細があります。 Eureka が Spring Security によって保護された後、他のサービス登録は Http Basic を介して認証されるため、次のようにコードで Http Basic 認証を有効にする必要があります (次のコードは古いバージョンでは必要ありませんが、新しいバージョンでは必要です)。
3.4 チケット購入サービス次にチケット購入サービスについて見てみましょう。 チケットの購入は注文から始まりますので、注文サービスからプロセス全体の分析を始めます。 3.4.1 新規注文処理(注文) ユーザーがチケット購入リクエストを開始すると、そのリクエストは注文サービスに送信されます。注文サービスは、まず order:new キューにメッセージを送信して、注文の処理を開始します。コードは次のとおりです。
上記で設定した UUID は、処理プロセス中の注文全体の一意の識別子であり、メインラインとみなすこともできます。 order:new キュー内のメッセージは、チケット サービスによって消費されます。チケット サービスは、メッセージを order:new で消費し、チケット ロック操作を実行します (チケットをロックする目的は、2 人の消費者が同時に同じチケットを購入することを防ぐことです)。チケットが正常にロックされると、チケット サービスは order:locked キューにメッセージを送信し、チケットが正常にロックされたことを示します。それ以外の場合は、チケットのロックが失敗したことを示すメッセージが order:fail キューに送信されます。 ここでの OrderDTO オブジェクトは、チケット購入プロセス全体を実行します。 3.4.2 チケットのロック チケットサービス内でチケットのロック操作が完了します。コードは次のとおりです。
まず lockTicket メソッドを呼び出して、データベース内のチケットをロックします。チケットのロックとは、購入するチケットの lock_user フィールドを customer_id (購入者の ID) に設定することを意味します。 チケットが正常にロックされた場合 (つまり、データベースが正常に変更された場合)、msg のステータスは TICKET_LOCKED に設定され、チケットが正常にロックされたことを示すメッセージが order:locked キューに送信されます。 チケットのロックが失敗した場合 (つまり、データベースの変更が失敗した場合)、msg のステータスは TICKET_LOCK_FAIL に設定され、チケットのロックが失敗したことを示すメッセージが order:fail キューに送信されます。 3.4.2 チケットロック成功(順序) 次に、注文サービスは order:locked キュー内のメッセージを消費します。これは、チケットが正常にロックされた後の次の操作です。
チケットが正常にロックされたら、まず注文 UUID に基づいて注文データベースを照会し、注文レコードがあるかどうかを確認します。そうであれば、メッセージが処理されたことを意味し、注文の重複処理を防ぐことができます (これは主にべき等性の問題を解決するためです)。 注文が処理されていない場合は、新しい注文オブジェクトを作成し、データベースに保存します。新しい注文オブジェクトを作成するときは、注文のステータスを NEW に設定します。 最後に、msg のステータスを NEW に設定し、order:pay キューにメッセージを送信して支払いプロセスを開始します。支払いはユーザーサービスによって提供されます。ユーザーサービスは、ユーザーのアカウント残高が十分かどうかを確認します。そうでない場合は、チケットの予約が失敗したことを示すメッセージが order:ticket_error キューに送信されます。残高が十分であれば、通常の支払い操作が実行され、支払いが成功すると、チケットの転送を開始するためのメッセージが order:ticket_move キューに送信されます。 3.4.3 支払い(ユーザー) チケットが正常にロックされた後、次のステップは支払いであり、支払いサービスはユーザーによって提供されます。
ここでの実行手順は次のとおりです。
3.4.4 チケット
チケット配信操作を完了するには、moveTicket メソッドを呼び出します。つまり、チケット テーブル内のチケットの所有者を customerId に設定します。 チケットが正常に配信されると、配信が完了したことを示すメッセージが order:finish キューに送信されます。 3.4.5 注文完了
ここでの処理は比較的簡単です。注文が完了したら、注文ステータスをFINISHに設定するだけです。 上記紹介が本文です。すべてがうまくいけば、この行に沿ってメッセージが送信され、注文が処理されます。 物事がスムーズに進まなければ、さまざまな予期せぬ問題が発生するでしょう。一つずつ見ていきましょう。 3.4.6 チケットロック失敗(注文) チケットのロックはチケットサービスで行われます。チケットのロックが失敗した場合、メッセージは order:fail キューに直接送信され、このキュー内のメッセージは注文サービスによって消費されます。 3.4.7 デビット失敗(チケット) ユーザ内で控除操作が完了します。控除が失敗した場合、メッセージは order:ticket_error キューに送信され、このキュー内のメッセージはチケット サービスによって消費されます。
控除が失敗した場合は、次の 3 つのことを行います。
3.4.8 注文の失敗 注文サービスでは、メッセージが order:fail キューに送信される状況が 3 つあります。
このメソッドの具体的な処理ロジックは次のとおりです。
3.4.9 注文タイムアウト(注文) 注文サービスには、次のように、処理に失敗した注文をデータベースから定期的に取得するスケジュールされたタスクもあります。
ご覧のとおり、ここではデータベースにアクセスして、ステータスが NEW で 1 分前に行われた注文を取得します。前回の分析によると、チケットが正常にロックされると、注文のステータスは NEW に設定され、データベースに保存されます。つまり、チケットがロックされてから 1 分以内にチケットが販売されない場合、注文タイムアウトが設定され、order:ticket_error キューにメッセージが送信されます。このメッセージはチケット サービスによって消費され、チケットの配信とロック操作が最終的に完了します。 これが一般的なコード処理フローです。 前の図をもう一度見てみましょう。 この図をコードと組み合わせると理解しやすいでしょうか? 3.5 テスト次に簡単なテストをしてみましょう。 次のように、チケット予約の失敗のテストから始めましょう。 ユーザーが持っているお金は 1,000 元だけで、チケットの価格は 10,000 元なので、チケットの購入は必ず失敗します。リクエストが正常に実行された後、注文テーブルをチェックして次のレコードを見つけます。 ご覧のとおり、注文が失敗した理由は口座残高不足です。この時点で、チケット テーブルとユーザー テーブルをチェックし、それらがそのままであることを確認します (必要に応じて、逆補正されています)。 次に、チケット テーブルの lock_user フィールドに次のように値を手動で設定します。 これはチケットがロックされていることを意味します。 次に、チケット購入リクエストを開始します (今回は金額を適切な範囲に設定できますが、今回は支払い段階に達していないため、設定しなくても問題ありません)。 リクエストが正常に送信された後、注文テーブルをチェックして次のレコードを見つけます。 この注文が失敗した理由は、チケットをロックできなかったためであることがわかります。この時点で、チケット テーブルとユーザー テーブルをチェックし、それらがそのままであることを確認します (必要に応じて、逆補正されています)。 最後に、もう一度成功するテストをしてみましょう。まず、チケット テーブルの lock_user フィールドをクリアし、次のリクエストを送信します。 今回はチケット購入に成功しました。チケット表を確認すると、請求書が発行されていることがわかります。 注文フォームを表示: チケット購入成功記録がもう1件残ります。 ユーザー テーブルを表示します。 ユーザーのアカウントから引き落とされました。 支払い記録テーブルを表示します。 すでに支払い記録があることがわかります。 4. 結論全体的に、上記のケースは技術的に難しいものではありません。複雑さは設計にあります。まず、良いニュースを処理するプロセスと、メッセージ処理が失敗した後に補償する方法を設計する必要があります。これは全員のスキルを試すテストです。 さらに、上記のケースでは、メッセージの送信と消費の両方で、RabbitMQ のトランザクション メカニズム (メッセージの消費が成功することを保証するため) と Spring のトランザクション メカニズム (メッセージの送信とデータの保存が同時に成功することを保証するため) が使用されます。これらについては詳しくは触れません。 つまり、メッセージ ミドルウェアを介して分散トランザクションを処理すると、パフォーマンスが大幅に向上する代わりに、強力なデータ一貫性が犠牲になります。ただし、この方法を実装するにはコストと複雑さが比較的高く、その使用は実際のビジネス状況に依存します。 この記事はWeChatの公式アカウント「江南一店語」から転載したものです。下のQRコードからフォローできます。この記事の転載については江南易店宇公式アカウントまでご連絡ください。 |
<<: VMware が Forrester によってゼロ トラスト ネットワーク アクセスのリーダーに選出
>>: クラウドネイティブテクノロジーが5Gモバイルネットワークに与える影響
10月23日はウェブマスターコミュニティにとって大きな打撃でした。百度は「ハイパーリンクアルゴリズム...
「今この瞬間、私はあなたと一緒にいます」は、過去には想像もできなかった消費シナリオでしたが、今では特...
この新聞の地図製作者、何江氏最近、一部のメディアは、オンラインP2P(別名Renrendai)プラッ...
locvps は新年に向けて特別オファーをご用意しました。年間支払いの小規模プランで 20% 割引 ...
周知のように、ウェブサイト最適化のプロセスは継続的な蓄積と段階的な進歩のプロセスです。百度のアルゴリ...
2018年最もホットなプロジェクト:テレマーケティングロボットがあなたの参加を待っていますこれは私の...
SEO 業界に参入したばかり、または参入準備中のウェブマスターの友人に宛てたメッセージです。最近、何...
raksmart は昨年 12.12 プロモーションを実施しました。米国データセンターの無制限トラフ...
2019年6月6日、工業情報化部は3大通信事業者に5G商用ライセンスを正式に発行し、中国が正式に5G...
最近、ウェブサイトの構築と最適化に忙しく、ブログの更新が遅くなってしまいました。この間、多くの企業サ...
自動車業界のウェブサイトのキーワード最適化ソリューションについてですが、自動車業界のキーワードランキ...
シャドー IT はコンプライアンスとセキュリティの問題を引き起こし、クラウド コンピューティングのコ...
「ビリビリのトップ100製品リスト?聞いたことないよ」多くのUpマスターは半ば冗談めかして「ビリビリ...
占い、性格分析、ゆるキャラ、生贄など伝統的なビジネスは、インターネット上でどのように生き残ることがで...
単語の削除、ランキングのロック、棚からの削除、より遅く厳しいレビュー、承認なしでの繰り返しの提出.....