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