Nacos+Spring Cloud Gateway動態路由配置

前言

  Nacos最近項目一直在使用,其簡單靈活,支持更細粒度的命令空間,分組等爲麻煩複雜的環境切換提供了方便;同時也很好支持動態路由的配置,只須要簡單的幾步便可。在國產的註冊中心、配置中心中比較突出,容易上手,本文經過gateway、nacos-consumer、nacos-provider三個簡單模塊來展現:Nacos下動態路由配置。git

  


1、Nacos環境準備

一、啓動Nacos配置中心並建立路由配置

具體的Nacos怎麼配置就不介紹了,能夠參考阿里巴巴的官方介紹,這裏經過windows直接本地啓動開啓單機模式,登陸Nacos Console,建立dev的namespace,在dev下的默認分組下建立gateway-router的dataIdgithub

gateway-router的主要初始化配置以下:關於gateway的組成(id,order、predicates斷言,uri)這裏就不詳細說明的了,能夠自行百度下web

[{ "id": "consumer-router", "order": 0, "predicates": [{ "args": { "pattern": "/consume/**" }, "name": "Path" }], "uri": "lb://nacos-consumer" },{ "id": "provider-router", "order": 2, "predicates": [{ "args": { "pattern": "/provide/**" }, "name": "Path" }], "uri": "lb://nacos-provider" }]

二、鏈接Nacos配置中心

一般在項目中配置「配置中心」每每都是在bootstrap.propertis(yaml)中配置,這樣才能保證項目中路由配置從Nacos Config中讀取。spring

# nacos配置中心配置建議在bootstrap.properties中配置 spring.cloud.nacos.config.server-addr=127.0.0.1:8848 #spring.cloud.nacos.config.file-extension=properties # 配置中心的命名空間:dev 的命名空間(環境) spring.cloud.nacos.config.namespace=08ecd1e5-c042-410a-84d5-b0a8fbeed8ea

Application啓動類中增長註解@EnableDiscoveryClient,才能保證鏈接到Nacos Configjson

@SpringBootApplication @EnableDiscoveryClient public class GatewayApplication { public static void main( String[] args ) { SpringApplication.run(GatewayApplication.class, args); } }

 

2、項目構建

一、項目結構

建立簡單的spring boot多模塊結構,推薦使用idea建立bootstrap

1)Nacos父模塊:windows

<groupId>com.springcloud</groupId> <artifactId>nacos</artifactId> <version>0.0.1-SNAPSHOT</version> <name>nacos</name> <description>Nacos Demo</description>

首先pom文件引入Spring Cloud Alibaba Nacos組件:註冊中心nacos-discovery與配置中心nacos-configapi

 <!--nacos 客戶端 註冊中心-->  <dependency>   <groupId>com.alibaba.cloud</groupId>   <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>   <version>${alibaba-nacos.version}</version>  </dependency>  <!--nacos 客戶端 配置中心-->  <dependency>   <groupId>com.alibaba.cloud</groupId>   <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>   <version>${alibaba-nacos.version}</version>  </dependency>

其次再引入Spring Cloud相關組件依賴跨域

<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <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>

其它組件依賴引入:架構

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>${fastjson.version}</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>${lombok.version}</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>${slf4j.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

注意,這裏有個坑,spring cloud gateway使用的web框架爲webflux,和springMVC不兼容。因此不要引入

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

2)三個子模塊:gateway、nacos-consumer、nacos-provider

<modules>   <module>nacos-provider</module>   <module>nacos-consumer</module>   <module>gateway</module> </modules>

結構截圖以下所示:

 

3)三個服務的端口分別爲:

  nacos-consume:6001

  nacos-provider:6002

  gateway:6003

4)服務架構以下:

 

                    

 

二、編寫測試代碼

(1)在gateway模塊中主要實現如下功能:

第一,從Nacos配置中心中加載動態路由的相關配置,就須要讀取Nacos的命名空間namespace,經過dataId獲取配置

/** * 路由類配置 */ @Configuration public class GatewayConfig { public static final long DEFAULT_TIMEOUT = 30000; public static String NACOS_SERVER_ADDR; public static String NACOS_NAMESPACE; public static String NACOS_ROUTE_DATA_ID; public static String NACOS_ROUTE_GROUP; @Value("${spring.cloud.nacos.discovery.server-addr}") public void setNacosServerAddr(String nacosServerAddr){ NACOS_SERVER_ADDR = nacosServerAddr; } @Value("${spring.cloud.nacos.discovery.namespace}") public void setNacosNamespace(String nacosNamespace){ NACOS_NAMESPACE = nacosNamespace; } @Value("${nacos.gateway.route.config.data-id}") public void setNacosRouteDataId(String nacosRouteDataId){ NACOS_ROUTE_DATA_ID = nacosRouteDataId; } @Value("${nacos.gateway.route.config.group}") public void setNacosRouteGroup(String nacosRouteGroup){ NACOS_ROUTE_GROUP = nacosRouteGroup; } }

properties配置關於Nacos下讀取gateway-router的配置:

spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.cloud.nacos.discovery.namespace=08ecd1e5-c042-410a-84d5-b0a8fbeed8ea
nacos.gateway.route.config.data-id=gateway-router
nacos.gateway.route.config.group=DEFAULT_GROUP

第二,初始化路由,監聽動態路由配置的數據源變化;

/** * * 經過nacos下發動態路由配置,監聽Nacos中gateway-route配置 * */ @Component @Slf4j @DependsOn({"gatewayConfig"}) // 依賴於gatewayConfig bean
public class DynamicRouteServiceImplByNacos { @Autowired private DynamicRouteServiceImpl dynamicRouteService; private ConfigService configService; @PostConstruct public void init() { log.info("gateway route init..."); try{ configService = initConfigService(); if(configService == null){ log.warn("initConfigService fail"); return; } String configInfo = configService.getConfig(GatewayConfig.NACOS_ROUTE_DATA_ID, GatewayConfig.NACOS_ROUTE_GROUP, GatewayConfig.DEFAULT_TIMEOUT); log.info("獲取網關當前配置:\r\n{}",configInfo); List<RouteDefinition> definitionList = JSON.parseArray(configInfo, RouteDefinition.class); for(RouteDefinition definition : definitionList){ log.info("update route : {}",definition.toString()); dynamicRouteService.add(definition); } } catch (Exception e) { log.error("初始化網關路由時發生錯誤",e); } dynamicRouteByNacosListener(GatewayConfig.NACOS_ROUTE_DATA_ID,GatewayConfig.NACOS_ROUTE_GROUP); } /** * 監聽Nacos下發的動態路由配置 * @param dataId * @param group */
    public void dynamicRouteByNacosListener (String dataId, String group){ try { configService.addListener(dataId, group, new Listener() { @Override public void receiveConfigInfo(String configInfo) { log.info("進行網關更新:\n\r{}",configInfo); List<RouteDefinition> definitionList = JSON.parseArray(configInfo, RouteDefinition.class); for(RouteDefinition definition : definitionList){ log.info("update route : {}",definition.toString()); dynamicRouteService.update(definition); } } @Override public Executor getExecutor() { log.info("getExecutor\n\r"); return null; } }); } catch (NacosException e) { log.error("從nacos接收動態路由配置出錯!!!",e); } } /** * 初始化網關路由 nacos config * @return
     */
    private ConfigService initConfigService(){ try{ Properties properties = new Properties(); properties.setProperty("serverAddr",GatewayConfig.NACOS_SERVER_ADDR); properties.setProperty("namespace",GatewayConfig.NACOS_NAMESPACE); return configService= NacosFactory.createConfigService(properties); } catch (Exception e) { log.error("初始化網關路由時發生錯誤",e); return null; } } }

第三,刷新最新的動態路由變化,實現動態增刪改路由

/** * 動態更新路由網關service * 1)實現一個Spring提供的事件推送接口ApplicationEventPublisherAware * 2)提供動態路由的基礎方法,可經過獲取bean操做該類的方法。該類提供新增路由、更新路由、刪除路由,而後實現發佈的功能。 */ @Slf4j @Service public class DynamicRouteServiceImpl implements ApplicationEventPublisherAware { @Autowired private RouteDefinitionWriter routeDefinitionWriter; /** * 發佈事件 */ @Autowired private ApplicationEventPublisher publisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.publisher = applicationEventPublisher; } /** * 刪除路由 * @param id * @return
     */
    public String delete(String id) { try { log.info("gateway delete route id {}",id); this.routeDefinitionWriter.delete(Mono.just(id)); return "delete success"; } catch (Exception e) { return "delete fail"; } } /** * 更新路由 * @param definition * @return
     */
    public String update(RouteDefinition definition) { try { log.info("gateway update route {}",definition); 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"; } } /** * 增長路由 * @param definition * @return
     */
    public String add(RouteDefinition definition) { log.info("gateway add route {}",definition); routeDefinitionWriter.save(Mono.just(definition)).subscribe(); this.publisher.publishEvent(new RefreshRoutesEvent(this)); return "success"; } }

(2)在consumer建立ConsumeController:經過訪問gateway網關/consume/sayHello/{name}("pattern": "/consume/**"),跳轉至nacos-consumer服務("uri": "lb://nacos-consumer"),

@RequestMapping("/consume/") @Slf4j public class ConsumeController { @GetMapping("/sayHello/{name}") public String sayHello(@PathVariable("name") String name){ log.info("I'm calling nacos-consumer service by dynamic gateway..."); return name + " Hi~, I'm from nacos-consumer"; } }

(3)在provider建立ProviderController:經過訪問gateway網關/provide/sayHello/{name}("pattern": "/provide/**"),跳轉至nacos-provider服務("uri": "lb://nacos-provider")

@RestController @RequestMapping("/provide/") @Slf4j public class ProviderController { @GetMapping("/sayHello/{name}") public String sayHello(@PathVariable("name") String name){ log.info("I'm calling nacos-provider service by dynamic gateway..."); return name + " Hi~, I'm from nacos-provider"; } }

3、測試動態網關配置

一、啓動服務,觀察註冊中心

分別啓動gateway、nacos-consumer、nacos-provider三個服務,觀察是否已經在Nacos上正確註冊

注意:須要指定註冊中心的namespace爲dev的空間,即spring.cloud.nacos.discovery.namespace=08ecd1e5-c042-410a-84d5-b0a8fbeed8ea

二、訪問網關,觀察服務日誌

(1)查看gateway服務的初始化啓動日誌:會發現能夠正常從Nacos獲取配置gateway-router網關配置文件內容,並進行正確路由加載...

 

2020-05-10 14:33:44.557  INFO 1272 --- [ main] c.g.r.DynamicRouteServiceImplByNacos : gateway route init... 2020-05-10 14:33:44.578  INFO 1272 --- [ main] c.g.r.DynamicRouteServiceImplByNacos : 獲取網關當前配置: [{ "id": "consumer-router", "order": 0, "predicates": [{ "args": { "pattern": "/consume/**" }, "name": "Path" }], "uri": "lb://nacos-consumer" },{ "id": "provider-router", "order": 2, "predicates": [{ "args": { "pattern": "/provide/**" }, "name": "Path" }], "uri": "lb://nacos-provider" }] 2020-05-10 14:33:44.691  INFO 1272 --- [           main] c.g.r.DynamicRouteServiceImplByNacos     : update route : RouteDefinition{id='consumer-router', predicates=[PredicateDefinition{name='Path', args={pattern=/consume/**}}], filters=[], uri=lb://nacos-consumer, order=0, metadata={}} 2020-05-10 14:33:44.691 INFO 1272 --- [ main] c.g.service.DynamicRouteServiceImpl : gateway add route RouteDefinition{id='consumer-router', predicates=[PredicateDefinition{name='Path', args={pattern=/consume/**}}], filters=[], uri=lb://nacos-consumer, order=0, metadata={}} 2020-05-10 14:33:45.192 INFO 1272 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [After] 2020-05-10 14:33:45.192 INFO 1272 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Before] 2020-05-10 14:33:45.192 INFO 1272 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Between] 2020-05-10 14:33:45.193 INFO 1272 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Cookie] 2020-05-10 14:33:45.193 INFO 1272 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Header] 2020-05-10 14:33:45.193 INFO 1272 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Host] 2020-05-10 14:33:45.194 INFO 1272 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Method] 2020-05-10 14:33:45.194 INFO 1272 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Path] 2020-05-10 14:33:45.194 INFO 1272 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Query] 2020-05-10 14:33:45.194 INFO 1272 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [ReadBodyPredicateFactory] 2020-05-10 14:33:45.194 INFO 1272 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [RemoteAddr] 2020-05-10 14:33:45.194 INFO 1272 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Weight] 2020-05-10 14:33:45.194 INFO 1272 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [CloudFoundryRouteService] 2020-05-10 14:33:45.335 INFO 1272 --- [ main] c.g.r.DynamicRouteServiceImplByNacos : update route : RouteDefinition{id='provider-router', predicates=[PredicateDefinition{name='Path', args={pattern=/provide/**}}], filters=[], uri=lb://nacos-provider, order=2, metadata={}} 2020-05-10 14:33:45.335 INFO 1272 --- [ main] c.g.service.DynamicRouteServiceImpl : gateway add route RouteDefinition{id='provider-router', predicates=[PredicateDefinition{name='Path', args={pattern=/provide/**}}], filters=[], uri=lb://nacos-provider, order=2, metadata={}} 2020-05-10 14:33:45.336 INFO 1272 --- [ main] c.g.r.DynamicRouteServiceImplByNacos : update route : RouteDefinition{id='github-router', predicates=[PredicateDefinition{name='Path', args={pattern=/github}}], filters=[], uri=https://github.com, order=3, metadata={}} 2020-05-10 14:33:45.336 INFO 1272 --- [ main] c.g.service.DynamicRouteServiceImpl : gateway add route RouteDefinition{id='github-router', predicates=[PredicateDefinition{name='Path', args={pattern=/github}}], filters=[], uri=https://github.com, order=3, metadata={}}
View Code

 

但這隻能說明是初始化靜態路由,下面咱們改變gateway-router網關配置內容,追加github-router路由

[{ "id": "consumer-router", "order": 0, "predicates": [{ "args": { "pattern": "/consume/**" }, "name": "Path" }], "uri": "lb://nacos-consumer" },{ "id": "provider-router", "order": 2, "predicates": [{ "args": { "pattern": "/provide/**" }, "name": "Path" }], "uri": "lb://nacos-provider" },{ "id": "github-router", "order": 2, "predicates": [{ "args": { "pattern": "/github/**" }, "name": "Path" }], "uri": "https://github.com" }]

以後點擊發布更新路由配置

觀察gateway服務日誌,有沒有監聽,而且進行正確的路由更新:以下日誌所示,最新路由配置立馬被打印,而且進行正確路由更新

 

2020-05-10 14:42:27.576  INFO 1272 --- [d5-b0a8fbeed8ea] c.g.r.DynamicRouteServiceImplByNacos : 進行網關更新: [{ "id": "consumer-router", "order": 0, "predicates": [{ "args": { "pattern": "/consume/**" }, "name": "Path" }], "uri": "lb://nacos-consumer" },{ "id": "provider-router", "order": 2, "predicates": [{ "args": { "pattern": "/provide/**" }, "name": "Path" }], "uri": "lb://nacos-provider" },{ "id": "github-router", "order": 2, "predicates": [{ "args": { "pattern": "/github/**" }, "name": "Path" }], "uri": "https://github.com" }] 2020-05-10 14:42:27.576  INFO 1272 --- [d5-b0a8fbeed8ea] c.g.r.DynamicRouteServiceImplByNacos     : update route : RouteDefinition{id='consumer-router', predicates=[PredicateDefinition{name='Path', args={pattern=/consume/**}}], filters=[], uri=lb://nacos-consumer, order=0, metadata={}} 2020-05-10 14:42:27.576 INFO 1272 --- [d5-b0a8fbeed8ea] c.g.service.DynamicRouteServiceImpl : gateway update route RouteDefinition{id='consumer-router', predicates=[PredicateDefinition{name='Path', args={pattern=/consume/**}}], filters=[], uri=lb://nacos-consumer, order=0, metadata={}} 2020-05-10 14:42:27.578 INFO 1272 --- [d5-b0a8fbeed8ea] c.g.r.DynamicRouteServiceImplByNacos : update route : RouteDefinition{id='provider-router', predicates=[PredicateDefinition{name='Path', args={pattern=/provide/**}}], filters=[], uri=lb://nacos-provider, order=2, metadata={}} 2020-05-10 14:42:27.578 INFO 1272 --- [d5-b0a8fbeed8ea] c.g.service.DynamicRouteServiceImpl : gateway update route RouteDefinition{id='provider-router', predicates=[PredicateDefinition{name='Path', args={pattern=/provide/**}}], filters=[], uri=lb://nacos-provider, order=2, metadata={}} 2020-05-10 14:42:27.580 INFO 1272 --- [d5-b0a8fbeed8ea] c.g.r.DynamicRouteServiceImplByNacos : update route : RouteDefinition{id='github-router', predicates=[PredicateDefinition{name='Path', args={pattern=/github/**}}], filters=[], uri=https://github.com, order=2, metadata={}} 2020-05-10 14:42:27.580 INFO 1272 --- [d5-b0a8fbeed8ea] c.g.service.DynamicRouteServiceImpl : gateway update route RouteDefinition{id='github-router', predicates=[PredicateDefinition{name='Path', args={pattern=/github/**}}], filters=[], uri=https://github.com, order=2, metadata={}}
View Code

 

其實,還有辦法能夠知道咱們的gateway服務有沒有監聽Nacos的gateway-router配置,那就是在Nacos Console--->監聽查詢----->選擇配置---->輸入配置文件的namespace與Group: 能夠發現我本地IP地址127.0.0.1對配置文件gateway-router進行了監聽

(2)訪問gateway網關服務:http://localhost:6003/consume/sayHello/nacos

  

 

查看consumer服務日誌:

2020-05-10 14:55:07.257  INFO 6552 --- [nio-6001-exec-2] c.n.c.controller.ConsumeController       : I'm calling nacos-consumer service by dynamic gateway...

發現跳轉至consumer服務,而且訪問了consumer服務的CosnumerController

(3)訪問gateway網關服務:http://localhost:6003/provider/sayHello/nacos

  

查看provider服務日誌:

2020-05-10 14:56:56.144  INFO 10024 --- [nio-6002-exec-1] c.n.p.controller.ProviderController      : I'm calling nacos-provider service by dynamic gateway...

發現跳轉至consumer服務,而且訪問了provider服務的ProviderController

(4)訪問訪問gateway網關服務:http://localhost:6003/github,正確跳轉至github頁面

4、總結

  1)Spring Cloud Gateway做用不光只是簡單的跳轉重定向,還能夠實現用戶的驗證登陸,解決跨域,日誌攔截,權限控制,限流,熔斷,負載均衡,黑名單和白名單機制等。是微服務架構不二的選擇

  2)Nacos的配置中心支持動態獲取配置文件,能夠將一些全局的常常變動的配置文件放在Nacos下,須要到微服務自行獲取。

相關文章
相關標籤/搜索