通过chan来实现一个简单的令牌桶限速,初始化一个chan之后,一个gorouting负责按设定的速率往里面放令牌,调用的时候取令牌,取到则可以继续执行,否则则表示被限流。
简单的代码实现如下:
package main
import (
"fmt"
"net/http"
"time"
)
var lim *Limit
// Limit 限速器
type Limit struct {
ch chan struct{}
}
// NewLimiter 生成一个限速器并开始往里面放令牌
// rate: 速率,单位次每秒
// cap: 令牌桶的容量
func NewLimiter(rate, cap int) *Limit {
limit := Limit{
ch: make(chan struct{}, cap),
}
go func() {
ticker := time.NewTicker(time.Duration(1000000/rate) * time.Microsecond)
for {
select {
case <-ticker.C:
limit.ch <- struct{}{}
}
}
}()
return &limit
}
// Allow 判断是否获取到令牌
func (lim *Limit) Allow() bool {
select {
case <-lim.ch:
return true
default:
return false
}
}
func hello(w http.ResponseWriter, req *http.Request) {
if lim.Allow() {
w.WriteHeader(200)
fmt.Fprintln(w, "hello")
} else {
w.WriteHeader(403)
fmt.Fprintln(w, "deny")
}
}
func main() {
lim = NewLimiter(100, 100)
http.HandleFunc("/", hello)
http.ListenAndServe(":8888", nil)
}
按照速率100/s,桶容量100来启动服务。
启动服务之后,使用go-wrt来进行测试
go-wrk -t=8 -c=100 -n=10000 “http://127.0.0.1:8888/”
结果如下:
==========================BENCHMARK==========================
URL: http://127.0.0.1:8888/
Used Connections: 100
Used Threads: 8
Total number of calls: 10000
===========================TIMINGS===========================
Total time passed: 1.16s
Avg time per request: 11.44ms
Requests per second: 8585.02
Median time per request: 8.47ms
99th percentile time: 288.15ms
Slowest time for request: 298.00ms
=============================DATA=============================
Total response body sizes: 50217
Avg response body per request: 5.02 Byte
Transfer rate per second: 43111.38 Byte/s (0.04 MByte/s)
==========================RESPONSES==========================
20X Responses: 217 (2.17%)
30X Responses: 0 (0.00%)
40X Responses: 9783 (97.83%)
50X Responses: 0 (0.00%)
Errors: 0 (0.00%)
因为是先启动之后,然后再启动测试脚本的。从结果来看,初始100个令牌满了,执行时间1.16s,往里面又放了116个,总计应该是216个未被限流。结果是217,符合预期。