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

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

ビジネスシナリオ#

ある会社に融資案件があります。具体的な事業内容はアリババの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が「蜀への難路」の解決を加速

推薦する

パーソナライズされたコンテンツ推奨エンジンが中小規模のウェブサイトへのトラフィックを増加

元のタイトル: パーソナライズされたコンテンツ推奨エンジン Outbrain が、中小規模のウェブサ...

医療ウェブサイトの最適化のためのキーワードの選び方

以前、医療ウェブサイトを最適化する際に注意すべき10のポイントについて説明しました。今日は、医療ウェ...

#BlackFriday# spinservers: サンノゼ/ダラス VPS、永久 50% オフ、月額 7 ドル、2G メモリ/2 コア/20gSSD/1T トラフィック

spinservers のアメリカの独立サーバーの価格は市場ではユニークで、多くの業者に勝っています...

淘宝網の交通入口開設の混乱の背景にある問題

タオバオは初めて、オープンプラットフォームへの道のりで岐路に立たされている。タオバオ自体とエコシステ...

分散選択分散ロック - 複数方向の比較

導入なぜこの記事を書くのですか?現在、インターネット上の Zookeeper と Redis に基づ...

kube-downscaler を使用して Kubernetes クラスターのコストを削減する

導入Kube-downscaler は、Kubernetes でポッド リソースが自動的にスケールダ...

DouMi創業者趙世勇:製品とサービスの革新を利用して、サービス産業の採用と管理のアップグレードと進化を実現する

2018年最もホットなプロジェクト:テレマーケティングロボットがあなたの参加を待っています5月25日...

品質重視の時代にSEO最適化は何をすべきか?

Baidu などの検索エンジン アルゴリズムの頻繁な更新を背景に、インターネット コンテンツの品質向...

ホームページデザインでは、訪問者のニーズを正確に把握し、その心理を解釈する必要がある

運用や純粋な SEO を行っている人にとって、自分のサイトで何ができるのか、訪問者にどのようなサービ...

エッジコンピューティング「CROSS」欧州の新たな戦場

2017年11月7日、インダストリー4.0の中心地であるドイツ・ベルリンで、ヨーロッパのメーカー、研...

2020年テンセントグローバルデジタルエコシステムカンファレンスが9月に開催予定:クラウドへの移行は初めて、デジタル経済の新たなトレンドを解釈

8月20日、テンセントから、2020年テンセントグローバルデジタルエコシステムカンファレンスが9月9...

百度珠光:1つのウェブサイト+1つの電話検索マーケティングの敷居は低い

記者は百度のマーケティング・広報担当シニアディレクターの朱光氏にインタビューした。今年3月、Goog...

SEO最適化はアルゴリズムの更新に盲目的に従うべきではない

SEO 最適化を行う際にウェブマスターが最も期待するのは、ウェブサイトのランキングが向上するように検...

WeChat以外にソーシャル分野で戦争はありません!

ソーシャルの世界では、WeChatは2位だと言っていますが、誰も立ち上がって1位だと言う勇気がないの...

クラウド コンピューティングの簡単な歴史 (完全版)

クラウドコンピューティングに関連する技術分野、技術用語、技術製品は目を見張るほどあります。クラウド ...