[Go]GO實現滑動視窗限流演算法-單機版
本程式碼基於原部落格java版本的GO實現 , 原文解釋也比較詳細 , 這裡也放上原文連結:https://www.cnblogs.com/dijia478/p/13807826.html
具體解釋如下 , 程式碼在最下面
1.10秒內通過5次 , 這條線就是佇列list,當第一個事件進來,佇列大小是0,時間是第1秒:
2.因為size=0,小於5,都沒有到限制的次數,完全不用考慮時間視窗,直接把這次事件的時間戳放到0的位置:
3.第2.8秒的時候,第二個事件來了。因為此時size=1,還是小於5,把這次事件的時間戳放到0的位置,原來第1秒來的事件時間戳會往後移動一格:
4.陸續的又來了3個事件,佇列大小變成了5,先來的時間戳依次向後移動。此時,第6個事件來了,時間是第8秒:
5.因為size=5,不小於5,此時已經達到限制次數,以後都需要考慮時間視窗了。所以取出位置4的時間(離現在最遠的時間),和第6個事件的時間戳做比較:
6.得到的差是7秒,小於時間視窗10秒,說明在10秒內,來的事件個數大於5了,所以本次不允許通過:
7.接下來即便來上100個事件,只要時間差小於等於10秒,都同上,拒絕通過:
8.第11.1秒,第101次事件過來了。因為size=5,不小於5,所以取出位置4的時間(離現在最遠的時間),和第101個事件的時間戳做比較:
9.得到的差是10.1秒,大於時間視窗10秒,說明在10秒內,來的事件個數小於等於5了,所以本次允許通過:
10.刪除位置4的時間(離現在最遠的時間),把這次事件的時間戳放到0的位置,後面的時間戳依次向後移動:
往後再來其他事件,就是重複4-10的步驟,即可實現,在任意滑動時間視窗內,限制通過的次數
其本質思想是轉換概念,將原本問題的確定時間大小,進行次數限制。轉換成確定次數大小,進行時間限制。
package utils import "time" var LimitQueue map[string][]int64 var ok bool //單機時間滑動視窗限流法 func LimitFreqSingle(queueName string, count uint, timeWindow int64) bool { currTime := time.Now().Unix()if LimitQueue == nil { LimitQueue = make(map[string][]int64) } if _, ok = LimitQueue[queueName]; !ok { LimitQueue[queueName] = make([]int64, 0) } //佇列未滿 if uint(len(LimitQueue[queueName])) < count { LimitQueue[queueName] = append(LimitQueue[queueName], currTime) return true } //佇列滿了,取出最早訪問的時間 earlyTime := LimitQueue[queueName][0] //說明最早期的時間還在時間視窗內,還沒過期,所以不允許通過 if currTime-earlyTime <= timeWindow { return false } else { //說明最早期的訪問應該過期了,去掉最早期的 LimitQueue[queueName] = LimitQueue[queueName][1:] LimitQueue[queueName] = append(LimitQueue[queueName], currTime) } return true }
使用的案例:
func limitIpFreq(c *gin.Context, timeWindow int64, count uint) bool { ip := c.ClientIP() key := "limit:" + ip if !utils.LimitFreqSingle(key, count, timeWindow) { c.JSON(200, gin.H{ "code": 400, "msg": "error Current IP frequently visited", }) return false } return true }