分散システムにおけるインターフェースの冪等性

分散システムにおけるインターフェースの冪等性

ビジネスシナリオ#

ある会社に融資案件があります。具体的な事業内容はアリババのAnt Borrowingに類似している。ユーザーはプラットフォーム上でお金を借りて、有効期限を設定します。この期間内に、ユーザーはローンを返済し、一定の手数料を支払う必要があります。指定された期間内にローンを返済しない場合は、遅延料金が発生します。

[[375620]]

ユーザーがローンを開始すると、ローン注文が生成されます。ユーザーは、Alipay を通じて、または満期時にシステムにバインドされた銀行カードから金額を自動的に差し引くことによって、ローンを返済することができます。返済手続きは決済システムを経由するため、ユーザーの返済が延滞しているかどうか、延滞日数、延滞料金などはすべてシステムによって計算されます。

しかし、注文システムの開発中に、ビジネス上の理由から、ユーザーがオフラインの Alipay を通じて支払いを行えるというビジネス シナリオに遭遇しました。つまり、当社は同社の公式Alipay QRコードを提供し、ユーザーはそのコードをスキャンして返済することになります。その後、財務部門は定期的に Alipay アカウントの返済リストを取得し、標準化された Excel テーブルを生成して支払いシステムに入力します。

支払いシステムは、これらの支払い情報に基づいて対応する支払い注文を生成し、データベースに保存します。同時に、返済記録ごとにメッセージを生成し、メッセージ システムに送信します。メッセージの消費者は注文システムです。メッセージを受け取った後、注文システムは現在のユーザーの金額を決済します。まず元金を返済し、次に延滞料金を返済します。元金が全額返済されると、注文が確定し、融資額が増額されるなど、全体のプロセスはおおよそ次のようになります。

上記のプロセスの説明から、これは元のオンライン支払いをオフラインに転送することと同等であり、支払い決済がタイムリーでないという問題が発生することがわかります。たとえば、ユーザーの注文が今日 19-05-27 に期限切れになるが、ユーザーが 19-05-26 に注文の支払いを済ませた場合、財務部門は Alipay からリストを取得し、19-05-27 またはそれ以降に支払いシステムに入力します。この結果、ユーザーは実際にはローンを全額返済していないことになりますが、当社の記録では、ユーザーがローンを返済しておらず、延滞料金が発生していることが示されています。

もちろん、上記はビジネスに関する問題です。本日お話しするのは、決済システムが注文システムにメッセージを送信するプロセスにおける問題です。周知のとおり、メッセージの損失や注文システム処理の例外、ネットワークの問題を回避するには、メッセージ システムを設計する際に、メッセージの永続性とメッセージ失敗の再試行メカニズムを考慮する必要があります。

再試行メカニズムの場合、注文システムがメッセージを消費しても、ネットワークの問題により、メッセージ システムはメッセージが正常に処理されたかどうかに関するフィードバックを受信しません。このとき、メッセージ システムは設定されたルールに従って一定期間ごとに 1 回再試行します。システムの正常な処理を確実にするために、一度再試行するのが適切です。しかし、この時点でネットワークが正常に戻れば、最初に受信したメッセージは正常に処理され、その後、別のメッセージが受信されます。保護対策を講じないと、ユーザーは一度支払いますが、注文システムではそれを 2 回計算し、財務請求が一致しないという状況が発生します。すると、上司が泣いているのにユーザーが笑っているという可能性が出てきます。

インターフェースの冪等性

上記のような事態を防ぐためには、保護対策を講じる必要があります。同じ支払い情報に対して、一度正常に処理した場合、再度メッセージを受信して​​も、今回は処理しません。つまり、インターフェースのべき等性を確保するためです。

Wikipediaからの定義:

  • べき等性は数学とコンピュータ サイエンスの概念であり、抽象代数でよく見られます。

プログラミングにおいて、べき等な操作とは、何度実行しても、その効果が 1 回実行した場合と同じになる操作のことです。べき等関数またはべき等メソッドは、同じパラメータで繰り返し実行して同じ結果を生成することができる関数です。これらの関数はシステムの状態に影響を与えないため、繰り返し実行してもシステムに変更が生じる心配はありません。たとえば、「setTrue()」関数はべき等関数です。何度実行しても結果は同じです。より複雑な操作は、一意のトランザクション番号 (シリアル番号) を使用することで、べき等性が保証されます。

実行回数に関係なく、その影響は 1 回の実行の場合と同じであり、これがべき等性の中心的な特性です。実際、私たちのプログラミングにおける主な操作は CURD であり、その中で読み取り (Retrieve) 操作と削除 (Delete) 操作は自然にべき等であり、影響を受ける操作は作成 (Create) と更新 (Update) のみです。

ビジネスにおいてべき等性を考慮する必要がある領域は、通常、インターフェースの繰り返し要求です。繰り返しリクエストとは、何らかの理由で同じリクエストが複数回送信されることを指します。この状況につながる可能性のあるシナリオはいくつかあります。

  • フロントエンドでの重複送信: 注文を送信するときに、ユーザーがすばやく複数回クリックを繰り返すと、バックエンドで重複したコンテンツを含む複数の注文が生成されます。
  • インターフェースのタイムアウト再試行: サードパーティによって呼び出されるインターフェースの場合、ネットワーク ジッターなどの理由による要求の損失を防ぐために、通常、そのようなインターフェースはタイムアウトして複数回再試行するように設計されています。
  • メッセージの重複消費: MQ メッセージ ミドルウェア、メッセージの重複消費。

比較的大きな影響を与える一部のビジネス シナリオでは、金銭取引のインターフェイスなど、インターフェイスのべき等性は考慮しなければならない問題です。そうしないと、誤った、または不注意なインターフェースが会社に多大な経済的損失をもたらす可能性があり、プログラマー自身が間違いなく非難されることになります。

冪等性の実装方法

Web 側と対話するインターフェースの場合、フォームの繰り返し送信を防止したり、ボタンをグレー表示したり、非表示にしたり、クリックできないようにしたりするなど、フロントエンドでその一部をインターセプトできます。

しかし、フロントエンド制御の実際のメリットはそれほど高くありません。ある程度の技術的知識があれば誰でもサービスを呼び出すリクエストをシミュレートできるため、セキュリティ戦略はバックエンドのインターフェース層から実装する必要があります。

では、バックエンドで分散インターフェースの冪等性を実装するにはどのような戦略があるのでしょうか?実装は、次の側面から検討できます。

トークンの仕組み#

ユーザーがショッピングの注文を送信するときなど、フロントエンドで繰り返し連続してクリックする場合、注文送信インターフェースはトークン メカニズムを通じて繰り返しの送信を防ぐことができます。

主なプロセスは次のとおりです。

  1. サーバーはトークンを送信するためのインターフェースを提供します。ビジネスを分析する場合、どのビジネスにべき等性の問題があるかを調べるために、ビジネスを実行する前にトークンを取得する必要があります。サーバーはトークンを Redis に保存します。 (マイクロサービスは確実に分散されており、単一のマシンの場合は、JVM キャッシュが適しています)。
  2. その後、ビジネス インターフェイス要求を呼び出すと、通常は要求ヘッダーでトークンが引き継がれます。
  3. サーバーは、トークンが Redis に存在するかどうかを判断します。存在する場合、それは最初のリクエストであることを意味します。このとき、Redis 内のトークンは削除され、ビジネスは継続されます。
  4. トークンが Redis に存在しないと判断された場合、繰り返し操作であることを意味するため、重複マークが直接クライアントに返され、ビジネス コードが繰り返し実行されないことが保証されます。

データベース重複排除テーブル#

重複排除テーブルにデータを挿入するときは、データベースの一意のインデックス機能を使用して、一意のロジックを確保します。一意のシリアル番号は、注文の注文番号などの単一のフィールド、または複数のフィールドの一意の組み合わせにすることができます。たとえば、次のデータベース テーブルを設計します。

  1. 作成する テーブル`t_idempotent` (
  2. `id` int (11)はない  NULLコメント'ID'
  3. `serial_no` varchar (255)なし  NULL COMMENT '固有のシリアル番号'
  4. `source_type` varchar (255)ではない  NULL COMMENT 'リソースタイプ'
  5. `status` int (4)デフォルト  NULLコメント「ステータス」
  6. `remark` varchar (255) NOT   NULL COMMENT 'コメント'
  7. `create_by` bigint (20)デフォルト  NULLコメント'作成者'
  8. `create_time` 日時デフォルト  NULLコメント「作成時間」
  9. `modify_by` bigint (20)デフォルト  NULLコメント「修飾子」
  10. `modify_time` 日時デフォルト  NULLコメント「変更時刻」
  11. 主要な キー(`id`)
  12. 個性的  KEY `key_s` (`serial_no`,`source_type`, `remark`) COMMENT 'ビジネスの一意性を確保する'  
  13. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT= 'べき等性チェックテーブル' ;

次の主要なフィールドを見てみましょう。

  1. @べき等キー

データには serial_no、source_type、remark の 3 つのフィールドで構成される一意のインデックスがあるため、これを使用して重複を削除し、インターフェイスの冪等性を実現できます。具体的なコード設計は次のとおりです。

  1. パブリッククラス PaymentOrderReq {
  2. /**
  3. * Alipayシリアル番号
  4. */
  5. @べき等キー(順序=1)
  6. プライベート文字列 alipayNo;
  7. /**
  8. * 支払い注文ID
  9. */
  10. @べき等キー(順序=2)
  11. プライベート文字列paymentOrderNo;
  12. /**
  13. * お支払い金額
  14. */
  15. プライベートな長い金額。
  16. }

Alipay のシリアル番号と注文番号はシステム内で一意であるため、MD5 と組み合わせることで一意のシリアル番号を生成できます。具体的な生成方法は以下の通りです。

  1. プライベート void getIdempotentKeys(Object keySource, Idempotent idempotent) {
  2. TreeMap< Integer , Object> keyMap = new TreeMap< Integer , Object>();
  3. (フィールドフィールド: keySource.getClass().getDeclaredFields()) {
  4. if (field.isAnnotationPresent(IdempotentKey.class)) {
  5. 試す {
  6. フィールドをアクセス可能に設定( true );
  7. keyMap.put(field.getAnnotation(IdempotentKey.class) .order (),
  8. フィールドを取得します(keySource)。
  9. } キャッチ (IllegalArgumentException | IllegalAccessException e) {
  10. logger.error( "" 、e);
  11. 戻る;
  12. }
  13. }
  14. }
  15. generateIdempotentKey(べき等, keyMap.values ().toArray());
  16. }

べき等キーを生成します。キーが複数ある場合は、区切り文字「|」で接続できます。

  1. プライベート void generateIdempotentKey(Idempotent idempotent, Object... keyObj) {
  2. (keyObj.length == 0)の場合{
  3. logger.info( "idempotentkey が空です、{}" 、 keyObj);
  4. 戻る;
  5. }
  6. StringBuilder シリアル番号 = 新しい StringBuilder();
  7. for (オブジェクトキー: keyObj) {
  8. serialNo.append(キー.toString()).append( "|" );
  9. }
  10. idempotent.setRemark(serialNo.toString());
  11. idempotent.setSerialNo(md5(serialNo));
  12. }

すべての準備が整ったら、外部に冪等性検証インターフェース メソッドを提供できます。インターフェースメソッドは次のとおりです。

  1. パブリック<T> void idempotentCheck(IdempotentTypeEnum idempotentType, T keyObj) は IdempotentException をスローします {
  2. べき等 idempotent = new Idempotent();
  3. getIdempotentKeys(keyObj, idempotent );
  4. if (StringUtils.isBlank(idempotent.getSerialNo())) {
  5. 新しい ServiceException をスローします ( "idempotentkey の取得に失敗しました" );
  6. }
  7. idempotentEvent.setSourceType( idempotentType.name ());
  8. 試す {
  9. idempotentMapper.saveIdempotent(idempotent);
  10. } キャッチ (DuplicateKeyException e) {
  11. logger.error( "べき等性チェックに失敗しました" , e);
  12. 新しい IdempotentException(idempotent) をスローします。
  13. }
  14. }

もちろん、プロジェクト内でこのインターフェースのメソッドを適切に使用できるかどうかは、プロジェクトの要件によって異なります。 @Autowire アノテーションを通じて必要な場所に挿入できますが、どこでも呼び出す必要があるという欠点があります。私の個人的な推奨は、アノテーションをカスタマイズし、冪等性の保証が必要なインターフェースにアノテーションを追加し、インターセプター メソッドを通じてそれをインターセプトして使用することです。これは単純なので、コードの侵入や汚染は発生しません。

さらに、重複テーブルを防ぐためにデータベースを使用する方法には、システムの耐障害性が高くないという重大な欠点があります。べき等テーブルが配置されているデータベース接続が異常であったり、配置されているサーバーが異常であったりすると、システム全体のべき等性チェックに問題が生じます。この状況を防ぐためにデータベースのバックアップを実行する場合は、追加の作業が必要になります。

Redis 実装

上記では重複防止テーブルの設計方法と疑似コードを紹介しましたが、その明らかな欠点の 1 つについても触れました。そこで、Redis の別の実装を紹介します。

Redis の実装方法は、一意のシリアル番号をキーとして使用することです。一意のシリアル番号を生成する方法は、上で紹介した重複防止テーブルと同じです。値には、入力したい任意の情報を指定できます。一意のシリアル番号は、注文の注文番号などのフィールド、または複数のフィールドの一意の組み合わせにすることもできます。もちろん、ここでキーの有効期限を設定する必要があります。そうしないと、Redis にキーが多すぎてしまいます。具体的な検証プロセスを以下の図に示します。実装コードも非常に単純なので、ここでは書きません。

企業がプロジェクトで Redis の使用を検討する場合、そのほとんどがキャッシュとして使用するため、通常はクラスター化された形式で表示され、少なくとも 2 つの Redis サーバーが確実に展開されます。したがって、インターフェースの冪等性を実装するには、Redis を使用するのが最適です。

ステートマシン

多くのビジネスでは、ビジネス フロー ステータスがあり、各状態には、前の状態、後の状態、および最終的な終了状態があります。たとえば、プロセスは、承認待ち、承認中、拒否、再開、承認、または拒否のいずれかの状態になる場合があります。注文は保留中、支払済み、またはキャンセルされています。

注文を例にとると、支払済み状態の前の状態は支払い保留のみとなり、キャンセル状態の前の状態は支払い保留のみとなります。このステートマシンのフローを通じて、リクエストのべき等性を制御できます。

  1. パブリック列挙型 OrderStatusEnum {
  2. UN_SUBMIT(0, 0, "提出予定" ) ,
  3. UN_PADING(0, 1, "支払い保留中" ) ,
  4. PAYED(1, 2, "支払い済み、配送待ち" ) 、
  5. 配送中(2, 3, "配送済み" ) 、
  6. COMPLETE(3, 4, "完了" ) 、
  7. CANCEL(0, 5, "キャンセル" ) ,
  8. ;
  9. // 事前状態
  10. プライベートint preStatus;
  11. //ステータス値
  12. プライベートintステータス;
  13. //ステータスの説明
  14. プライベート文字列desc ;
  15. OrderStatusEnum( int preStatus, int status, String desc ) {
  16. this.preStatus = preStatus;
  17. this.status = ステータス;
  18. this.desc = desc ;
  19. }
  20. //...
  21. }

現在のステータスが支払済みであると仮定すると、この時点で支払いインターフェースが別の支払い要求を受信すると、例外がスローされるか、処理が拒否されます。

要約する

上記の理解から、さまざまなビジネス シナリオに応じて、べき等性の実装方法を柔軟に選択する必要があることがわかります。

たとえば、フロントエンドでの繰り返しの送信や繰り返しの注文などのシナリオを防ぐことは、トークン メカニズムを通じて実現できます。一方、ステートフルな変換前および変換後のシナリオでは、ステート マシンを通じて冪等性を実現できます。繰り返し消費やインターフェースの再試行のシナリオでは、一意のデータベース インデックスを使用する方が合理的です。

<<:  クラウドファースト戦略: 何がそんなに話題になっているのでしょうか?

>>:  中国南西部初のインテリジェントコネクテッドカー・道路連携テストサイトの構築に成功、ファーウェイクラウドIoTが「蜀への難路」の解決を加速

推薦する

vps.us: 月額 10 ドル、1G メモリ/1 コア/20g SSD/5T トラフィック/1G 帯域幅、12 のオプション データ センター (シンガポール/UAE/米国など)

2011 年に設立された vps.us は、実際には米国の VPS のみを販売しているわけではありま...

VMware が 3 年連続で Gartner Magic Quadrant の WAN エッジ インフラストラクチャのリーダーに選出

VMware (NYSE: VMW) は最近、WAN エッジ インフラストラクチャの Gartner...

青雲の真の魂:効率的、便利、信頼性、包括的

[51CTO.com からのオリジナル記事] サーバーのダウンタイムは、開発者や運用・保守担当者にと...

ウェブサイトのタイトルが見つかりませんか?接続が切れるのは検索エンジンの遅れによるものでしょうか?

多くの人がウェブサイトを最適化するとき、サイト内最適化とサイト外最適化の両方を怠ることはありません。...

Googleは.googleと.youtubeのトップレベルドメインを申請する予定

北京時間6月1日、海外メディアの報道によると、グーグル(微博)は木曜、「.google」や「.you...

あなたのクラウド ネットワークは生成 AI に対応していますか?

生成型人工知能 (AI) は、企業に数兆ドルの価値をもたらし、私たちの働き方を根本的に変える可能性を...

クラウド インフラストラクチャの複雑さを軽減する 3 つのステップ

多くの企業にとって、クラウド リソースの活用は戦略の一部ではなく、個々のチームがニーズを満たすために...

ターゲットを絞ることとプロフェッショナリズムは、将来のウェブサイトの競争力において強力な武器となるでしょう。

現代社会は分断社会に突入しており、例えば人口1万人の小さな町には小さな商店があれば十分ですが、人口1...

SEO トレーニング市場はあなたを救うために何ができるでしょうか?

いつからかは分かりませんが、SEOという3文字が中国のネットユーザーの視野に徐々に入り始めました。い...

友好的なリンクを交換する原則: 少ないほど良い、質がより重要、量の方が慎重

フレンドリーリンクは、ウェブサイトの宣伝と運営に不可欠な要素の 1 つです。誰もが独自の理解を持って...

テンセントクラウド小威「AIアシスタント」は複数のアプリケーションを備え、業界のアップグレードのための新しいAIドライバーです

9月9日から11日まで、「未来経済、デジタルファースト」をテーマにしたテンセントグローバルデジタルエ...

ローカルウェブサイト運営の初期段階と成長の軌跡について語る

最近、地元のウェブサイトを運営している多くの友人から、次のような質問を受けました。「私たちのウェブサ...

コンテナ内の JVM リソースを安全に制限するにはどうすればよいですか?

[[254653]]序文Java と Docker の組み合わせにより、アプリケーションのパッケー...

詳細が成功と失敗を決める:ウェブサイトのランキング要因の詳細に関する調査

ウェブサイトのランキングについてはさまざまな意見があります。ウェブサイトが Baidu または Go...

百度ウェブサイトの外部リンクを判断することに関する李氏の意見

4月25日、百度ウェブマスタープラットフォームの李氏は「外部リンクの判定について」と題した発表を発表...