redis之頻率限制

最近有個(gè)分享,關(guān)于頻率限制的redis實(shí)現(xiàn)。

主要是使用redis的命令:incr/decr

incr/derc的作用就是對(duì)某個(gè)key進(jìn)行+1或者-1。適用的場景之一:頻率限制

簡單的實(shí)現(xiàn)

在以往的頻率限制方案里面,如果不是太講究的話,可以直接使用簡單粗暴地方法——當(dāng)訪問一次之后,在redis生成一個(gè)key(帶有過期時(shí)間),再次訪問的時(shí)候,就查一下這個(gè)key是否存在,用來判斷該次訪問是否被放行。這個(gè)做法有個(gè)不好的地方,時(shí)間頻率都固定下來了,說好1秒1次,就不會(huì)讓你1秒2次。
所以,如果想要靈活一點(diǎn)的話,我們可以嘗試使用以下兩個(gè)算法。

令牌桶、漏桶算法

這里主要講令牌桶
我是令牌桶

原理: 每一個(gè)訪客都擁有一個(gè)獨(dú)立的“令牌桶”,在這個(gè)“令牌桶”里放了一些“令牌”,訪客每次來訪都會(huì)消耗“令牌桶”中的“令牌”,如果“令牌桶”空了,將會(huì)對(duì)訪客做特殊處理(如拒絕其繼續(xù)訪問以達(dá)到限流的目的,或者拉黑一段時(shí)間再放行)。

比如,60秒內(nèi)限制A用戶最多只能訪問a接口100次

這里有兩個(gè)問題

  • 訪客終有一天會(huì)花光所有的令牌,所以我們需要不斷地補(bǔ)給。按照一定的頻率往桶里放一些令牌。至于這個(gè)頻率是多少,和預(yù)期保持一致即可(每過60/100秒鐘往桶里加一個(gè))
  1. 如果某個(gè)用戶訪問了一次之后,再也沒有來過,怎么辦?令牌會(huì)一直增加一直增加啊。所以我們可以設(shè)置最大的令牌數(shù)(100).

  2. 怎么定時(shí)加令牌?如果直接簡單粗暴地加個(gè)定時(shí)器,不斷往桶里加令牌。那不實(shí)際,恐怕沒過多久,項(xiàng)目就崩了 。這里有個(gè)比較巧妙的地方——我不需要定時(shí)器,只要記下你當(dāng)前訪問的時(shí)間,然后和下次來的時(shí)間作一個(gè)比對(duì),就可以知道應(yīng)該往桶里增加多少了。比如,上次訪問的時(shí)候有34個(gè)令牌,6秒之后再來訪問,可以往桶里增加 (100/60)* 6 = 10 個(gè),所以這個(gè)時(shí)候桶里就有44個(gè)啦。

  3. 如果一個(gè)用戶訪問一次以后,從此以后不再訪問,怎么辦?redis會(huì)一直保存著他的信息啊。做法之一,可以在初始化的時(shí)候,給他一個(gè)過期時(shí)間。

總結(jié)上面的種種問題。我們需要確定幾個(gè)值:時(shí)間、次數(shù)、令牌桶的初始值。如60秒、100次、50個(gè)令牌(半桶)。

redis的實(shí)現(xiàn)

很幸運(yùn)的是,redis天生就可以為我們做這些事情?!? inrc/derc是原子操作, 這里用到的是derc, 當(dāng)然,也可以反過來使用inrc。區(qū)別不大
實(shí)現(xiàn)起來難度不大,代碼就不寫了。附上鏈接

不過,這個(gè)算法還是會(huì)存在一定的缺陷。假如,每分鐘允許某個(gè)用戶訪問100次,按照以上的做法,可以會(huì)出現(xiàn)以下情況。

  • A用戶訪問a接口,在第1秒直接就擼完了100次,剩下的59秒應(yīng)該怎么辦?因?yàn)槭敲棵腌娫黾?00/60個(gè)令牌,所以,1分鐘過去,可能這個(gè)用戶會(huì)訪問了200次了。
  1. 還是A用戶。前59秒(第1秒)之?dāng)]了1次(為什么是1次?),在接近60秒的時(shí)候,突然爆發(fā)訪問99次。這個(gè)時(shí)候就用光了所有令牌,而且有效期剛過完,下一個(gè)循環(huán)又來了,再第61秒狂擼100次。至此,在短短的時(shí)間里,訪問了接近200次,甚至,在這兩分鐘里訪問了300次。顯然,這并不符合我們的要求。

上面兩個(gè)問題,如果可以忍受,問題不大,實(shí)在不能忍,可以稍微改一下。關(guān)于有效期,我們可以效仿session的機(jī)制。給用戶一個(gè)過期時(shí)間(1分鐘),每次來訪問的時(shí)候,都重置這個(gè)有效期。一分鐘過后,再無訪問的話,就直接過期了,等待用戶下次光臨。而且,可以讓初始令牌為最大個(gè)數(shù)的一半,即50。這兩個(gè)做法,可以解決什么問題呢?
問題1,一分鐘最多只能150次
問題2,短時(shí)間內(nèi)只能一次性訪問100次,降低應(yīng)用被拖垮的風(fēng)險(xiǎn)。

如果還是不能忍的話,可以再想辦法進(jìn)行改造??倳?huì)有一個(gè)方案滿足你的胃口。

比如,可以用列表來保存用戶每次訪問的時(shí)間。一旦當(dāng)前列表長度大于等于100,就用當(dāng)前時(shí)間和第一個(gè)元素的時(shí)間進(jìn)行比較,如果小于60秒的話,就直接拒絕啦。同時(shí),把第一個(gè)元素刪除,讓當(dāng)前時(shí)間保存到列表里。如此一來,就可以保證每60秒,最多只能讓用戶最多訪問100次。

上面就是令牌桶算法了。

漏桶算法

既然提到令牌桶,就不得不提另外一個(gè)了——漏桶算法。這兩個(gè)概念有點(diǎn)像,都是限制一段時(shí)間允許用戶訪問的最大次數(shù)。不同的是,令牌桶更加靈活一點(diǎn),允許某種程序的突發(fā)傳輸。比如上面說的問題2,洪峰爆發(fā)。那么,漏桶算法是什么鬼?

我是漏桶

如上圖所示,我們假設(shè)系統(tǒng)是一個(gè)漏桶,當(dāng)請(qǐng)求到達(dá)時(shí),就是往漏桶里“加水”,而當(dāng)請(qǐng)求被處理掉,就是水從漏桶的底部漏出。水漏出的速度是固定的,當(dāng)“加水”太快,桶就會(huì)溢出,也就是“拒絕請(qǐng)求”。從而使得桶里的水的體積不可能超出桶的容量。

需要注意的是,這里的漏水速率是恒定的??梢院唵未炙椎乩斫鉃椋摲?wù)每秒鐘只能處理N個(gè)請(qǐng)求,當(dāng)接收到請(qǐng)求(從天而降的水)的時(shí)候,會(huì)放入桶中待處理。當(dāng)桶(隊(duì)列)滿了的時(shí)候,會(huì)直接拒絕丟棄。使用該算法,可以有效地避免服務(wù)被過度使用導(dǎo)致性能嚴(yán)重下降。

這兩個(gè)算法可以結(jié)合使用。令牌桶可以用于針對(duì)單個(gè)用戶訪問的頻率,漏桶可以用于限制某個(gè)接口在規(guī)定時(shí)間內(nèi)的被(所有用戶)訪問次數(shù)。

舉個(gè)例子。對(duì)于接口a而言。

  1. 每個(gè)人在1分鐘內(nèi)最多只允許訪問100次;
  2. 該接口在1分鐘內(nèi)最多只能被調(diào)用100,000次;
    前者為了防止被惡意大量調(diào)用,后者保證服務(wù)可用。這樣,在接口頻率限制的情況下,允許個(gè)別情況持續(xù)突發(fā)。

說了這么多,可以發(fā)現(xiàn)redis還是挺好用的。雖然,這種事情并不一定要在項(xiàng)目里面做,其實(shí)還可以在代理服務(wù)器上面進(jìn)行。

nginx的方案

nginx也有自己的解決方案,直接在配置文件里面配置即可

limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
server {    
  location /search/ {        
    limit_req zone=one burst=5;    
  }
}

總結(jié)

通篇文章讀完之后,發(fā)現(xiàn)自己寫的其實(shí)是訪問頻率限制(可惡的標(biāo)題黨,還以為你講的是redis)。好吧,我說的就是這個(gè)算法。
這兩個(gè)算法不只是接口頻率限制,其實(shí)還能用在各種各樣關(guān)于限制的事情上。比如,驗(yàn)證碼的使用,不能被過度使用。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 摘要:在開發(fā)高并發(fā)系統(tǒng)時(shí)有三把利器用來保護(hù)系統(tǒng):緩存、降級(jí)和限流。而有些場景并不能用緩存和降級(jí)來解決,因此需有一種...
    落羽成霜丶閱讀 2,230評(píng)論 0 18
  • 聊聊高并發(fā)系統(tǒng)限流特技-1來自開濤的博客 在開發(fā)高并發(fā)系統(tǒng)時(shí)有三把利器用來保護(hù)系統(tǒng):緩存、降級(jí)和限流。緩存的目的是...
    meng_philip123閱讀 6,865評(píng)論 1 20
  • 最近一直都在研究壓力測試客戶端的問題,如果突破客戶端壓力測試線程,端口等問題,如果服務(wù)器端處理網(wǎng)絡(luò)請(qǐng)求處理不過來,...
    望月成三人閱讀 8,761評(píng)論 1 25
  • 一 、場景描述 在開發(fā)接口服務(wù)器的過程中,為了防止客戶端對(duì)于接口的濫用,保護(hù)服務(wù)器的資源, 通常來說我們會(huì)對(duì)于服務(wù)...
    dreamer_lk閱讀 10,249評(píng)論 7 19
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,534評(píng)論 19 139

友情鏈接更多精彩內(nèi)容