SpringCloud Gateway 動態路由【篇1】Nacos 配置
本節開始介紹 SpringCloud Gateway 中動態路由的實現方法,包括:
- Nacos 集成動態路由配置,更新配置文件即自動更新路由
- MySQL + 二級緩存實現,主要基於 Gateway 的一些特性進行重寫,實現路由信息的自動更新
這篇文章主要介紹第一種方式:將配置文件放到 Nacos 進行託管,網關服務通過引入 Nacos 而自動更新路由配置信息。實現較爲簡單。
本節代碼在: https://github.com/laolunsi/spring-boot-examples ,參考例 23 即可。
下面進入正題。
1. 創建網關服務
創建一個 springboot gateway 網關服務,默認是從 yaml 文件中讀取 route 的配置。如果想要從 nacos 中讀取配置,就要引入 nacos-config
的依賴,並設置配置文件的地址。
首先創建一個空 maven 項目 spring-cloud-gateway-nacos-routes
,聲明 springboot 和 springcloud 的版本,並引入 nacos。
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.eknown</groupId> <artifactId>spring-cloud-gateway-nacos-routes</artifactId> <description>SpringCloud Gateway Nacos 動態路由示例</description> <version>1.0-SNAPSHOT</version> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR6</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>0.9.0.RELEASE</version> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
然後創建一個 springboot 項目,命名爲: gateway-demo
,引入 gateway/nacos-config 等依賴:
<parent> <groupId>com.eknown</groupId> <artifactId>spring-cloud-gateway-nacos-routes</artifactId> <version>1.0-SNAPSHOT</version> <relativePath/> <!-- lookup parent from repository --> </parent> <artifactId>gateway-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>gateway-demo</name> <description>Demo project for Spring Boot</description> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/cn.hutool/hutool-core --> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-core</artifactId> <version>5.3.9</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-alibaba-nacos-config</artifactId> <version>0.9.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis-reactive</artifactId> </dependency> </dependencies>
我們知道 SpringBoot 加載配置文件的順序是:bootstrap.yml -> application.yml 這裏需要將 nacos-config 的配置項放到 bootstrap.yml 中,這樣項目啓動時才能優先從 nacos 中加載配置信息。主要配置如下:
server: port: 8501 spring: application: name: gateway-demo cloud: nacos: discovery: server-addr: 127.0.0.1:8848 config: group: ${gateway.dynamicRoute.group} file-extension: json server-addr: 127.0.0.1:8848 gateway: discovery: locator: enabled: true redis: host: localhost password: port: 6379 database: 10 # 自定義的配置項,用於設置路由信息所載的配置文件,比如這裏是 group + dataId gateway: dynamicRoute: enabled: true dataType: nacos dataId: 'yq_routes' group: 'YQ_GATEWAY'
將自定義配置項加載到 Java 中:
@Configuration public class GatewayConfig { public static String NACOS_DATA_ID; public static String NACOS_GROUP_ID; @Value("${gateway.dynamicRoute.dataId}") public void setNacosDataId(String dataId) { NACOS_DATA_ID = dataId; } @Value("${gateway.dynamicRoute.group}") public void setNacosGroupId(String group) { NACOS_GROUP_ID = group; } }
下面我們重寫一下 RouteDefinitionRepository 接口,將其改造成使用 Nacos 引入路由配置信息就可以了!
public class NacosRouteDefinitionRepository implements RouteDefinitionRepository { private static final Logger log = LoggerFactory.getLogger(NacosRouteDefinitionRepository.class); // 更新路由信息需要的 private ApplicationEventPublisher publisher; // nacos 的配置信息 private NacosConfigProperties nacosConfigProperties; // 構造器 public NacosRouteDefinitionRepository(ApplicationEventPublisher publisher, NacosConfigProperties nacosConfigProperties) { this.publisher = publisher; this.nacosConfigProperties = nacosConfigProperties; System.out.println(GatewayConfig.NACOS_DATA_ID + ", " + GatewayConfig.NACOS_GROUP_ID); addListener(); } @Override public Flux<RouteDefinition> getRouteDefinitions() { try { String content = nacosConfigProperties.configServiceInstance() .getConfig(GatewayConfig.NACOS_DATA_ID, GatewayConfig.NACOS_GROUP_ID,5000); List<RouteDefinition> routeDefinitions = getListByStr(content); return Flux.fromIterable(routeDefinitions); } catch (NacosException e) { log.error("getRouteDefinitions by nacos error", e); } return Flux.fromIterable(CollUtil.newArrayList()); } /** * 添加Nacos監聽 */ private void addListener() { try { nacosConfigProperties.configServiceInstance().addListener(GatewayConfig.NACOS_DATA_ID, GatewayConfig.NACOS_GROUP_ID, new Listener() { @Override public Executor getExecutor() { return null; } @Override public void receiveConfigInfo(String configInfo) { log.info("自動更新配置...\r\n" + configInfo); publisher.publishEvent(new RefreshRoutesEvent(this)); } }); } catch (NacosException e) { log.error("nacos-addListener-error", e); } } @Override public Mono<Void> save(Mono<RouteDefinition> route) { return null; } @Override public Mono<Void> delete(Mono<String> routeId) { return null; } // 從 json 中解析出路由配置信息 —— 所以配置文件的格式一定要寫對! private List<RouteDefinition> getListByStr(String content) { if (StrUtil.isNotEmpty(content)) { return JSONObject.parseArray(content, RouteDefinition.class); } return new ArrayList<>(0); } }
最簡單的方法是直接將這個 NacosRouteDefinitionRepository
類聲明爲 Bean。這裏爲了方便配置的切換寫的複雜一點,通過 DynamicRouteConfig
來確認是否需要引入 nacos 的配置:
/** * 動態路由配置 */ @Configuration @ConditionalOnProperty(prefix = "gateway.dynamicRoute", name = "enabled", havingValue = "true") public class DynamicRouteConfig { @Autowired private ApplicationEventPublisher publisher; /** * Nacos實現方式 */ @Configuration @ConditionalOnProperty(prefix = "gateway.dynamicRoute", name = "dataType", havingValue = "nacos", matchIfMissing = true) public class NacosDynRoute { @Autowired private NacosConfigProperties nacosConfigProperties; @Bean public NacosRouteDefinitionRepository nacosRouteDefinitionRepository() { return new NacosRouteDefinitionRepository(publisher, nacosConfigProperties); } } }
到這一步我們其實已經基本完成了 gateway + nacos 動態網關的實現了。下面我們測試一下。
2. 測試
創建一個簡單的 springboot web 項目,引入 nacos 作爲註冊中心,並實現一個接口,比如:
server: port: 8502 spring: application: name: demo cloud: nacos: discovery: server-addr: 127.0.0.1:8848
接口:
@RestController @RequestMapping(value = "test") public class TestAction { @GetMapping(value = "hello") public String hello(String name) { System.out.println("hello, " + name); return "hello, " + name; } }
啓動 nacos,添加配置文件,需要設置配置文件的 group 和 dataId 與上面的 gateway.dynamicRoute
中的配置相同。
內容示例:
[ { "id": "demo", "uri": "lb://demo", "predicates": [ { "name": "Path", "args": { "pattern": "/api/demo/**" } } ], "filters": [ { "name": "StripPrefix", "args": { "parts": "2" } } ] } ]
啓動 gateway-demo 以及 demo 項目,訪問 gateway-demo 項目對應的路由地址,比如這裏的: http://localhost:8501/api/demo/test/hello
,如果運行正常,請求將被轉發到 demo 項目的 /test/hello
接口,並返回對應的數據。
今天的分享就到這裏啦!本來打算寫一篇前文所說的第二種實現方法的文章的,不過由於二級緩存那邊是用了組裏封裝的框架所以不便公佈,後面有空我會自己實現一個基本的 demo,並同步到 Git 上!
水平有限,如有紕漏還請見諒。如果對文章內容有什麼疑問,可以留言或直接聯繫我!thanks for your reading !
參考資料: