インタフェースの重複要求を防止する方法

2828 ワード

インタフェースの重複要求を防止する方法
に質問
最近、インタフェースの重複要求によるデータ異常のバグに遭遇しました.通知モジュールで、先生が保護者に通知を送信し、保護者が通知を開いた後、クライアントはackインタフェースを呼び出してサーバーに通知を読み取り、notify_readテーブルに保護者の情報を挿入し、内容は多分(notify_id,member_id)で、通知テーブルnotifyのread_numフィールド+1.ネットワークの問題が発生すると、クライアントはインタフェースを複数回要求します.
int count = queryRead(notifyId, memberId);
if( count == 0){
    insertNotify(notifyId, memberId);
    incrementNotifyReadNum(notify, memberId);
}

コンカレント要求が発生した場合、1つの要求がinsertNotifyに実行される場合、別の要求もinsertNotifyに実行されます.incrementNotifyReadNumメソッドが2回実行されると、データがエラーになります.
ソリューション
デルのサービスは分散型であるため、問題を解決する際に2つのリクエストが異なる2つのサーバに落ちている場合を考慮する必要があります.当時、この問題を解決する2つの案があると思っていた.
データベースの使用
新しい中間表を作成
CREATE TABLE request(rkey varchar(32)PRIMARY KEY (`rkey`))

各リクエストは、ビジネス処理を行う前にテーブルにレコードを挿入し、リクエストが完了するとレコードを削除します.
try{
    insertRequest(String.format("NOTIFY_%s_%s", notify, memberId))

    int count = queryRead(notifyId, memberId);
    if( count == 0){
        insertNotify(notifyId, memberId);
        incrementNotifyReadNum(notify, memberId);
    }
}catch(Exception e){
    //doSomething();
}finally{
 deleteRequest(String.format("NOTIFY_%s_%s", notify, memberId));
}


データベース内の一意のキーを使用して、同じ時点でrequestテーブルを正常に挿入できるリクエストが1つしかないことを保証すると、挿入に失敗したリクエストはビジネス処理されません.シナリオは簡単ですが、リクエスト量が大きいとDBの性能が損なわれ、拡張が容易ではありません.
Redisのcounterの使用
Redisを使用することとデータベースを使用することの実装プロセスは、実際にはそれほど悪くありません.要求のたびにkeyに対してsetnx操作を行い、setnx操作が1を返すと、現在の要求だけがこの業務を処理していることを示す.setnxオペレーションが0に等しいことを返すと、このビジネスを処理する他の要求があることを示します.
**SETNX key value **
キーの値をvalueに設定し、キーが存在しない場合にのみ使用します.与えられたkeyが既に存在する場合、SETNXは何もしない.SETNXは『SET if Not eXists』(存在しなければSET)の略である.時間複雑度:O(1)戻り値:設定に成功し、1を返します.設定に失敗し、0を返します.
String key = String.format("NOTIFY_%s_%s", notify, memberId);
try{
    int requestCount =  redisServer.setnx(key);
    
    if(requestCount == 1){
        int count = queryRead(notifyId, memberId);
        if( count == 0){
            insertNotify(notifyId, memberId);
            incrementNotifyReadNum(notify, memberId);
        }
    }
    
}catch(Exception e){
    //doSomething();
}finally{
    //    
    redisServer.delete(key);
}

Redisのsetnxコマンドは、インタフェースの重複要求を防止するために、分散ロックを実装するためによく使用されます.Redisの実装は、データベースの実装に比べてパフォーマンスが向上し、拡張が容易です.
転載先:https://juejin.im/post/5b1e336d51882513bb1c637a