インターフェイスの冪等テスト

この記事では、冪等性とは何か、冪等性の問題が発生した場合に冪等性を確保する方法について主に説明します。この記事が読者のお役に立てば幸いです。

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

いわゆるべき等性とは、メソッドまたはインターフェイスを複数回呼び出してもビジネス ステータスが変化せず、繰り返しの単一呼び出しの結果の一貫性が保証されることを意味します。

2. どのような状況でべき等性を考慮する必要がありますか?

私たちの開発では、主な操作は CRUD であり、読み取り操作と削除操作は当然冪等であり、主な関心は新規操作と更新操作です。

冪等性の使用シナリオ:

1. フロントエンドの繰り返し送信

たとえば、新しい製品機能を追加し、保存ボタンをクリックし、フロントエンドが保存を複数回連続してクリックすると、バックエンドは複数のリクエスト インターフェイスを受け取ります。冪等性が行われていない場合、複数のレコードが繰り返し作成されます。 、ダーティなデータが表示されます。

これは、フロントエンドでの繰り返しの送信を防ぐ方法の問題と呼ばれるものです。

2. ユーザーが悪意を持って注文をスワイプする

例: ユーザー投票機能では、ユーザーが (インターフェースを呼び出して) 繰り返しユーザーに投票を送信します。これにより、インターフェースがユーザーによって繰り返し送信された投票情報を受信する可能性があり、投票結果が仕様と大きく矛盾する可能性があります。事実。

3. インターフェースタイムアウトのリトライ

サードパーティのインターフェースを呼び出すと、ネットワークやその他の理由で呼び出しが失敗する可能性があるため、インターフェース呼び出しに失敗した再試行メカニズムを追加します (Spring は @Retryable アノテーションを介して再試行メカニズムを実装できます)。

再試行が発生する可能性があるため、インターフェイスへの呼び出しが繰り返し発生する可能性があります。このとき再度呼び出すときに冪等性が行われていないと、ダーティなデータが表示される可能性があります。

4. メッセージの繰り返しの消費 

MQ メッセージ ミドルウェアを使用する場合、プロダクション側とコンシューマ側の両方に再試行メカニズムがあり、同じメッセージが繰り返し消費される可能性があります。

テスト インターフェイスが冪等であるシナリオ:

1. ネットワークの変動により、リクエストが繰り返される可能性があります。

2. ユーザーは操作を繰り返し、操作中に意図せずに複数の注文を行う可能性があります。

3. 失敗またはタイムアウトの再試行メカニズムを使用します。

4. ページが繰り返し更新される 

5. ブラウザーの「戻る」ボタンを使用して前の操作を繰り返すと、フォームが繰り返し送信されます。

6. ブラウザ履歴を使用してフォームを繰り返し送信します。

7. ブラウザからの反復的な HTTP リクエスト。

8. スケジュールされたタスクの繰り返し実行。

3. インターフェースの冪等性を確保する方法

初歩的な方法:

1. 挿入する前にデータが存在するかどうかを確認します。

これは最も基本的なものであり、開発時に行う必要があります。挿入または更新する前に、現在のデータベースに既に存在するかどうかが判断され、存在する場合は繰り返し挿入できませんが、存在しない場合は挿入できます。

2. フロントエンドで対話型制御を行う

たとえば、ユーザーが保存ボタンをクリックした後、ボタンはグレー表示されるか読み込みプロセスになり、再度クリックしたり他のページにジャンプしたりすることはできなくなり、フロントエンドの大部分が繰り返し送信されなくなる可能性があります。 ;

高い同時実行性の下で冪等性を確保するにはどうすればよいでしょうか?

1. 悲観的ロックに基づく

2. 楽観的ロックに基づく

3.ステータスコードに基づく

4. 独自のインデックスに基づく

5. 分散ロックに基づく

6. トークンに基づく

実際の使用では、基本的な分散ロックがより一般的に使用されます。

以下に、各方法の原理を 1 つずつ紹介します。

1. 悲観的ロックに基づく

定義: データベース内のデータの一部を変更する場合、他の人が同時に変更されることを避けるために、最良の方法は同時実行を防ぐためにデータを直接ロックすることです。

同時実行性が高い場合、ビジネスは 2 回実行されますが、これは悲観的ロック、つまり SQL クエリ ステートメントに for update フィールドを追加することで実現できます。

ここで注意してください:

1) ロックされたデータ行の場合、特定のフィールドにインデックスを付ける必要があり、そうでない場合はテーブルがロックされます ( order_no など) にインデックスを追加します。

2) 悲観的ロックは、同じトランザクション操作中にデータ行をロックします。悲観的ロックはパフォーマンスが低いため、通常、この目的で悲観的ロックを使用することはお勧めできません。

2. 楽観的ロックに基づく

定義: 楽観的ロックは非常に楽観的です。データを取得するたびに、他の人がそのデータを変更しないと考え、ロックはしません。ただし、更新するときは、この期間中に他の人がデータを更新したかどうかを判断します。 . バージョン番号の仕組みを利用することができます。

いわゆる楽観的ロックとは、テーブルにバージョン (バージョン番号) フィールドを追加することです。

更新操作の冪等性はバージョン番号によって制御されます。ユーザーは変更するデータをクエリし、システムはデータをページに返し、データのバージョン番号を非表示フィールドに入力します。ユーザーはデータを変更し、「送信」をクリックします。 、バージョン番号は です。1 つのパスがバックエンドに送信され、バックエンドはバージョン番号を更新条件として使用します。

注: オプティミスティック ロックで保証できるのは、更新操作の冪等性です。更新自体が冪等操作またはインストール操作である場合、オプティミスティック ロックは使用できません。

3.ステータスコードに基づく

多くのビジネス テーブル (注文テーブルなど) にはステータスがあります。通常、注文には1 つの注文の作成2 つの注文の確認3 つの注文の支払い、  4 つの注文の完了5 つの注文のキャンセル、およびその他の注文プロセスがあります。注文の状況

最初のリクエストでは、 注文確認ステータスがorderpayment に正常に変更され 、SQL 実行結果の影響を受ける行数は 1 でした。

2 番目のリクエストでも、注文確認ステータスをorderpayment に 変更したいのです が、SQL 実行結果の影響を受ける行数は 0 です。0 の場合は、直接成功を返すことができます。インターフェースの冪等性を保証するために後続のビジネス操作を実行する必要はありません。

4. 独自のインデックスに基づく

一般に、悲観的ロック、楽観的ロック、およびステータス コードは冪等性を達成するために更新操作に使用され、一意のインデックスは冪等性を確保するために挿入操作に使用されます。

1) 注文を作成するとき、フロントエンドは最初にインターフェイスを通じて注文番号を取得し、バックエンドを要求するときにその注文番号を持ってきます. 注文テーブル内の注文番号には一意のインデックスが追加されます. 同じ注文番号の場合が挿入されると、エラーが直接報告されます。

2) MQ メッセージを消費するとき、messageIdそれらは一意です。新しい消費レコード テーブルを追加し、主キーとして messageId を使用できます。繰り返し消費される場合、同じ messageId が存在し、挿入時にエラーが直接報告されます。

5. 分散ロックに基づく

冪等性を実現するための分散ロックのロジックは、リクエストが受信されると、まず分散ロックの取得を試みます。成功した場合、ビジネス ロジックが実行されます。そうでない場合、取得に失敗した場合、リクエストは破棄され、リクエストは成功に直接戻されます。

実際、前に紹介した悲観的ロックは、冪等性を確保するために複数の操作を 1 つのアトミック操作にパッケージ化するデータベースの分散ロックを使用します。ただし、データベース分散ロックのパフォーマンスが低いため、

代わりに、redis または Zookeeper を使用して分散ロックを実装できます。

具体的な手順:

ユーザーがブラウザを通じてリクエストを開始すると、サーバーがデータを収集し、唯一のビジネス フィールドとして注文番号コードを生成します。

redis set コマンドを使用して、注文コードを redis に設定し、同時にタイムアウトを設定します。

設定が成功したかどうかを確認し、設定が成功した場合は、最初のリクエストであることを意味し、データ操作が実行されます。

設定が失敗した場合は、リクエストが繰り返されたことを意味し、成功が直接返されます。

6.トークンに基づく

トークン メカニズムの中心的な考え方は、操作ごとに一意の証明書、つまりトークンを生成することです。トークンには操作の各段階で 1 つの実行権限しかなく、実行が成功すると実行結果が保存されます。リクエストが繰り返された場合、同じ結果が返されます。トークンメカニズムの応用範囲は非常に広いです。

例: 各リクエストにはチケットが保持されます。このチケットは 1 回限りの使用です。一度使用されると破棄され、再利用することはできません。このトークンはチケットの概念に相当します。トークンは各インターフェース要求とともに持ち込まれます。サーバーは最初にトークンを処理するときにトークンを検証します。そして、このトークンは 1 回だけ使用できます。ユーザーが同じトークンを使用する場合リクエストが 2 回行われた場合、2 回目は処理されず、直接返されます。

トークン ソリューションの特徴は、ビジネス オペレーションを完了するために 2 つのリクエストが必要であることです。

通常、次の 2 つのリクエスト ステージが含まれます。

1) クライアントがリクエストする申请获取tokenと、サーバーはトークンを生成して返します。

2) 2 番目のリクエストでは带着这个token、サーバーはトークンを検証し、ビジネス操作を完了します。

具体的な手順:

ユーザーがページにアクセスすると、ブラウザは自動的にトークン要求を開始します。

サーバーはトークンを生成し、redis に保存して、ブラウザーに返します。

ユーザーがブラウザを通じてリクエストを開始すると、トークンが送信されます。

トークンが Redis に存在するかどうかをクエリします。トークンが存在しない場合は、それが最初のリクエストであることを意味し、後続のデータ操作が実行されます。

存在する場合、それは繰り返しリクエストであることを意味し、成功が直接返されます。

Redis では、トークンは有効期限が過ぎると自動的に削除されます。

メインのトークン スキームには特定のリスクがありますので、分析してみましょう。

1. 最初にトークンを削除する [最初にトークンを削除してからビジネスを実行する]、または後でトークンを削除する [最初にビジネスを実行してからトークンを削除する];

(a) 最初に削除すると、ビジネスが実行されず、前のトークンで再試行される可能性がありますが、再設計防止のため、リクエストは依然として実行できません。

(b) 後からトークンを削除すると大問題となり、業務は正常に処理されるが、サービスが中断され、タイムアウトが発生し、トークンが削除されずにリトライが繰り返され、業務が実行されなくなる可能性があります。 2回。

したがって、最初にトークンを削除し、ビジネス コールが失敗した場合はトークンを再取得して再度リクエストするように設計する方がよいでしょう。

2. ここで、トークンの取得、比較、削除は原子性を保証する必要があります

トークンが存在するかどうかを確認した後は、redis.get(token) を使用せず、redis.del(token) を使用してください。これはアトミックな操作ではないため、同時実行性が高い状況では冪等の問題が発生します。

直接使用する方法redis.del(token):

おすすめ

転載: blog.csdn.net/weixin_43500974/article/details/129316496