1.前言
網關中有兩個重要的概念,那就是路由配置和路由規則,路由配置是指配置某請求路徑路由到指定的目的地址。而路由規則是指匹配到路由配置之后,再根據路由規則進行轉發處理。
Spring Cloud Gateway作為所有請求流量的入口,在實際生產環境中為了保證高可靠和高可用,盡量避免重啟,需要實現Spring Cloud Gateway動態路由配置。前面章節介紹了Spring Cloud Gateway提供的兩種方法去配置路由規則,但都是在Spring Cloud Gateway啟動時候,就將路由配置和規則加載到內存里,無法做到不重啟網關就可以動態的對應路由的配置和規則進行增加,修改和刪除。本篇文章簡單介紹如何實現Spring Cloud Gateway的動態路由。
2. Spring Cloud Gateway簡單的動態路由實現
Spring Cloud Gateway的官方文檔並沒有講如何動態配置,查看 Spring Cloud Gateway的源碼,發現在org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint類中提供了動態配置的Rest接口,但是需要開啟Gateway的端點,而且提供的功能不是很強大。通過參考和GatewayControllerEndpoint相關的代碼,可以自己編碼實際動態路由配置。
下面通過案例的方式去講解怎么實現Gateway的動態路由配置。案例工程如ch18-7-gateway所示。
代碼地址:https://github.com/SpringCloud/spring-cloud-code/blob/master/ch18-7/ch18-7-gateway
3. 簡單動態路由的實現
3.1 新建Maven工程ch18-7-gateway
配置主要的核心依賴如代碼清單18-33所示:
代碼清單: ch18-7/ch18-7-gateway/pom.xml
<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency></dependencies>
3.2 根據Spring Cloud Gateway的路由模型定義數據傳輸模型
分別創建GatewayRouteDefinition.java, GatewayPredicateDefinition.java, GatewayFilterDefinition.java這三個類。
(1) 創建路由定義模型如下代碼清單18-34所示:
代碼清單 18-34: ch18-7/ch18-7-gateway/src/main/java/cn/springcloud/book/gateway/model/GatewayRouteDefinition.java
public class GatewayRouteDefinition {//路由的Idprivate String id;//路由斷言集合配置private List<GatewayPredicateDefinition> predicates = new ArrayList<>();//路由過濾器集合配置private List<GatewayFilterDefinition> filters = new ArrayList<>();//路由規則轉發的目標uriprivate String uri;//路由執行的順序private int order = 0;//此處省略get和set方法}
(2)創建過濾器定義模型,代碼如代碼清單18-35所示:
代碼清單18-35: ch18-7/ch18-7-gateway/src/main/java/cn/springcloud/book/gateway/model/GatewayFilterDefinition.java
public class GatewayFilterDefinition {//Filter Nameprivate String name;//對應的路由規則private Map<String, String> args = new LinkedHashMap<>();//此處省略Get和Set方法}
(3)路由斷言定義模型,代碼如代碼清單18-36所示:
代碼清單18-36: ch18-7/ch18-7-gateway/src/main/java/cn/springcloud/book/gateway/model/GatewayPredicateDefinition.java
public class GatewayPredicateDefinition {//斷言對應的Nameprivate String name;//配置的斷言規則private Map<String, String> args = new LinkedHashMap<>();//此處省略Get和Set方法}
3.3 編寫動態路由實現類
編寫DynamicRouteServiceImpl並實現ApplicationEventPublisherAware接口,代碼如代碼清單18-37所示: ch18-37/ch18-7-gateway/src/main/java/cn/springcloud/book/gateway/route/DynamicRouteServiceImpl.java
@Servicepublic class DynamicRouteServiceImpl implements ApplicationEventPublisherAware {@Autowiredprivate RouteDefinitionWriter routeDefinitionWriter;private ApplicationEventPublisher publisher;//增加路由public String add(RouteDefinition definition) {routeDefinitionWriter.save(Mono.just(definition)).subscribe();this.publisher.publishEvent(new RefreshRoutesEvent(this));return "success";}//更新路由public String update(RouteDefinition definition) {try {this.routeDefinitionWriter.delete(Mono.just(definition.getId()));} catch (Exception e) {return "update fail,not find route routeId: "+definition.getId();}try {routeDefinitionWriter.save(Mono.just(definition)).subscribe();this.publisher.publishEvent(new RefreshRoutesEvent(this));return "success";} catch (Exception e) {return "update route fail";}}//刪除路由public Mono<ResponseEntity<Object>> delete(String id) {return this.routeDefinitionWriter.delete(Mono.just(id)).then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build()))).onErrorResume(t -> t instanceof NotFoundException, t -> Mono.just(ResponseEntity.notFound().build()));}@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.publisher = applicationEventPublisher;}}
3.4 編寫Rest接口
編寫RouteController類的提供Rest接口,用於動態路由配置。代碼如代碼清單18-38所示:
代碼清單 18-38: ch18-7/ch18-7-gateway/src/main/java/cn/springcloud/book/gateway/controller/RouteController.java
@RestController@RequestMapping("/route")public class RouteController {@Autowiredprivate DynamicRouteServiceImpl dynamicRouteService;//增加路由@PostMapping("/add")public String add(@RequestBody GatewayRouteDefinition gwdefinition) {try {RouteDefinition definition = assembleRouteDefinition(gwdefinition);return this.dynamicRouteService.add(definition);} catch (Exception e) {e.printStackTrace();}return "succss";}//刪除路由@DeleteMapping("/routes/{id}")public Mono<ResponseEntity<Object>> delete(@PathVariable String id) {return this.dynamicRouteService.delete(id);}//更新路由@PostMapping("/update")public String update(@RequestBody GatewayRouteDefinition gwdefinition) {RouteDefinition definition = assembleRouteDefinition(gwdefinition);return this.dynamicRouteService.update(definition);}}
3.5 配置application.yml文件
在application.yml文件配置應用的配置信息,並開啟Spring Cloud Gateway對外提供的端點Rest接口。代碼如代碼清單18-39所示:
代碼清單 18-39: ch18-7/ch18-7-gateway/src/main/resources/application.yml
配置輸出日志如下所示:
# 配置輸出日志logging:level:org.springframework.cloud.gateway: TRACEorg.springframework.http.server.reactive: DEBUGorg.springframework.web.reactive: DEBUGreactor.ipc.netty: DEBUG#開啟端點management:endpoints:web:exposure:include: '*'security:enabled: false
3.6 啟動ch18-7-gateway應用測試
(1) 啟動ch18-7-gateway應用之后,由於開啟了端點,首先打開瀏覽器訪問端點URL:
http://localhost:8080/actuator/gateway/routes ,查看路由信息返回為空,如下圖所示:

(2)打開PostMan,訪問http://localhost:8080/route/add, 發起Post請求,如下圖所示,返回success說明向Gateway增加路由配置成功。
然后再打開PostMan訪問端點URL:http://localhost:8080/actuator/gateway/routes ,
查看路由信息返回如下圖所示,可以看到已經添加的路由配置。
(3) 打開瀏覽器訪問http://localhost:8080/jd, 可以正常轉發https://www.jd.com/對應的京東商城首頁。
(4) 通過訪問http://localhost:8080/route/update, 對id為jd_route的路由更新配置,如下圖所示:
然后再訪問路由端點URL,發現路由配置已經被更新,如下圖所示:
然后通過瀏覽器訪問http://localhost:8080/taobao ,可以成功轉發到淘寶網。
(5) 通過訪問http: //localhost:8080/route/delete/jd_route,其中的id為路由對應的id,刪除路由結果如下圖所示:
