nacos动态配置数据源_Nacos+Spring Cloud Gateway动态路由配置

本文介绍了如何在Nacos环境下配置和使用Spring Cloud Gateway的动态路由。通过创建Nacos配置中心,设置路由规则并监听配置变化,实现在运行时动态更新路由。文章详细展示了配置过程、项目结构、服务间的调用以及测试步骤,强调了动态路由配置的灵活性和实用性。
摘要由CSDN通过智能技术生成

前言

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

@EnableDiscoveryClientpublic classGatewayApplication

{public static voidmain( String[] args )

{

SpringApplication.run(GatewayApplication.class, args);

}

}

二、项目构建

1、项目结构

创建简单的spring boot多模块结构,推荐使用idea创建

1)Nacos父模块:

com.springcloud

nacos

0.0.1-SNAPSHOT

nacos

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相关组件依赖

org.springframework.boot

spring-boot-dependencies

${spring-boot.version}

pom

import

org.springframework.cloud

spring-cloud-dependencies

${spring-cloud.version}

pom

import

其它组件依赖引入(修正:如果引入了nacos-api相关的JSON依赖,那么fastjson就不需要再引入了,否则可能冲突):

com.alibaba

fastjson

${fastjson.version}

org.projectlombok

lombok

${lombok.version}

provided

org.slf4j

slf4j-api

${slf4j.version}

org.springframework.boot

spring-boot-starter-actuator

注意,这里有个坑,spring cloud gateway使用的web框架为webflux,和springMVC不兼容。所以不要引入(修正:只有gateway服务不用引入springMVC,其他需要引入)

org.springframework.boot

spring-boot-starter-web

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获取配置

/*** 路由类配置*/@Configurationpublic classGatewayConfig {public static final long DEFAULT_TIMEOUT = 30000;public staticString NACOS_SERVER_ADDR;public staticString NACOS_NAMESPACE;public staticString NACOS_ROUTE_DATA_ID;public staticString NACOS_ROUTE_GROUP;

@Value("${spring.cloud.nacos.discovery.server-addr}")public voidsetNacosServerAddr(String nacosServerAddr){

NACOS_SERVER_ADDR=nacosServerAddr;

}

@Value("${spring.cloud.nacos.discovery.namespace}")public voidsetNacosNamespace(String nacosNamespace){

NACOS_NAMESPACE=nacosNamespace;

}

@Value("${nacos.gateway.route.config.data-id}")public voidsetNacosRouteDataId(String nacosRouteDataId){

NACOS_ROUTE_DATA_ID=nacosRouteDataId;

}

@Value("${nacos.gateway.route.config.group}")public voidsetNacosRouteGroup(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 classDynamicRouteServiceImplByNacos {

@AutowiredprivateDynamicRouteServiceImpl dynamicRouteService;privateConfigService configService;

@PostConstructpublic voidinit() {

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 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下发的动态路由配置

*@paramdataId

*@paramgroup*/

public voiddynamicRouteByNacosListener (String dataId, String group){try{

configService.addListener(dataId, group,newListener() {

@Overridepublic voidreceiveConfigInfo(String configInfo) {

log.info("进行网关更新:\n\r{}",configInfo);

List definitionList = JSON.parseArray(configInfo, RouteDefinition.class);for(RouteDefinition definition : definitionList){

log.info("update route : {}",definition.toString());

dynamicRouteService.update(definition);

}

}

@OverridepublicExecutor getExecutor() {

log.info("getExecutor\n\r");return null;

}

});

}catch(NacosException e) {

log.error("从nacos接收动态路由配置出错!!!",e);

}

}/*** 初始化网关路由 nacos config

*@return

*/

privateConfigService initConfigService(){try{

Properties properties= newProperties();

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

@Servicepublic class DynamicRouteServiceImpl implementsApplicationEventPublisherAware {

@AutowiredprivateRouteDefinitionWriter routeDefinitionWriter;/*** 发布事件*/@AutowiredprivateApplicationEventPublisher publisher;

@Overridepublic voidsetApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.publisher =applicationEventPublisher;

}/*** 删除路由

*@paramid

*@return

*/

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

}

}/*** 更新路由

*@paramdefinition

*@return

*/

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

}

}/*** 增加路由

*@paramdefinition

*@return

*/

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

@Slf4jpublic classConsumeController {

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

@Slf4jpublic classProviderController {

@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网关配置文件内容,并进行正确路由加载...

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进行了监听

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

查看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下,需要到微服务自行获取。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值