Redis 限流演算法

2021-10-02 05:11:28 字數 4432 閱讀 3423

判斷有限時間內的數量是否超過限制上線

<?php

date_default_timezone_set('prc');

class limitelse

}elseelse

}else

}

}}

有問題,併發上來,一直就允許5個,但是利用 pipeline 保證了各個client之間的原子性

function isactionallowed($userid, $action, $period, $maxcount)

for ($i=0; $i<20; $i++)

//返回當前的毫秒時間戳

function msectime()

漏桶(leaky bucket)演算法思路很簡單,水(請求)先進入到漏桶裡,漏桶以一定的速度出水(介面有響應速率),當水流入速度過大會直接溢位(訪問頻率超過介面響應速率),然後就拒絕請求,可以看出漏桶演算法能強行限制資料的傳輸速率.示意圖如下:

[外鏈轉存失敗,源站可能有防盜煉機制,建議將儲存下來直接上傳(img-ohooeaws-1579075258180)(redis非同步佇列&延時佇列.assets/640.webp)]

<?php

class funnel

public function makespace()

$this->freespace += $deltaquota; //增加剩餘空間

$this->lastleaking = time(); //記錄漏水時間

if($this->leftquota > $this->capacaty)

}public function watering($quota)

return false;

}}$funnels = ;

global $funnel;

function isactionallowed($userid, $action, $capacity, $waterrate)

return $funnel->watering(1);

}for ($i=0; $i<20; $i++)

核心邏輯就是makespace,在每次灌水前呼叫以觸發漏水,給漏斗騰出空間。funnels我們可以利用redis中的hash結構來儲存對應字段,灌水時將字段取出進行邏輯運算後再存入hash結構中即可完成一次行為頻度的檢測。但這有個問題就是整個過程的原子性無法保證,意味著要用鎖來控制,但如果加鎖失敗,就要重試或者放棄,這回導致效能下降和影響使用者體驗,同時**複雜度也公升高了,此時redis提供了乙個外掛程式,redis-cell出現了。

redis 4.0提供了乙個限流redis模組,名稱為redis-cell,該模組提供漏斗演算法,並提供原子的限流指令。

該模組只有一條指令cl.throttle,其引數和返回值比較複雜。

漏斗演算法

初始化漏斗數量,以一定速率往漏斗加水,一定速率誰流出

安裝

進入 redis-cli,執行命令

module load /path/to/libredis_cell.so;

cl.throttle test   100 400 60 3

▲ ▲ ▲ ▲ ▲

| | └──┴─────── 30 tokens / 60 seconds

| └───────────── 15 max_burst

└─────────────────── key "user123"

以上命令表示從乙個初始值為100的漏斗中流出速度為3,該漏斗的加水的速率限制為400次/60秒

返回值說明

127.0.0.1:6379> cl.throttle test 100 400 60 3

1) (integer) 0

2) (integer) 101

3) (integer) 98

4) (integer) -1

5) (integer) 0

是否成功,0 成功 1:拒絕

2) 漏斗的初始水量 +1

3)當前 漏斗中的剩餘水量

4)若請求被拒絕,這個值表示多久後漏斗中會有水量,單位可作為嘗試時間

5)表示多久漏斗中水量會滿

示例

127.0.0.1:6379> cl.throttle test1 10 5 60 3

1) (integer) 0

2) (integer) 11

3) (integer) 8

4) (integer) -1

5) (integer) 36

127.0.0.1:6379> cl.throttle test1 10 5 60 3

1) (integer) 0

2) (integer) 11

3) (integer) 5

4) (integer) -1

5) (integer) 71

127.0.0.1:6379> cl.throttle test1 10 5 60 3

1) (integer) 0

2) (integer) 11

3) (integer) 2

4) (integer) -1

5) (integer) 106

127.0.0.1:6379> cl.throttle test1 10 5 60 3

1) (integer) 1

2) (integer) 11

3) (integer) 2

4) (integer) 10

5) (integer) 106

令牌桶演算法(token bucket)和 leaky bucket 效果一樣但方向相反的演算法,更加容易理解.隨著時間流逝,系統會按恆定1/qps時間間隔(如果qps=100,則間隔是10ms)往桶裡加入token(想象和漏洞漏水相反,有個水龍頭在不斷的加水),如果桶已經滿了就不再加了.新請求來臨時,會各自拿走乙個token,如果沒有token可拿了就阻塞或者拒絕服務.

令牌桶的另外乙個好處是可以方便的改變速度. 一旦需要提高速率,則按需提高放入桶中的令牌的速率. 一般會定時(比如100毫秒)往桶中增加一定數量的令牌, 有些變種演算法則實時的計算應該增加的令牌的數量.

<?php

class trafficshaper

/*** 加入令牌

* @param int $num 加入的令牌數量

* @return int 加入的數量

*/public function add($num = 0)

return 0;

}/**

* 獲取令牌

* @return boolean

*/public function get()

/*** 重設令牌桶,填滿令牌

*/public function reset()

private function connect()

$redis->select($this->_config['index']);

} catch (\redi***ception $e)

return $redis;

}}$config = array(

'host' => 'localhost',

'port' => 6379,

'index' => 0,

'auth' => '',

'timeout' => 1,

'reserved' => null,

'retry_interval' => 100,

);// 令牌桶容器

$queue = 'mycontainer';

// 最大令牌數

$max = 5;

// 建立trafficshaper物件

$otrafficshaper = new trafficshaper($config, $queue, $max);

// 重設令牌桶,填滿令牌

$otrafficshaper->reset();

// 迴圈獲取令牌,令牌桶內只有5個令牌,因此最後3次獲取失敗

for ($i = 0; $i < 8; $i++)

// 加入10個令牌,最大令牌為5,因此只能加入5個

$add_num = $otrafficshaper->add(10);

var_dump($add_num);

// 迴圈獲取令牌,令牌桶內只有5個令牌,因此最後1次獲取失敗

for ($i = 0; $i < 6; $i++)

Redis 簡單限流

首先我們來看乙個常見 的簡單的限流策略。系統要限定使用者的某個行為在指定的時間裡只能允許發生 n 次,如何使用 redis 的資料結構來實現這個限流的功能?這個限流需求中存在乙個滑動時間視窗,想想 zset 資料結構的 score 值,是不是可以通過 score 來圈出這個時間視窗來。而且我們只需要...

redis簡單限流

需求 如果要保證乙個使用者一分鐘內只能訪問5次介面,超過就拒絕範圍。這個限流需求中存在乙個滑動時間視窗,想想 zset 資料結構的 score 值,是不是可以通過 score 來圈出這個時間視窗來。而且我們只需要保留這個時間視窗,視窗之外的資料都可以砍掉。那這個 zset 的 value 填什麼比較...

限流和限流演算法

目錄 一 什麼是限流 二 為什麼需要限流 三 那些場景需要用到限流 3.1 對外服務 3.2 對內服務 四 限流演算法 4.1 計數器演算法 4.2 漏桶演算法 4.3 令牌桶演算法 限流其實是指當系統資源不夠,不足以應對大量請求,即系統資源與訪問量出現矛盾的時候,我們為了保證有限的資源能夠正常服務...