網上找到的資料大多是針對服務限流,接口限流的資料很少,而且配置複雜,於是我結合網上資料和自己的實踐,去粗取精,形成了適合自己的極簡配置。
導入依賴
<dependency>
<groupId>com.marcosbarbero.cloud</groupId>
<artifactId>spring-cloud-zuul-ratelimit</artifactId>
<version>2.4.0.RELEASE</version>
</dependency>
配置參考
zuul:
routes:
user-web:
path: /user/**
serviceId: jd-user-web
stripPrefix: false
order-web:
path: /order/**
serviceId: jd-order-web
stripPrefix: false
ratelimit:
key-prefix: ${spring.application.name}-rate-limit #緩存key的前綴
enabled: true #開啓限流
repository: REDIS #key存儲方式爲redis
behind-proxy: true # 當前網關是代理後的請求,需要獲取Header中的X-FORWARDED-FOR以便獲取源IP
policy-list: #指定服務策略,優先默認
user-web: #路由ID
- limit: 5 #每個刷新窗口請求數
quota: 3 #每個刷新窗口總請求時間(秒)
refresh-interval: 15 #刷新窗口時間(秒),默認60秒
type:
- url=/user/getUser
- limit: 5 #每個刷新窗口請求數
quota: 3 #每個刷新窗口總請求時間(秒)
refresh-interval: 15 #刷新窗口時間(秒),默認60秒
type:
- url=/user/getWxUser
order-web:
- limit: 5 #每個刷新窗口請求數
quota: 3 #每個刷新窗口總請求時間(秒)
refresh-interval: 15 #刷新窗口時間(秒),默認60秒
type:
- url=/order/shop/list
關鍵參數解讀
以下面參數爲例,進行詳細說明。
user-web: #路由ID
limit: 5 #每個刷新窗口請求數
quota: 3 #每個刷新窗口總請求時間(秒)
refresh-interval: 15 #刷新窗口時間(秒),默認60秒
user-web:此處不是serviceId,網上都是誤導的,我管他叫路由ID,如果serviceId和路由ID不一致,此次要取路由ID,否則,會導致限流不起作用。
limit: 請求總次數。15秒內只能請求5次,超過了報 "429 TOO_MANY_REQUESTS"。
quota: 請求累計時長。每次請求的時間,累計不能超過3秒,假如前4次請求總耗時已經超過3秒,那麼第5次請求報"429 TOO_MANY_REQUESTS"。這個參數一般可以省略。
refresh-interval: 統計請求的時間窗口長度。從第一次請求開始,在緩存裏設置一個15秒後失效的key,key由key-prefix + routeId + url拼接成。
如果在第5秒的時候,請求超過了5次,那麼後續請求都報"429 TOO_MANY_REQUESTS",只能繼續等待10秒緩存失效後,再次請求恢復正常。這就是“突刺現象”,也是這個限流框架的缺點。
第一次接口調用後,可以看到緩存中生成的key:
jd-zuul-dev-rate-limit:user-web:/user/getUser:/user/getUser
jd-zuul-dev-rate-limit:user-web:/user/getUser:/user/getUser-quota
自定義異常信息
這是網上的方式。實際上RateLimiterErrorHandler無法捕獲到限流異常信息429 TOO_MANY_REQUESTS,也就不能自定義異常返回信息。得通過ExceptionHandler返回自定義異常信息。
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ExceptionHandler implements ErrorController {
@Override
public String getErrorPath() {
return "error";
}
@RequestMapping(value="/error")
public Rsp error(){
return new Rsp(RSP_CODE_ERROR, "系統忙,請稍後再試");
}
}
網關限流參數
自定義Key策略
如果希望自己控制key的策略,可以通過自定義RateLimitKeyGenerator的實現來增加自己的策略邏輯。
例如:根據請求上的參數來對請求進行限流。比如有一個請求是http://localhost:8765/api-a//hello2?name=kevin,對相同的name值進行限流。我們設置了1分鐘內,限流10次,那麼如果1分鐘內,name是kevin的請求超過10次,就會發生限流。
RateLimitKeyGenerator的實現:
附記
這篇文章寫的很好,各種限流框架做了一個對比,列出各種優缺點,原理分析等等。
https://segmentfault.com/a/1190000020745218