算法描述:
用户配置的平均发送速率为r,则每隔1/r秒一个令牌被加入到桶中(每秒会有r个令牌放入桶中),桶中最多可以存放b个令牌。如果令牌到达时令牌桶已经满了,那么这个令牌会被丢弃;
实现用户粒度的限流
虽然在某些情况下使用单个全局速率限制器非常有用,但另一种常见情况是基于IP地址或API密钥等标识符为每个用户实施速率限制器。我们将使用IP地址作为标识符。简单实现代码如下:
package main
import (
"net/http"
"sync"
"time"
"golang.org/x/time/rate"
)
// Create a custom visitor struct which holds the rate limiter for each
// visitor and the last time that the visitor was seen.
type visitor struct {
limiter *rate.Limiter
lastSeen time.Time
}
// Change the the map to hold values of the type visitor.
var visitors = make(map[string]*visitor)
var mtx sync.Mutex
// Run a background goroutine to remove old entries from the visitors map.
func init() {
go cleanupVisitors()
}
func addVisitor(ip string) *rate.Limiter {
limiter := rate.NewLimiter(2, 5)
mtx.Lock()
// Include the current time when creating a new visitor.
visitors[ip] = &visitor{limiter, time.Now()}
mtx.Unlock()
return limiter
}
func getVisitor(ip string) *rate.Limiter {
mtx.Lock()
v, exists := visitors[ip]
if !exists {
mtx.Unlock()
return addVisitor(ip)
}
// Update the last seen time for the visitor.
v.lastSeen = time.Now()
mtx.Unlock()
return v.limiter
}
// Every minute check the map for visitors that haven't been seen for
// more than 3 minutes and delete the entries.
func cleanupVisitors() {
for {
time.Sleep(time.Minute)
mtx.Lock()
for ip, v := range visitors {
if time.Now().Sub(v.lastSeen) > 3*time.Minute {
delete(visitors, ip)
}
}
mtx.Unlock()
}
}
func limit(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
limiter := getVisitor(r.RemoteAddr)
if limiter.Allow() == false {
http.Error(w, http.StatusText(429), http.StatusTooManyRequests)
return
}
next.ServeHTTP(w, r)
})
}
当然这只是一个简单的实现方案,如果我们要在微服务的API-GateWay中去实现限流还是要考虑很多东西的。建议大家可以看看 https://github.com/didip/tollbooth 的源码。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持易采站长站。









