Redis限流の実現方式は3種類ある

2886 ワード

Redis限流の実現方式は3種類あり、それぞれ:1、Redisに基づくsetnxの操作で、指定したkeyに期限切れの実践を設定した.2、Redisのデータ構造zsetに基づいて、要求をzset配列にする.3.Redisに基づくトークンバケツアルゴリズムは、出力速度が入力速度より大きく、ストリームを制限する.
1つ目:Redisベースsetnxの操作
Redisの分散ロックを使用する場合、setnxの命令に依存していることはよく知られていますが、CAS(Compare and swap)の操作時に、指定したkeyに期限切れの実践(expire)を設定し、ストリーム制限の主な目的は単位時間内にN数の要求があって私のコードプログラムにアクセスできることです.だからsetnxに頼って簡単にこの方面の機能をやり遂げることができます.
例えば、10秒以内に20個のリクエストを限定する必要がある場合、setnxの場合、要求されたsetnxの数が20に達した場合にストリーム制限効果を達成する期限10を設定することができます.コードが簡単なので展示しません.
もちろん、このようなやり方の弊害は多い.例えば、1~10秒を統計すると、2~11秒以内に統計できない.N秒以内のM個の要求を統計する必要がある場合、私たちのRedisではN個のkeyを維持する必要があるなどの問題がある.
 
2つ目:Redisベースのデータ構造zset
実は限流が関与する最も主要なのはスライドウィンドウで、上述した1-10がどのように2-11になるかについても言及した.つまり,開始値と終端値がそれぞれ+1でよい.
Redisのlistデータ構造でこの機能を簡単に実現できれば
リクエストをzset配列にすることができます.リクエストが入るたびにvalueは一意に維持され、UUIDで生成できます.scoreは現在のタイムスタンプで表すことができます.scoreは現在のタイムスタンプ内にどのくらいのリクエスト数があるかを計算するために使用できます.zsetデータ構造はrangeメソッドを提供し、2つのタイムスタンプ内のリクエストの数を簡単に取得できます.
コードは次のとおりです.
public Response limitFlow(){
 Long currentTime = new Date().getTime();
 System.out.println(currentTime);
 if(redisTemplate.hasKey("limit")) {
 Integer count = redisTemplate.opsForZSet().rangeByScore("limit", currentTime -  intervalTime, currentTime).size();        // intervalTime       
 System.out.println(count);
 if (count != null && count > 5) {
 return Response.ok("         5 ");
 }
 }
 redisTemplate.opsForZSet().add("limit",UUID.randomUUID().toString(),currentTime);
 return Response.ok("    ");
 }

上記のコードによりウィンドウをスライドさせる効果が得られ、N秒ごとにM個までの要求が保証され、zsetのデータ構造がますます大きくなるという欠点がある.実現方式も比較的簡単です.
3つ目:Redisベースのトークンバケツアルゴリズム
ストリーム制限といえばトークンバケツアルゴリズムに言及せざるを得ない.トークンバケツアルゴリズムはバケツアルゴリズムとも呼ばれ、具体的には度娘の解釈トークンバケツアルゴリズムを参照することができる.
トークンバケツアルゴリズムは、入力レートと出力レートに言及し、出力レートが入力レートより大きい場合、流量制限を超える.
すなわち,リクエストにアクセスするたびにRedisからトークンを取得することができ,トークンを取得すると制限を超えていないことを示し,取得できないと結果が逆になる.
上記の考え方により,RedisのListデータ構造と組み合わせてこのようなコードを容易に行うことができる.
ListのleftPopによるトークン取得
//     
public Response limitFlow2(Long id){
 Object result = redisTemplate.opsForList().leftPop("limit_list");
 if(result == null){
 return Response.ok("         ");
 }
 return Response.ok(articleDescription2);
 }

さらにJavaのタイミングタスクに頼って、タイミングでListにrightPushトークンを入れ、もちろんトークンにも一意性が必要なので、ここではUUIDで生成しました
// 10S          UUID,       
 @Scheduled(fixedDelay = 10_000,initialDelay = 0)
 public void setIntervalTimeTask(){
 redisTemplate.opsForList().rightPush("limit_list",UUID.randomUUID().toString());
 } 

以上のコードをAOPまたはfilterに追加して、インタフェースのストリーム制限を行い、最終的にあなたのサイトを保護することができます.
Redisには他にも多くの用途があります.彼の役割はキャッシュ、分散ロックの役割だけではありません.彼のデータ構造もString,Hash,List,Set,Zsetだけではない.興味のある人は後で彼のGeoHashアルゴリズムを理解することができます.BitMap,HLLおよびブロンフィルタデータ(Redis 4.0の後に追加し,Dockerで直接redislabs/rebloomをインストールできる)構造とした.