redis實(shí)際應(yīng)用-限流

為什么要做限流

首先讓我們先看一看系統(tǒng)架構(gòu)設(shè)計(jì)中苏遥,為什么要做“限流”奸例。

旅游景點(diǎn)通常都會(huì)有最大的接待量彬犯,不可能無限制的放游客進(jìn)入,比如故宮每天只賣八萬張票查吊,超過八萬的游客躏嚎,無法買票進(jìn)入,因?yàn)槿绻^八萬人菩貌,景點(diǎn)的工作人員可能就忙不過來卢佣,過于擁擠的景點(diǎn)也會(huì)影響游客的體驗(yàn)和心情,并且還會(huì)有安全隱患箭阶;只賣N張票虚茶,這就是一種限流的手段

軟件架構(gòu)中的服務(wù)限流也是類似仇参,也是當(dāng)系統(tǒng)資源不夠的時(shí)候嘹叫,已經(jīng)不足以應(yīng)對(duì)大量的請(qǐng)求,為了保證服務(wù)還能夠正常運(yùn)行诈乒,那么按照規(guī)則罩扇,系統(tǒng)會(huì)把多余的請(qǐng)求直接拒絕掉,以達(dá)到限流的效果怕磨;

不知道大家注意過沒有喂饥,比如雙11,剛過12點(diǎn)有些顧客的網(wǎng)頁或APP會(huì)顯示下單失敗的提示肠鲫,有些就是被限流掉了员帮。

常見的限流算法

計(jì)數(shù)法

顧名思義就是來一個(gè),記錄一個(gè)导饲,比如我1分鐘只能處理1000個(gè)請(qǐng)求捞高,那么我們就可以設(shè)置一個(gè)計(jì)數(shù)器氯材,來一個(gè)請(qǐng)求就incr+1,當(dāng)1分鐘之內(nèi)的數(shù)量大于等于1000之后不處理了即可硝岗,偽代碼如下

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$rate_limit = 1000;  //限制個(gè)數(shù)
$rate_seconds = 60;  //限制時(shí)間
$redis_key = "redis_limit";
$count = $redis->get($redis_key);
if ($count >= $rate_limit){  //判斷60秒內(nèi)請(qǐng)求個(gè)數(shù)是否已經(jīng)達(dá)到上限
    //直接返回氢哮,不處理請(qǐng)求
    return
}
$redis->incr($redis_key, 1);//請(qǐng)求計(jì)數(shù)
$redis->expire($redis, $rate_seconds); //設(shè)置過期時(shí)間 60s
//to do  業(yè)務(wù)邏輯處理.......

這種計(jì)數(shù)方式比較簡(jiǎn)單快捷,但是有很大的缺點(diǎn)型檀,因?yàn)檎?qǐng)求的訪問不一定是很平穩(wěn)的冗尤,如果0:59過來了1000個(gè)請(qǐng)求,1:01已經(jīng)是下一個(gè)窗口贱除,又過來了1000個(gè)請(qǐng)求生闲,但實(shí)際上三秒內(nèi)來了2000個(gè)請(qǐng)求,已經(jīng)超過我們的限流上限了月幌。所以這種方法是不推薦的碍讯。

滑動(dòng)窗口算法

還拿上面的例子,一分鐘分6份扯躺,每份10秒捉兴;每過10秒鐘,我們的時(shí)間窗口就會(huì)往右滑動(dòng)一格录语,每個(gè)格子都有獨(dú)立的計(jì)數(shù)器倍啥,我們每次都計(jì)算時(shí)間窗口內(nèi)的數(shù)量,可以解決計(jì)數(shù)器法中的問題澎埠,而且當(dāng)滑動(dòng)窗口的格子越多虽缕,那么限流的統(tǒng)計(jì)就會(huì)越精確。具體可以參考下圖蒲稳,看圖比較清晰


image

偽代碼實(shí)現(xiàn)如下

function api_limit($scene,  $period, $maxCount){
    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);
    $key = sprintf('hist:%s', $scene); //限流場(chǎng)景唯一標(biāo)識(shí)
    $now = msectime();   // 毫秒時(shí)間戳,這樣更精確
    $pipe=$redis->multi(Redis::PIPELINE); //使用管道提升性能
    $pipe->zadd($key, $now, $now); //value 和 score 都使用毫秒時(shí)間戳
    $pipe->zremrangebyscore($key, 0, $now - $period); //移除時(shí)間窗口之前的行為記錄氮趋,剩下的都是時(shí)間窗口內(nèi)的
    $pipe->zcard($key);  //獲取窗口內(nèi)的行為數(shù)量
    $pipe->expire($key, $period/1000 + 1);  //多加一秒過期時(shí)間
    $replies = $pipe->exec();
    return $replies[2] <= $maxCount;  //$replies[2]為zcard返回的個(gè)數(shù)  如果zcard結(jié)果大于maxCount早抠,則不處理結(jié)果
}

for ($i=0; $i<20; $i++){  //測(cè)試限流是否實(shí)現(xiàn)代碼
    var_dump(isActionAllowed("uniq_scene", 60*1000, 5)); //執(zhí)行可以發(fā)現(xiàn)只有前5次是通過的
}

//返回當(dāng)前的毫秒時(shí)間戳
function msectime() {
    list($msec, $sec) = explode(' ', microtime());
    $msectime = (float)sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);
    return $msectime;
 }

這段代碼還是略顯復(fù)雜枣氧,需要讀者花一定的時(shí)間好好啃。它的整體思路就是:每一個(gè)行為到來時(shí)袱巨,都維護(hù)一次時(shí)間窗口祥国。將時(shí)間窗口外的記錄全部清理掉昵观,只保留窗口內(nèi)的記錄。

因?yàn)檫@幾個(gè)連續(xù)的 Redis 操作都是針對(duì)同一個(gè) key 的舌稀,使用 pipeline 可以顯著提升Redis 存取效率啊犬。但這種方案也有缺點(diǎn),因?yàn)樗涗洉r(shí)間窗口內(nèi)所有的行為記錄扩借,如果這個(gè)量很大椒惨,比如限定 60s 內(nèi)操作不得超過 100w 次這樣的參數(shù),它是不適合做這樣的限流的潮罪,因?yàn)闀?huì)消耗大量的存儲(chǔ)空間康谆。

后面還有漏桶算法和令牌桶算法,由于各自的實(shí)現(xiàn)比較復(fù)雜嫉到,所以準(zhǔn)備各自新開一篇文章單獨(dú)描述

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末沃暗,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子何恶,更是在濱河造成了極大的恐慌孽锥,老刑警劉巖,帶你破解...
    沈念sama閱讀 223,002評(píng)論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件细层,死亡現(xiàn)場(chǎng)離奇詭異惜辑,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)疫赎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門盛撑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人捧搞,你說我怎么就攤上這事抵卫。” “怎么了胎撇?”我有些...
    開封第一講書人閱讀 169,787評(píng)論 0 365
  • 文/不壞的土叔 我叫張陵介粘,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我晚树,道長(zhǎng)姻采,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,237評(píng)論 1 300
  • 正文 為了忘掉前任爵憎,我火速辦了婚禮慨亲,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘纲堵。我一直安慰自己巡雨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,237評(píng)論 6 398
  • 文/花漫 我一把揭開白布席函。 她就那樣靜靜地躺著铐望,像睡著了一般。 火紅的嫁衣襯著肌膚如雪茂附。 梳的紋絲不亂的頭發(fā)上正蛙,一...
    開封第一講書人閱讀 52,821評(píng)論 1 314
  • 那天,我揣著相機(jī)與錄音营曼,去河邊找鬼乒验。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蒂阱,可吹牛的內(nèi)容都是我干的锻全。 我是一名探鬼主播狂塘,決...
    沈念sama閱讀 41,236評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼鳄厌!你這毒婦竟也來了荞胡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,196評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤了嚎,失蹤者是張志新(化名)和其女友劉穎泪漂,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體歪泳,經(jīng)...
    沈念sama閱讀 46,716評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡萝勤,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,794評(píng)論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了呐伞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片敌卓。...
    茶點(diǎn)故事閱讀 40,928評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖荸哟,靈堂內(nèi)的尸體忽然破棺而出假哎,到底是詐尸還是另有隱情,我是刑警寧澤鞍历,帶...
    沈念sama閱讀 36,583評(píng)論 5 351
  • 正文 年R本政府宣布舵抹,位于F島的核電站,受9級(jí)特大地震影響劣砍,放射性物質(zhì)發(fā)生泄漏惧蛹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,264評(píng)論 3 336
  • 文/蒙蒙 一刑枝、第九天 我趴在偏房一處隱蔽的房頂上張望香嗓。 院中可真熱鬧,春花似錦装畅、人聲如沸靠娱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽像云。三九已至,卻和暖如春蚂夕,著一層夾襖步出監(jiān)牢的瞬間迅诬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工婿牍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留侈贷,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,378評(píng)論 3 379
  • 正文 我出身青樓等脂,卻偏偏與公主長(zhǎng)得像俏蛮,于是被迫代替她去往敵國(guó)和親撑蚌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,937評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容