Nacos+SpringCloudGateway动态路由配置实现步骤
⽬录
前⾔
⼀、Nacos环境准备
1、启动Nacos配置中⼼并创建路由配置
2、连接Nacos配置中⼼
⼆、项⽬构建
1、项⽬结构
2、编写测试代码
三、测试动态⽹关配置
1、启动服务,观察注册中⼼
2、访问⽹关,观察服务⽇志
四、总结
前⾔
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.fig.server-addr=127.0.0.1:8848
#spring.fig.file-extension=properties
# 配置中⼼的命名空间:dev 的命名空间(环境)
spring.fig.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、项⽬结构
创建简单的springboot多模块结构,推荐使⽤idea创建
1)Nacos⽗模块:
<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-config
<!--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>
其它组件依赖引⼊(修正:如果引⼊了nacos-api相关的JSON依赖,那么fastjson就不需要再引⼊了,否则可能冲突):
<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不兼容。所以不要引⼊(修正:只有gateway服务不⽤引⼊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)服务架构如下:
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("${fig.data-id}")
public void setNacosRouteDataId(String nacosRouteDataId){
NACOS_ROUTE_DATA_ID = nacosRouteDataId;
}
@Value("${up}")
public void setNacosRouteGroup(String nacosRouteGroup){
spring怎么读取propertiesNACOS_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
fig.data-id=gateway-router
up=DEFAULT_GROUP
第⼆,初始化路由,监听动态路由配置的数据源变化(2020.12.28解决删除路由不⽣效问题);
/**
*
* 通过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 ");
configService = initConfigService();
if(configService == null){
log.warn("initConfigService fail");
return;
}
String configInfo = Config(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 : {}",String());
dynamicRouteService.add(definition);
}
} catch (Exception e) {
<("初始化⽹关路由时发⽣错误",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);
log.info("update route : {}",String());
dynamicRouteService.updateList(definitionList);
}
@Override
public Executor getExecutor() {
log.info("getExecutor\n\r");
return null;
}
});
} catch (NacosException e) {
<("从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= ateConfigService(properties);
} catch (Exception e) {
<("初始化⽹关路由时发⽣错误",e);
return null;
}
}
}
第三,刷新最新的动态路由变化,实现动态增删改路由(2020.12.28解决删除路由不⽣效问题)
/**
* 动态更新路由⽹关service
* 1)实现⼀个Spring提供的事件推送接⼝ApplicationEventPublisherAware
* 2)提供动态路由的基础⽅法,可通过获取bean操作该类的⽅法。该类提供新增路由、更新路由、删除路由,然后实现发布的功能。
*/
@Slf4j
@Service
public class DynamicRouteServiceImpl implements ApplicationEventPublisherAware {
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
@Autowired
private RouteDefinitionLocator routeDefinitionLocator;
/**
* 发布事件
*/
@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.publisher.publishEvent(new RefreshRoutesEvent(this));
return "delete success";
} catch (Exception e) {
return "delete fail";
}
}
/**
* 更新路由
* @param definitions
* @return
*/
public String updateList(List<RouteDefinition> definitions) {
log.info("gateway update route {}",definitions);
// 删除缓存routerDefinition
List<RouteDefinition> routeDefinitionsExits = RouteDefinitions().buffer().blockFirst();
if (!CollectionUtils.isEmpty(routeDefinitionsExits)) {
routeDefinitionsExits.forEach(routeDefinition -> {
log.info("delete routeDefinition:{}", routeDefinition);
Id());
});
}
definitions.forEach(definition -> {
updateById(definition);
});
return "success";
/**
* 更新路由
* @param definition
* @return
*/
public String updateById(RouteDefinition definition) {
try {
log.info("gateway update route {}",definition);
} catch (Exception e) {
return "update fail,not find route routeId: "+Id();
}
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";
}
}
三、测试动态⽹关配置
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] DynamicRouteServiceImplByNacos : gateway
2020-05-10 14:33:44.578 INFO 1272 --- [ main] 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] DynamicRouteServiceImplByNacos : update route : RouteDefinition{id='consumer-router', predicates=[PredicateDefinition{name='Path', args={pattern=/consume/**}}], filters=[], uri=lb://nacos-consum 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-cons 2020-05-10 14:33:45.192 INFO 1272 --- [ main] RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [After]
2020-05-10 14:33:45.192 INFO 1272 --- [ main] RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Before]
2020-05-10 14:33:45.192 INFO 1272 --- [ main] RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Between]
2020-05-10 14:33:45.193 INFO 1272 --- [ main] RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Cookie]
2020-05-10 14:33:45.193 INFO 1272 --- [ main] RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Header]
2020-05-10 14:33:45.193 INFO 1272 --- [ main] RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Host]
2020-05-10 14:33:45.194 INFO 1272 --- [ main] RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Method]
2020-05-10 14:33:45.194 INFO 1272 --- [ main] RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Path]
2020-05-10 14:33:45.194 INFO 1272 --- [ main] RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Query]
2020-05-10 14:33:45.194 INFO 1272 --- [ main] RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [ReadBodyPredicateFactory]
2020-05-10 14:33:45.194 INFO 1272 --- [ main] RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [RemoteAddr]
2020-05-10 14:33:45.194 INFO 1272 --- [ main] RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Weight]
2020-05-10 14:33:45.194 INFO 1272 --- [ main] RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [CloudFoundryRouteService]
2020-05-10 14:33:45.335 INFO 1272 --- [ main] DynamicRouteServiceImplByNacos : update route : RouteDefinition{id='provider-router', predicates=[PredicateDefinition{name='Path', args={pattern=/provide/**}}], filters=[], uri=lb://nacos-provider, or 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, 2020-05-10 14:33:45.336 INFO 1272 --- [ main] DynamicRouteServiceImplByNacos : update route : RouteDefinition{id='github-router', predicates=[PredicateDefinition{name='Path', args={pattern=/github}}], filters=[], uri=github, order=3 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=github, order 但这只能说明是初始化静态路由,下⾯我们改变gateway-router⽹关配置内容,追加github-router路由
[{
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论