https://blog.csdn.net/ta_ab/article/details/77984312
API限流
微服務開發中有時需要對API做限流保護,防止網絡攻擊,比如做一個短信驗證碼API,限制客戶端的請求速率能在一定程度上抵御短信轟炸攻擊,降低損失。
微服務網關是每個請求的必經入口,非常適合做一些API限流、認證之類的操作,這里有一個基於zuul微服務網關的API限流庫:
https://github.com/marcosbarbero/spring-cloud-zuul-ratelimit
使用方法
比如我們要對user-service這個服務進行限流,限制每個請求源每分鍾最多只能請求10次。
首先在項目中添加 spring-cloud-zuul-ratelimit 依賴:
<dependency> <groupId>com.marcosbarbero.cloud</groupId> <artifactId>spring-cloud-zuul-ratelimit</artifactId> <version>1.5.0.RELEASE</version> </dependency>
- 1
- 2
- 3
- 4
- 5
然后再添加如下配置即可:
zuul:
ratelimit:
enabled: true
behind-proxy: true
policy-list:
user-service:
- limit: 10 refresh-interval: 60 type: - user - origin - url
-
測試客戶端如果60s內請求超過10次,服務端就拋出異常,一分鍾后又可以正常請求
-
某個IP的客戶端被限流並不影響其他客戶端,即API網關對每個客戶端限流是相互獨立的
限流數據存儲
對API限流是基於Zuul過濾器完成的,默認情況下限流數據是記錄在內存中的,實際上是用ConcurrentHashMap保存,當然也提供了多種存儲方式,包括Redis、Consul、Spring Data JPA,使用這三種存儲方式要添加相關依賴。
然后再添加存儲配置,比如使用Redis的配置:
zuul: ratelimit: repository: Redis
- 1
- 2
- 3
原理分析
限流攔截時機
限流過濾器是在請求被轉發之前調用的
@Override public String filterType() { return "pre"; }
限流類型
限流類型主要包括url、origin、user三種
if (types.contains(URL)) { joiner.add(route.getPath()); } if (types.contains(ORIGIN)) { joiner.add(getRemoteAddr(request)); } if (types.contains(USER)) { joiner.add(request.getUserPrincipal() != null ? request.getUserPrincipal().getName() : ANONYMOUS); }
-
url類型的限流就是通過請求路徑區分
-
origin是通過客戶端IP地址區分
-
user是通過授權用戶進行區分,也包括匿名用戶
-
可以多個限流類型結合使用
-
如果不配置限流類型,就不做以上區分
攔截限流請求
在過濾器的run方法中判斷請求剩余次數,小於0就攔截請求:
if (rate.getRemaining() < 0) { ctx.setResponseStatusCode(TOO_MANY_REQUESTS.value()); ctx.put("rateLimitExceeded", "true"); throw new ZuulRuntimeException(new ZuulException(TOO_MANY_REQUESTS.toString(), TOO_MANY_REQUESTS.value(), null)); }
- 1
- 2
- 3
- 4
- 5
- 6
可以看到,單位時間內剩余請求次數小於0時拋出ZuulRuntimeException,直接返回客戶端TOO_MANY_REQUESTS異常消息,達到攔截請求的效果。
