redis限流的實(shí)際應(yīng)用
為什么要做限流
首先讓我們先看一看系統(tǒng)架構(gòu)設(shè)計(jì)中,為什么要做“限流”。
旅游景點(diǎn)通常都會(huì)有最大的接待量,不可能無(wú)限制的放游客進(jìn)入,比如故宮每天只賣八萬(wàn)張票,超過(guò)八萬(wàn)的游客,無(wú)法買票進(jìn)入,因?yàn)槿绻^(guò)八萬(wàn)人,景點(diǎn)的工作人員可能就忙不過(guò)來(lái),過(guò)于擁擠的景點(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á)到限流的效果
」;
不知道大家注意過(guò)沒(méi)有,比如雙11,剛過(guò)12點(diǎn)有些顧客的網(wǎng)頁(yè)或APP會(huì)顯示下單失敗的提示,有些就是被限流掉了。
常見(jiàn)的限流算法
計(jì)數(shù)法
顧名思義就是來(lái)一個(gè),記錄一個(gè),比如我1分鐘只能處理1000個(gè)請(qǐng)求,那么我們就可以設(shè)置一個(gè)計(jì)數(shù)器,來(lái)一個(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è)置過(guò)期時(shí)間 60s //to do 業(yè)務(wù)邏輯處理.......
這種計(jì)數(shù)方式比較簡(jiǎn)單快捷,但是有很大的缺點(diǎn),因?yàn)檎?qǐng)求的訪問(wèn)不一定是很平穩(wěn)的,如果0:59過(guò)來(lái)了1000個(gè)請(qǐng)求,1:01已經(jīng)是下一個(gè)窗口,又過(guò)來(lái)了1000個(gè)請(qǐng)求,但實(shí)際上三秒內(nèi)來(lái)了2000個(gè)請(qǐng)求,已經(jīng)超過(guò)我們的限流上限了。所以這種方法是不推薦的。
滑動(dòng)窗口算法
還拿上面的例子,一分鐘分6份,每份10秒;每過(guò)10秒鐘,我們的時(shí)間窗口就會(huì)往右滑動(dòng)一格,每個(gè)格子都有獨(dú)立的計(jì)數(shù)器,我們每次都計(jì)算時(shí)間窗口內(nèi)的數(shù)量,可以解決計(jì)數(shù)器法中的問(wèn)題,而且當(dāng)滑動(dòng)窗口的格子越多,那么限流的統(tǒng)計(jì)就會(huì)越精確。具體可以參考下圖,看圖比較清晰
偽代碼實(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); //多加一秒過(guò)期時(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次是通過(guò)的 } //返回當(dāng)前的毫秒時(shí)間戳 function msectime() { list($msec, $sec) = explode(' ', microtime()); $msectime = (float)sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000); return $msectime; }
這段代碼還是略顯復(fù)雜,需要讀者花一定的時(shí)間好好啃。它的整體思路就是:每一個(gè)行為到來(lái)時(shí),都維護(hù)一次時(shí)間窗口。將時(shí)間窗口外的記錄全部清理掉,只保留窗口內(nèi)的記錄。
因?yàn)檫@幾個(gè)連續(xù)的 Redis 操作都是針對(duì)同一個(gè) key 的,使用 pipeline 可以顯著提升Redis 存取效率?!?code>但這種方案也有缺點(diǎn),因?yàn)樗涗洉r(shí)間窗口內(nèi)所有的行為記錄,如果這個(gè)量很大,比如限定 60s 內(nèi)操作不得超過(guò) 100w 次這樣的參數(shù),它是不適合做這樣的限流的,因?yàn)闀?huì)消耗大量的存儲(chǔ)空間」。
后面還有漏桶算法和令牌桶算法,由于各自的實(shí)現(xiàn)比較復(fù)雜,所以準(zhǔn)備各自新開一篇文章單獨(dú)描述
到此這篇關(guān)于redis限流的實(shí)際應(yīng)用的文章就介紹到這了,更多相關(guān)redis限流內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redis報(bào)錯(cuò):Could not create server TCP 
這篇文章主要介紹了Redis報(bào)錯(cuò):Could not create server TCP listening socket 127.0.0.1:6379: bind:解決方法,是安裝與啟動(dòng)Redis過(guò)程中比較常見(jiàn)的問(wèn)題,需要的朋友可以參考下2023-06-06Redis實(shí)現(xiàn)短信驗(yàn)證碼登錄的示例代碼
本文主要介紹了基于Redis如何實(shí)現(xiàn)短信驗(yàn)證碼登錄功能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06Redis實(shí)現(xiàn)唯一計(jì)數(shù)的3種方法分享
這篇文章主要介紹了Redis實(shí)現(xiàn)唯一計(jì)數(shù)的3種方法分享,本文講解了基于SET、基于 bit、基于 HyperLogLog三種方法,需要的朋友可以參考下2015-03-03詳解Redis高效恢復(fù)策略內(nèi)存快照與AOF
這篇文章主要為大家介紹了Redis高效恢復(fù)策略內(nèi)存快照與AOF及對(duì)比詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12Redis使用元素刪除的布隆過(guò)濾器來(lái)解決緩存穿透問(wèn)題
本文主要介紹了Redis使用元素刪除的布隆過(guò)濾器來(lái)解決緩存穿透問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08SpringBoot集成redis錯(cuò)誤問(wèn)題及解決方法
這篇文章主要介紹了SpringBoot集成redis錯(cuò)誤問(wèn)題,本文給大家分享完美解決方法,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02Redis做數(shù)據(jù)持久化的解決方案及底層原理
Redis有兩種方式來(lái)實(shí)現(xiàn)數(shù)據(jù)的持久化,分別是RDB(Redis Database)和AOF(Append Only File),今天通過(guò)本文給大家聊一聊Redis做數(shù)據(jù)持久化的解決方案及底層原理,感興趣的朋友一起看看吧2021-07-07Redis?存儲(chǔ)對(duì)象信息用?Hash?和String的區(qū)別
這篇文章主要介紹了Redis存儲(chǔ)對(duì)象信息用Hash和String的區(qū)別,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09