Spring Cloud Gateway中的過濾器工廠:重試過濾器

aoho發表於2018-04-25

Spring Cloud Gateway基於Spring Boot 2,是Spring Cloud的全新專案,該專案提供了一個構建在Spring 生態之上的API閘道器。本文基於的Spring Cloud版本為Finchley M9,Spring Cloud Gateway對應的版本為2.0.0.RC1

Spring Cloud Gateway入門一文介紹了全新的Spring Cloud Gateway的一些基礎應用。本文將會介紹Spring Cloud Gateway重試過濾器。

過濾器

GatewayFilter閘道器過濾器用於攔截和鏈式處理web請求,可以實現橫切的、與應用無關的需求,比如安全、訪問超時的設定等等。

public interface GatewayFilter {
	Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}
複製程式碼

介面中定義了唯一的方法#filter,處理web請求,並且可以通過給定的過濾器鏈傳遞到下一個過濾器。該介面有多個實現類,下面看一下該介面的類圖。

GatewayFilter
GatewayFilter類圖
從類圖可以看到,GatewayFilter有兩個實現類,但是在原始碼中尋找該介面的用法會發現,在GatewayFilterFactory實現類中有內部匿名類,實際是返回了一個 GatewayFilter 內部實現類。

Spring Cloud Gateway提供了很多種類的過濾器工廠,閘道器過濾器有近二十個實現類,總得說來可以分為七類:Header、Parameter、Path、Status、Redirect跳轉、Hystrix熔斷和RateLimiter限流等。

重試過濾器

請求的重試

當轉發到代理服務時,遇到指定的服務端Error,如httpStatus為500時,我們可以設定重試幾次。除了對指定的異常重試之外,還可以指定請求的方法,GET或POST。

實驗場景涉及到:閘道器服務和使用者服務。客戶端請求經過閘道器,請求使用者服務的API介面,遇到指定的異常時,進行重試。

專案準備

示例啟動兩個服務:Gateway-Server和user-Server。模擬的場景是,客戶端請求後端服務,閘道器提供後端服務的統一入口。後端的服務都註冊到服務發現Consul(搭建zk,Eureka都可以,筆者比較習慣使用consul)。閘道器通過負載均衡轉發到具體的後端服務。

使用者服務

使用者服務註冊到Consul上,並提供一個介面/test。

閘道器服務

引入閘道器的依賴,並進行相應配置。上一章已經講過,這裡不重複列出程式碼,具體見原始碼。

服務改造

閘道器服務

閘道器服務中,新增一個路由的定義retry_java,請求的判定是路徑以/test為字首的請求,並將請求轉發到user服務。當遇到內部服務錯誤(狀態碼為500)時,設定重試的次數為2。當然該路由也可以通過閘道器服務的配置檔案,效果是一樣的。

    @Bean
    public RouteLocator retryRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("retry_java", r -> r.path("/test/**")
                        .filters(f -> f.stripPrefix(1)
                                .retry(config -> config.setRetries(2).setStatuses(HttpStatus.INTERNAL_SERVER_ERROR)))
			.uri("lb://user"))
		.build();
    }
複製程式碼

使用者服務

使用者服務增加一個API介面,請求中傳入引數key和count。

    ConcurrentHashMap<String, AtomicInteger> map = new ConcurrentHashMap<>();

    @GetMapping("/exception")
    public String testException(@RequestParam("key") String key, @RequestParam(name = "count", defaultValue = "3") int count) {
        AtomicInteger num = map.computeIfAbsent(key, s -> new AtomicInteger());
        int i = num.incrementAndGet();
        log.warn("Retry count: "+i);
        if (i < count) {
            throw new RuntimeException("temporarily broken");
        }
        return String.valueOf(i);
    }
複製程式碼

這裡主要是為了能配置閘道器請求次數的演示,count是指定的重試次數,預設為3,第一次和第二次都會丟擲執行時異常(狀態碼為500),變數 i 是key對應的值,初始為0,每重試一次,i 會遞增,直到 i 大於等於count的值。

測試結果

根據上面的實現,我們訪問的地址為http://localhost:9090/test/exception?key=abc&count=2。 按照使用者服務實現的邏輯,使用者服務將會重試一次即可成功。使用者服務的控制檯日誌資訊如下:

Retry count: 1

java.lang.IllegalArgumentException: temporarily broken] with root cause
...
Retry count: 2
複製程式碼

請求的響應結果
請求的響應結果

控制檯的資訊和最後的響應結果可以看出,請求的重試執行成功。

小結

本文在Spring Cloud Gateway入門的基礎上,介紹了Spring Cloud Gateway的過濾器相關概念,並具體介紹了其中的一個過濾器工廠:RetryGatewayFilterFactory。當轉發到代理服務時,遇到指定的服務端Error,如httpStatus為500時,我們可以設定重試幾次,應用重試過濾器。Spring Cloud Gateway提供了很多過濾器工廠的實現,後面文章將會介紹其中比較重要的過濾器,敬請關注。

原始碼地址

https://github.com/keets2012/Spring-Cloud_Samples

訂閱最新文章,歡迎關注我的公眾號

微信公眾號

參考

Spring Cloud Gateway官方文件

相關文章