前言
限流這層按說是應該在網關那做的,這裏爲了學習go kit暫時放在這裏
在middleware/rate.go使用gokit的endpointMiddleWare定義一個限流中間件,在main函數裏面將要限流的接口用這個函數裝飾一下,這個接口就能實現限流相關功能,本項目地址: github地址
1.限流原理
常見的限流方法有令牌桶和漏桶
- 令牌桶
令牌桶算法的原理是系統會以一個恆定的速度往桶裏放入令牌,而如果請求需要被處理,則需要先從桶裏獲取一個令牌,當桶裏沒有令牌可取時,則拒絕服務。 - 漏桶
漏桶算法思路很簡單,水(請求)先進入到漏桶裏,漏桶以一定的速度出水(接口有響應速率),當水流入速度過大會直接溢出(訪問頻率超過接口響應速率),然後就拒絕請求,可以看出漏桶算法能強行限制數據的傳輸速率。 - 對比
漏桶的漏出速率是固定的參數,所以即使網絡中不存在資源衝突(沒有發生擁塞),漏桶算法也不能使流突發(burst)到端口速率,因此,漏桶算法對於存在突發特性的流量來說缺乏效率。令牌桶算法用來控制發送到網絡上的數據的數目,並允許突發數據的發送,對於不穩定情況下的流量有一定的兼容性。本文使用更爲常見的令牌桶算法進行限流。
2. 創建限流器
在middleware/rate.go使用gokit的endpointMiddleWare定義一個限流中間件:
func NewRateLimit(interval int, burst int) endpoint.Middleware {
limiter := rate.NewLimiter(rate.Every(time.Second*time.Duration(interval)), burst)
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
if !limiter.Allow() {
//這裏使用了上節的自定義錯誤
return nil, util.NewMyError(429, "too many request,please waiting...")
}
return next(ctx, request)
}
}
}
3.修改main函數
getUserNameEndpoint := userservice.MakeGetUserNameEndpoint(svc)
//每秒鐘getUserName接口只能接受一個請求,但是可以容忍瞬間提高的5個請求,超過限制的請求會報429
getUserNameEndpoint = middleware.NewRateLimit(1, 5)(getUserNameEndpoint)
調試
- 啓動服務,訪問
localhost:8000/user/1
,連續點擊url的刷新按鈕,開始會正常返回,過一會之後會返回我們在中間件裏面自定義的錯誤"too many request,please waiting…",http status code也會變成429