天天看點

Nacos+Spring Cloud Gateway動态路由配置nacos配置中心配置建議在bootstrap.properties中配置spring.cloud.nacos.config.file-extension=properties配置中心的命名空間:dev 的命名空間(環境)

Nacos+Spring Cloud Gateway動态路由配置

前言

  Nacos最近項目一直在使用,其簡單靈活,支援更細粒度的指令空間,分組等為麻煩複雜的環境切換提供了友善;同時也很好支援動态路由的配置,隻需要簡單的幾步即可。在國産的注冊中心、配置中心中比較突出,容易上手,本文通過gateway、nacos-consumer、nacos-provider三個簡單子產品來展示:Nacos下動态路由配置。

一、Nacos環境準備

1、啟動Nacos配置中心并建立路由配置

具體的Nacos怎麼配置就不介紹了,可以參考阿裡巴巴的官方介紹,這裡通過windows直接本地啟動開啟單機模式,登入Nacos Console,建立dev的namespace,在dev下的預設分組下建立gateway-router的dataId

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

[{

"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"           

}]

2、連接配接Nacos配置中心

通常在項目中配置“配置中心”往往都是在bootstrap.propertis(yaml)中配置,這樣才能保證項目中路由配置從Nacos Config中讀取。

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 Config

@SpringBootApplication

@EnableDiscoveryClient

public class GatewayApplication

{

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

}

二、項目建構

1、項目結構

建立簡單的spring boot多子產品結構,推薦使用idea建立

1)Nacos父子產品:

com.springcloud

nacos

0.0.1-SNAPSHOT

Nacos Demo

首先pom檔案引入Spring Cloud Alibaba Nacos元件:注冊中心nacos-discovery與配置中心nacos-config

  com.alibaba.cloud

  spring-cloud-starter-alibaba-nacos-discovery

  ${alibaba-nacos.version}

  com.alibaba.cloud

  spring-cloud-starter-alibaba-nacos-config

  ${alibaba-nacos.version}

其次再引入Spring Cloud相關元件依賴

<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>           

其它元件依賴引入(修正:如果引入了nacos-api相關的JSON依賴,那麼fastjson就不需要再引入了,否則可能沖突):

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

注意,這裡有個坑,spring cloud gateway使用的web架構為webflux,和springMVC不相容。是以不要引入(修正:隻有gateway服務不用引入springMVC,其他需要引入)

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

2)三個子子產品:gateway、nacos-consumer、nacos-provider

  nacos-provider

  nacos-consumer

  gateway

結構截圖如下所示:

3)三個服務的端口分别為:

  nacos-consume:6001

nacos-provider:6002

gateway:6003

4)服務架構如下:

2、編寫測試代碼

(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操作該類的方法。該類提供新增路由、更新路由、删除路由,然後實作釋出的功能。

@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/")

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/")

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";
}           

三、測試動态網關配置

1、啟動服務,觀察注冊中心

分别啟動gateway、nacos-consumer、nacos-provider三個服務,觀察是否已經在Nacos上正确注冊

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

2、通路網關,觀察服務日志

(1)檢視gateway服務的初始化啟動日志:會發現可以正常從Nacos擷取配置gateway-router網關配置檔案内容,并進行正确路由加載...

 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服務日志,有沒有監聽,并且進行正确的路由更新:如下日志所示,最新路由配置立馬被列印,并且進行正确路由更新

其實,還有辦法可以知道我們的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頁面

四、總結

1)Spring Cloud Gateway作用不光隻是簡單的跳轉重定向,還可以實作使用者的驗證登入,解決跨域,日志攔截,權限控制,限流,熔斷,負載均衡,黑名單和白名單機制等。是微服務架構不二的選擇;

2)Nacos的配置中心支援動态擷取配置檔案,可以将一些全局的經常變更的配置檔案放在Nacos下,需要到微服務自行擷取。

原文位址

https://www.cnblogs.com/jian0110/p/12862569.html