golang限流器
限流应该是我们开发中经常遇到的了,限流器能保证我们不至于在流量过大的时候服务超过负载,能有效地保证服务的可用和稳定。
go自带有限流器rate,它的本质其实就是令牌桶。用起来也十分简单。我们修改下之前的http服务做一些修改:
func main() {
// ServeMux类型是HTTP请求的多路转接器。它会将每一个接收的请求的URL与一个注册模式的列表进行匹配,并调用和URL最匹配的模式的处理器。
mux := http.NewServeMux()
mux.HandleFunc("/", defaultHttp)
http.ListenAndServe(":8080", middlewareLimit(mux))
}
// 限流桶
var limiter = rate.NewLimiter(rate.Every(time.Second), 1)
func middlewareLimit(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if limiter.Allow() == false {
fmt.Println("limit")
return
}
next.ServeHTTP(w, r)
})
}
// 默认http处理
func defaultHttp(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
if path == "/" {
w.Write([]byte("index"))
fmt.Println("index")
return
}
// 自定义404
http.Error(w, "you lost???", http.StatusNotFound)
}
这里的
var limiter = rate.NewLimiter(rate.Every(2*time.Second), 1)
就是定义了一个限流器,生成速率是1 个/s,令牌桶的容量是1。也就是每秒最多能通过2个请求。
我们可以用ab或者快速刷新浏览器来看一下效果
是不是有点类似nginx的限流模块:
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
server {
...
location / {
#缓存区队列burst=5个,nodelay表示不延期(超过的请求失败),即每秒最多可处理rate+burst个,同时处理rate个。
limit_req zone=one burst=1 nodelay;
}
}
上面的代码有两处注意点:
- middlewareLimit 可看作一个http server的前置中间件,你可以类比去自己处理复杂的http中间件业务。
- 限流器的初始化务必在中间件前生成,可以尝试修改代码再测试:
func middlewareLimit(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
limiter := rate.NewLimiter(rate.Every(2*time.Second), 10)
if limiter.Allow() == false {
fmt.Println("limit")
return
}
next.ServeHTTP(w, r)
})
}
此外,Limiter 也有其他的方法:
- SetLimit(Limit) 动态修改放入令牌的速率
- SetBurst(int) 动态修改桶大小
- Wait/WaitN 当没有可用事件时,将阻塞等待
- Reserve/ReserveN 当没有可用事件时,返回 Reservation,和要等待多久才能获得足够的事件
代码点击见githubgithub.com/puresai/go-example/tree/main/demo9-rate
golang限流器
https://blog.puresai.com/2021/02/12/goexample9/