服務限流
服務網關的一個重要功能即服務限流。在Zuul中雖然可以通過前置類型過濾器對有效請求和無效請求(參數有效性,合法性)進行校驗,将無效請求隔離在服務網關之外(傳回錯誤提示資訊、異常資訊)。但有的時候某一個時間點内突發有效請求量很大,超過了系統可承受的範圍,此時可以依靠服務限流算法,限制該時間點内通過的請求數,保證系統可用,避免出現服務雪崩效應。
此時可以通過Spring Cloud提供的Resilience4j限速器(RateLimiter)進行限速,也可以使用spring-cloud-zuul-ratelimit包。
項目依賴
<dependency> <groupId>io.github.resilience4jgroupId> <artifactId>resilience4j-spring-boot2artifactId> <version>0.13.2version> dependency> <dependency> <groupId>com.marcosbarbero.cloudgroupId> <artifactId>spring-cloud-zuul-ratelimitartifactId> <version>2.2.2.RELEASEversion> dependency>
使用Resilience4j限流
限流配置
#resilience4j限流配置resilience4j.ratelimiter.limiters.user-service.limit-for-period=3resilience4j.ratelimiter.limiters.user-service.limit-refresh-period-in-millis=5000resilience4j.ratelimiter.limiters.user-service.timeout-in-millis=10
上述配置中,resilience4j.ratelimiter.limiters.user-service.limit-for-period配置時間戳内的請求數(預設為50),limit-refresh-period-in-millis配置時間戳(機關毫秒,預設500),timeout-in-millis配置逾時時間。user-service可以替換為其他有意義的名稱。
限流過濾器
@Componentpublic class Resilience4jRateLimiterFilter extends ZuulFilter { //Resilience4j限速器注冊機 @Autowired private RateLimiterRegistry rateLimiterRegistry; /** * 傳回過濾器類型 * @return 前置類型過濾器 */ @Override public String filterType() { return FilterConstants.PRE_TYPE; } /** * 傳回過濾器執行順序 * @return */ @Override public int filterOrder() { return FilterConstants.PRE_DECORATION_FILTER_ORDER+5; } /** * 傳回過濾器是否執行攔截操作 * @return 請求路徑是否為/user/開頭 */ @Override public boolean shouldFilter() { RequestContext requestContext=RequestContext.getCurrentContext(); String uri=requestContext.getRequest().getRequestURI(); return uri.startsWith("/user/"); } /** * 過濾器具體執行業務邏輯 * @return * @throws ZuulException */ @Override public Object run() throws ZuulException { //擷取Resilience4j限速器(key與配置檔案的Key名稱一緻) RateLimiter rateLimiter=rateLimiterRegistry.rateLimiter("user-service"); //限速器執行邏輯 Callable callResult=()->new ResultMessage(true,"執行成功"); //綁定限速器 Callable callResult2=RateLimiter.decorateCallable(rateLimiter,callResult); //嘗試擷取執行結果 Try resultMessageTry=Try.of(()->callResult2.call()).recover(ex->new ResultMessage(false,"超出限定流量,執行降級操作")); ResultMessage resultMessage=resultMessageTry.get(); //執行結果傳回true,說明在限流範圍内,限流操作成功,繼續執行後續的自定義過濾器 if(resultMessage.isSuccess()) { return null; } //超過限流範圍處理 RequestContext requestContext=RequestContext.getCurrentContext(); requestContext.setSendZuulResponse(false);//不在路由到下一個執行順序的過濾器,直接傳回失敗 requestContext.setResponseStatusCode(HttpStatus.TOO_MANY_REQUESTS.value());//設定響應狀态碼(值為429) requestContext.getResponse().setContentType(MediaType.APPLICATION_JSON_VALUE);//設定響應類型(APPLICATION_JSON_UTF8_VALUE已廢棄) ObjectMapper responseMapper=new ObjectMapper(); String responseBody=null; try { responseBody=responseMapper.writeValueAsString(resultMessage);//将執行結果轉換為字元串并輸出 } catch (Exception e) { e.printStackTrace(); } requestContext.setResponseBody(responseBody); return null; }}
在開發Zuul過濾器的過程中,需要熟悉Zuul過濾器類型及Zuul内置過濾器執行順序。本例中,限流過濾器應該在過濾有效請求(請求參數有效)後執行,故過濾器執行順序為前置(pre)過濾器PreDecorationFilter後路由(route)過濾器前(PreDecorationFilter用于處理請求上下文,SimpleHostRoutingFilter用于具體的URL請求轉發)。
我們将Resilience4jRateLimiterFilter(Resilience4j限速過濾器)聲明為元件(@Component),在入口程式中被掃描發現并裝配;注入限速注冊機(RateLimiterRegistry),通過限速注冊機擷取具體的Resilience4j限速器執行個體(rateLimiterRegistry.rateLimiter(key));通過RateLimiter.decorateCallable方法(參數為Resilience4j執行個體,限速器業務邏輯);在嘗試擷取執行結果的過程中發生異常(限流操作超出限制範圍),通過recover方法實作服務降級相關操作(傳回緩存結果、服務降級資訊、異常),提供更好的使用者體驗;未超過服務限流範圍執行後續的過濾器操作,超過服務限流範圍則設定響應資訊給服務調用方展現。
Spring-cloud-zuul-ratelimit限流
為了防止微服務系統中的服務調用接口被惡意頻繁請求調用,需要在服務網關層進行服務限流對服務調用接口安全性進行保護。使用spring-cloud-zuul-ratelimit可以在Zuul的基礎上實作服務網關限流相關功能。
限流方式
限流方式 | 限流描述 |
Authenticated User(認證使用者) | 使用已經認證的使用者名或'anonymous' |
Request Origin(原始請求) | 使用使用者的原始請求 |
URL | 使用上遊請求的位址 |
Global configuration per service(全局配置) | 針對每個服務的全局配置,無需指定限流方式 |
配置詳解
#spring-cloud-zuul-ratelimit配置(全局配置)#是否開啟限速配置(boolean,預設false)zuul.ratelimit.enabled=true #使用Redis緩存對應的度量資料zuul.ratelimit.repository=redis#是否開啟代理服務(boolean,預設false)zuul.ratelimit.behind-proxy=true#是否添加響應報頭(boolean,預設false)zuul.ratelimit.add-response-header=true#每個重新整理時間視窗對應的請求數量限制zuul.ratelimit.default-policy.limit=10#每個重新整理時間視窗對應的請求時間限制zuul.ratelimit.default-policy.quota=1000#重新整理時間視窗時間(預設60,機關秒)zuul.ratelimit.default-policy.refresh-interval=60#全局限流方式(可選,user,origin,url)zuul.ratelimit.default-policy.type=user#spring-cloud-zuul-ratelimit配置(服務執行個體配置)#限制使用者微服務執行個體(user-service)重新整理時間視窗對應的請求數量限制zuul.ratelimit.user-service.limit=5#限制使用者微服務執行個體(user-service)重新整理時間視窗對應的請求時間限制zuul.ratelimit.user-service.quota=100#限制使用者微服務執行個體(user-service)限流方式zuul.ratelimit.user-service.type=origin