SpringCloud Gateway能夠實現多種網關功能,比如路由轉發、斷言、過濾器、熔斷、限流、降級、自定義謂詞配置、自定義過濾器等等多種功能。今天,我們就一起來聊聊SpringCloud Gateway中的斷言、過濾器與熔斷機制。
網關斷言斷言的英文是Predicate,也可以翻譯成謂詞。主要的作用就是進行條件判斷,可以在網關中實現多種條件判斷,只有所有的判斷結果都通過時,也就是所有的條件判斷都返回true,才會真正的執行路由功能。
SpringCloud Gateway內置斷言SpringCloud Gateway包括許多內置的斷言工廠,所有這些斷言都與HTTP請求的不同屬性匹配。
基于日期時間類型的斷言基于日期時間類型的斷言根據時間做判斷,主要有三個:
AfterRoutePredicateFactory:接收一個日期時間參數,判斷當前請求的日期時間是否晚于指定的日期時間。BeforeRoutePredicateFactory:接收一個日期時間參數,判斷當前請求的日期時間是否早于指定的日期時間。BetweenRoutePredicateFactory:接收兩個日期時間參數,判斷當前請求的日期時間是否在指定的時間時間段內。使用示例- After=2022-05-10T23:59:59.256+08:00[Asia/Shanghai]基于遠程地址的斷言
RemoteAddrRoutePredicateFactory:接收一個IP地址段,判斷發出請求的客戶端的IP地址是否在指定的IP地址段內。
使用示例- RemoteAddr=192.168.0.1/24基于Cookie的斷言
CookieRoutePredicateFactory:接收兩個參數, Cookie的名稱和一個正則表達式。判斷請求的Cookie是否具有給定名稱且值與正則表達式匹配。
使用示例- Cookie=name, binghe.基于Header的斷言
HeaderRoutePredicateFactory:接收兩個參數,請求Header的名稱和正則表達式。判斷請求Header中是否具有給定的名稱且值與正則表達式匹配。
使用示例- Header=X-Request-Id, \d+基于Host的斷言
HostRoutePredicateFactory:接收一個參數,這個參數通常是主機名或者域名的模式,例如**.binghe.com這種格式。判斷發出請求的主機是否滿足匹配規則。
使用示例- Host=**.binghe.com基于Method請求方法的斷言
MethodRoutePredicateFactory:接收一個參數,判斷請求的類型是否跟指定的類型匹配,通常指的是請求方式。例如,POST、GET、PUT等請求方式。
使用示例- Method=GET基于Path請求路徑的斷言
PathRoutePredicateFactory:接收一個參數,判斷請求的鏈接地址是否滿足路徑規則,通常指的是請求的URI部分。
使用示例- Path=/binghe/{segment}基于Query請求參數的斷言
QueryRoutePredicateFactory :接收兩個參數,請求參數和正則表達式, 判斷請求的參數是否具有給定的名稱并且參數值是否與正則表達式匹配。
使用示例- Query=name, binghe.基于路由權重的斷言
WeightRoutePredicateFactory:接收一個[組名,權重]格式的數組,然后對于同一個組內的路由按照權重轉發。
使用示例- id: weight1 uri: http://localhost:8080 predicates: - Path=/api/** - Weight=group1,2 filters: - StripPrefix=1- id: weight2 uri: http://localhost:8081 predicates: - Path=/api/** - Weight=group1,8 filters: - StripPrefix=1演示內置斷言
在演示的示例中,我們基于Path請求路徑的斷言判斷請求路徑是否符合規則,基于遠程地址的斷言判斷請求主機地址是否在地址段中,并且限制請求的方式為GET方式。整個演示的過程以訪問用戶微服務的接口為例。
(1)由于在開發項目時,所有的服務都是在我本地啟動的,首先查看下我本機的IP地址,如下所示。
可以看到,我本機的IP地址為192.168.0.27,屬于192.168.0.1/24網段。
(2)在服務網關模塊shop-gateway中,將application.yml文件備份成application-sentinel.yml文件,并將application.yml文件中的內容修改成application-simple.yml文件中的內容。接下來,在application.yml文件中的spring.cloud.gateway.routes節點下的- id: user-gateway下面進行斷言配置,配置后的結果如下所示。
spring: cloud: gateway: routes: - id: user-gateway uri: http://localhost:8060 order: 1 predicates: - Path=/server-user/** - RemoteAddr=192.168.0.1/24 - Method=GET filters: - StripPrefix=1
注意:完整的配置參見案例完整源代碼。
(3)配置完成后啟動用戶微服務和網關服務,通過網關服務訪問用戶微服務,在瀏覽器中輸入http://localhost:10001/server-user/user/get/1001,如下所示。
可以看到通過http://localhost:10001/server-user/user/get/1001鏈接不能正確訪問到用戶信息。
接下來,在瀏覽器中輸入http://192.168.0.27:10001/server-user/user/get/1001,能夠正確獲取到用戶的信息。
(4)停止網關微服務,將基于遠程地址的斷言配置成- RemoteAddr=192.168.1.1/24,也就是將基于遠程地址的斷言配置成與我本機IP地址不在同一個網段,這樣就能演示請求主機地址不在地址段中的情況,修改后的基于遠程地址的斷言配置如下所示。
- RemoteAddr=192.168.1.1/24
(5)重啟網關服務,再次在瀏覽器中輸入http://localhost:10001/server-user/user/get/1001,如下所示。
可以看到通過http://localhost:10001/server-user/user/get/1001鏈接不能正確訪問到用戶信息。
接下來,在瀏覽器中輸入http://192.168.0.27:10001/server-user/user/get/1001,也不能正確獲取到用戶的信息了。
自定義斷言SpringCloud Gateway支持自定義斷言功能,我們可以在具體業務中,基于SpringCloud Gateway自定義特定的斷言功能。
自定義斷言概述SpringCloud Gateway雖然提供了多種內置的斷言功能,但是在某些場景下無法滿足業務的需要,此時,我們就可以基于SpringCloud Gateway自定義斷言功能,以此來滿足我們的業務場景。
實現自定義斷言這里,我們基于SpringCloud Gateway實現斷言功能,實現后的效果是在服務網關的application.yml文件中的spring.cloud.gateway.routes節點下的- id: user-gateway下面進行如下配置。
spring: cloud: gateway: routes: - id: user-gateway uri: http://localhost:8060 order: 1 predicates: - Path=/server-user/** - Name=binghe filters: - StripPrefix=1
通過服務網關訪問用戶微服務時,只有在訪問的鏈接后面添加?name=binghe參數時才能正確訪問用戶微服務。
(1)在網關服務shop-gateway中新建io.binghe.shop.predicate包,在包下新建NameRoutePredicateConfig類,主要定義一個Spring類型的name成員變量,用來接收配置文件中的參數,源碼如下所示。
/** * @author binghe * @version 1.0.0 * @description 接收配置文件中的參數 */@Datapublic class NameRoutePredicateConfig implements Serializable { private static final long serialVersionUID = -3289515863427972825L; private String name;}
(2)實現自定義斷言時,需要新建類繼承org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory類,在io.binghe.shop.predicate包下新建NameRoutePredicateFactory類,繼承org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory類,并覆寫相關的方法,源碼如下所示。
/** * @author binghe * @version 1.0.0 * @description 自定義斷言功能 */@Componentpublic class NameRoutePredicateFactory extends AbstractRoutePredicateFactory (3)在服務網關的application.yml文件中的spring.cloud.gateway.routes節點下的- id: user-gateway下面進行如下配置。 spring: cloud: gateway: routes: - id: user-gateway uri: http://localhost:8060 order: 1 predicates: - Path=/server-user/** - Name=binghe filters: - StripPrefix=1 (4)分別啟動用戶微服務與網關服務,在瀏覽器中輸入http://localhost:10001/server-user/user/get/1001,如下所示。 可以看到,在瀏覽器中輸入http://localhost:10001/server-user/user/get/1001,無法獲取到用戶信息。 (5)在瀏覽器中輸入http://localhost:10001/server-user/user/get/1001?name=binghe,如下所示。 可以看到,在訪問鏈接后添加?name=binghe參數后,能夠正確獲取到用戶信息。 至此,我們實現了自定義斷言功能。 過濾器可以在請求過程中,修改請求的參數和響應的結果等信息。在生命周期的角度總體上可以分為前置過濾器(Pre)和后置過濾器(Post)。在實現的過濾范圍角度可以分為局部過濾器(GatewayFilter)和全局過濾器(GlobalFilter)。局部過濾器作用的范圍是某一個路由,全局過濾器作用的范圍是全部路由。 局部過濾器又稱為網關過濾器,這種過濾器主要是作用于單一路由或者某個路由分組。 在SpringCloud Gateway中內置了很多不同類型的局部過濾器,主要如下所示。 演示內部過濾器時,我們為原始請求添加一個名稱為IP的Header,值為localhost,并添加一個名稱為name的參數,參數值為binghe。同時修改響應的結果狀態,將結果狀態修改為1001。 (1)在服務網關的application.yml文件中的spring.cloud.gateway.routes節點下的- id: user-gateway下面進行如下配置。 spring: cloud: gateway: routes: - id: user-gateway uri: http://localhost:8060 order: 1 predicates: - Path=/server-user/** filters: - StripPrefix=1 - AddRequestHeader=IP,localhost - AddRequestParameter=name,binghe - SetStatus=1001 (2)在用戶微服務的io.binghe.shop.user.controller.UserController類中新增apiFilter1()方法,如下所示。 @GetMapping(value = "/api/filter1")public String apiFilter1(HttpServletRequest request, HttpServletResponse response){ log.info("訪問了apiFilter1接口"); String ip = request.getHeader("IP"); String name = request.getParameter("name"); log.info("ip = " + ip + ", name = " + name); return "apiFilter1";} 可以看到,在新增加的apiFilter1()方法中,獲取到新增加的Header與參數,并將獲取出來的參數與Header打印出來。并且方法返回的是字符串apiFilter1。 (3)分別啟動用戶微服務與網關服務,在瀏覽器中輸入http://localhost:10001/server-user/user/api/filter1,如下所示。 此時,查看瀏覽器中的響應狀態碼,如下所示。 可以看到,此時的狀態碼已經被修改為1001。 接下來,查看下用戶微服務的控制臺輸出的信息,發現在輸出的信息中存在如下數據。 訪問了apiFilter1接口ip = localhost, name = binghe 說明使用SpringCloud Gateway的內置過濾器成功為原始請求添加了一個名稱為IP的Header,值為localhost,并添加了一個名稱為name的參數,參數值為binghe。同時修改了響應的結果狀態,將結果狀態修改為1001,符合預期效果。 這里,我們基于SpringCloud Gateway自定義局部過濾器實現是否開啟灰度發布的功能,整個實現過程如下所示。 (1)在服務網關的application.yml文件中的spring.cloud.gateway.routes節點下的- id: user-gateway下面進行如下配置。 spring: cloud: gateway: routes: - id: user-gateway uri: http://localhost:8060 order: 1 predicates: - Path=/server-user/** filters: - StripPrefix=1 - Grayscale=true (2)在網關服務模塊shop-gateway中新建io.binghe.shop.filter包,在包下新建GrayscaleGatewayFilterConfig類,用于接收配置中的參數,如下所示。 /** * @author binghe * @version 1.0.0 * @description 接收配置參數 */@Datapublic class GrayscaleGatewayFilterConfig implements Serializable { private static final long serialVersionUID = 983019309000445082L; private boolean grayscale;} (3)在io.binghe.shop.filter包下GrayscaleGatewayFilterFactory類,繼承org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory類,主要是實現自定義過濾器,模擬實現灰度發布。代碼如下所示。 /** * @author binghe * @version 1.0.0 * @description 自定義過濾器模擬實現灰度發布 */@Componentpublic class GrayscaleGatewayFilterFactory extends AbstractGatewayFilterFactory (4)分別啟動用戶微服務和服務網關,在瀏覽器中輸入http://localhost:10001/server-user/user/get/1001,如下所示。 可以看到,通過服務網關正確訪問到了用戶微服務,并正確獲取到了用戶信息。 接下來,查看下服務網關的終端,發現已經成功輸出了如下信息。 開啟了灰度發布功能... 說明正確實現了自定義的局部過濾器。 全局過濾器是一系列特殊的過濾器,會根據條件應用到所有路由中。 在SpringCloud Gateway中內置了多種不同的全局過濾器,如下所示。 (1)在服務網關模塊shop-gateway模塊下的io.binghe.shop.config包下新建GatewayFilterConfig類,并在類中配置幾個全局過濾器,如下所示。 /** * @author binghe * @version 1.0.0 * @description 網關過濾器配置 */@Configuration@Slf4jpublic class GatewayFilterConfig { @Bean @Order(-1) public GlobalFilter globalFilter() { return (exchange, chain) -> { log.info("執行前置過濾器邏輯"); return chain.filter(exchange).then(Mono.fromRunnable(() -> { log.info("執行后置過濾器邏輯"); })); }; }} 注意:@Order注解中的數字越小,執行的優先級越高。 (2)啟動用戶微服務與服務網關,在瀏覽器中訪問http://localhost:10001/server-user/user/get/1001,如下所示。 在服務網關終端輸出如下信息。 執行前置過濾器邏輯執行后置過濾器邏輯 說明我們演示的全局過濾器生效了。 SpringCloud Gateway內置了很多全局過濾器,一般情況下能夠滿足實際開發需要,但是對于某些特殊的業務場景,還是需要我們自己實現自定義全局過濾器。 這里,我們就模擬實現一個獲取客戶端訪問信息,并統計訪問接口時長的全局過濾器。 (1)在網關服務模塊shop-order的io.binghe.shop.filter包下,新建GlobalGatewayLogFilter類,實現org.springframework.cloud.gateway.filter.GlobalFilter接口和org.springframework.core.Ordered接口,代碼如下所示。 /** * @author binghe * @version 1.0.0 * @description 自定義全局過濾器,模擬實現獲取客戶端信息并統計接口訪問時長 */@Slf4j@Componentpublic class GlobalGatewayLogFilter implements GlobalFilter, Ordered { /** * 開始訪問時間 */ private static final String BEGIN_VISIT_TIME = "begin_visit_time"; @Override public Mono 上述代碼的實現邏輯還是比較簡單的,這里就不再贅述了。 (2)啟動用戶微服務與網關服務,在瀏覽器中輸入http://localhost:10001/server-user/user/api/filter1?name=binghe,如下所示。 接下來,查看服務網關的終端日志,可以發現已經輸出了如下信息。 訪問接口主機: localhost訪問接口端口: 10001訪問接口URL: /server-user/user/api/filter1訪問接口URL參數: name=binghe訪問接口時長: 126ms 說明我們自定義的全局過濾器生效了。
X 關閉
X 關閉