基于Nacos配置中心实现Spring Cloud Gateway的动态路由管理
前面我们了解过了Sentinel 网关流量控制之Spring Cloud Gateway实战,今天带给大家是基于Nacos配置中心实现Spring Cloud Gateway的动态路由管理。1.为什要使用nacos来实现动态路由管理大家如果了解Spring Cloud Gateway启动过程的话,应该都知道Spring Cloud Gateway启动时,就将yml配置文件中的路由配置和规则加载..
前面我们了解过了Sentinel 网关流量控制之Spring Cloud Gateway实战,今天带给大家是基于Nacos配置中心实现Spring Cloud Gateway的动态路由管理。
1.为什要使用nacos来实现动态路由管理
大家如果了解Spring Cloud Gateway启动过程的话,应该都知道Spring Cloud Gateway启动时,就将yml配置文件中的路由配置和规则加载到内存里,使用InMemoryRouteDefinitionRepository来管理。但是我们的上线项目一般都无法做到不重启网关,就可以添加或删除一个新的路由配置和规则。于是这是Nacos就可以出场了,来担任次任务。当我们需要添加或删除一个新的路由配置和规则,我们直接通过·Nacos配置中心下发添加或者删除路由的功能,网关监听配置的更改,就可以轻松实现在不重启网关的情况下,实现动态路由管理。
2.使用nacos-config-spring-boot-starter 实现自动配置
2.1 添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lidong</groupId>
<artifactId>spring-cloud-gateway-service-dynamic-nacos</artifactId>
<version>1.0.0</version>
<name>spring-cloud-gateway-service-dynamic-nacos</name>
<description>基于Nacos实现Spring Cloud Gateway的动态管理</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-config-spring-boot-starter</artifactId>
<version>0.2.3</version>
</dependency>
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-config-spring-boot-actuator</artifactId>
<version>0.2.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.51</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
spring-cloud-starter-gateway:路由转发、请求过滤(权限校验、限流以及监控等)spring-boot-starter-actuator:监控网关的健康状况nacos-config-spring-boot-starter:springboot实现的nacos的自动化配置nacos-config-spring-boot-actuator:springboot实现的nacos的健康spring-cloud-starter-alibaba-nacos-discovery:nacos 作为注册中心
这里为什么没有使用spring-cloud-starter-alibaba-nacos-config 是因为目前2.1.0版本对json的支持有点问题。这里采用的springboot的版本来实现
2.2 配置文件
2.2.1 application.yml 配置文件
server:
port: 9921 #网关的端口
management:
endpoints:
web:
exposure:
include: '*' #暴露端点,这样actuator就可以监控的到健康状况
logging:
level:
org.springframework.cloud.gateway: trace
org.springframework.http.server.reactive: debug
org.springframework.web.reactive: debug
reactor.ipc.netty: debug
# config: classpath:logback-spring.xml
nacos:
config:
server-addr: 127.0.0.1:8848 #nacos的serverAdd配置
group: NAOCS-SPRING-CLOUD-GATEWAY #分组的配置
file-extension: json
data-id: spring-cloud-gateway.json #data-id的配置
2.2.1 bootstrap.yml 配置文件
spring:
application:
name: spring-cloud-gateway-service-dynamic-nacos
jackson:
serialization:
indent-output: true
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #naocs配置中心地址
gateway:
discovery: #是否与服务发现组件进行结合,通过 serviceId(必须设置成大写) 转发到具体的服务实例。
#默认为false,
#设为true便开启通过服务中心的自动根据 serviceId 创建路由的功能。
locator: #路由访问方式:http://Gateway_HOST:Gateway_PORT/大写的serviceId/**,其中微服务应用名默认大写访问。
enabled: false
enabled: true #如果包含启动程序,但出于某些原因,不希望启用网关,则设置spring.cloud.gateway.enabled=false
routes:
- id: 163
uri: http://www.163.com/
predicates:
- Path=/163/**
2.3 使用 @NacosPropertySource加载配置源
使用 @NacosPropertySource加载 dataId 为spring-cloud-gateway.json的配置源,并开启自动更新:
package com.lidong.gateway.config;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.annotation.NacosInjected;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.spring.context.annotation.config.NacosPropertySource;
import com.lidong.gateway.service.NacosDynamicRouteService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.Executor;
@Slf4j
@Component
@NacosPropertySource(dataId = "${nacos.config.data-id}", autoRefreshed = true, groupId = "${nacos.config.group}")
public class NacosGatewayDefineConfig implements CommandLineRunner {
@NacosInjected
private ConfigService configService;
@Value("${nacos.config.data-id}")
private String dataId;
@Value("${nacos.config.group}")
private String group;
@Autowired
NacosDynamicRouteService nacosDynamicRouteService;
/**
* Callback used to run the bean.
*
* @param args incoming main method arguments
* @throws Exception on error
*/
@Override
public void run(String... args) throws Exception {
addRouteNacosListen();
}
/**
* 添加动态路由监听器
*/
private void addRouteNacosListen() {
try {
String configInfo = configService.getConfig(dataId, group, 5000);
log.info("从Nacos返回的配置:" + configInfo);
getNacosDataRoutes(configInfo);
//注册Nacos配置更新监听器,用于监听触发
configService.addListener(dataId, group, new Listener() {
@Override
public void receiveConfigInfo(String configInfo) {
log.info("Nacos更新了!");
log.info("接收到数据:"+configInfo);
getNacosDataRoutes(configInfo);
}
@Override
public Executor getExecutor() {
return null;
}
});
} catch (NacosException e) {
log.error("nacos-addListener-error", e);
e.printStackTrace();
}
}
/**
* 从Nacos返回的配置
* @param configInfo
*/
private void getNacosDataRoutes(String configInfo) {
List<RouteDefinition> list = JSON.parseArray(configInfo, RouteDefinition.class);
list.stream().forEach(definition -> {
log.info(""+JSON.toJSONString(definition));
nacosDynamicRouteService.update(definition);
});
}
}
2.4 定义NacosDynamicRouteService实现路由的更新
package com.lidong.gateway.service;
import org.springframework.cloud.gateway.route.RouteDefinition;
/**
* 更新内存中的路由信息
*/
public interface NacosDynamicRouteService {
/**
* 更新路由信息
* @param gatewayDefine
* @return
* @throws Exception
*/
String update(RouteDefinition gatewayDefine);
}
NacosDynamicRouteServiceImpl 是NacosDynamicRouteService实现,我们将已存在的路由删除,重新添加,来保证内存中路由的一致性。
package com.lidong.gateway.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
@Service
public class NacosDynamicRouteServiceImpl implements NacosDynamicRouteService {
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
@Autowired
private ApplicationEventPublisher publisher;
/**
* 更新路由
* 只能是先删除在添加,由于没有提供更新路由的方法
*
* @param definition
* @return
*/
@Override
public String update(RouteDefinition definition) {
try {
this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
} catch (Exception e) {
return "删除路由失败: RouteId:" + definition.getId();
}
try {
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "更新路由成功";
} catch (Exception e) {
return "更新路由失败";
}
}
}
这里我们使用RouteDefinitionWriter来对路由进行操作,最后使用 ApplicationEventPublisher发布事件,去刷新路由信息。
2.5启动类GatewayApplication的实现
package com.lidong.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
*
*
* CachingRouteDefinitionLocator 缓存目标
* CompositeRouteDefinitionLocator 组合多种
* DiscoveryClientRouteDefinitionLocator 从注册中心
* InMemoryRouteDefinitionRepository 读取内存中的
* PropertiesRouteDefinitionLocator 读取配置文件 GatewayProperties yml/properties
* RouteDefinitionRepository 从存储器读取
*
* @Bean
* @ConditionalOnMissingBean(RouteDefinitionRepository.class)
* public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() {
* return new InMemoryRouteDefinitionRepository();
* }
* 通过上面代码,可以看到如果没有RouteDefinitionRepository的实例,
* 则默认用InMemoryRouteDefinitionRepository。而做动态路由的关键就在这里。
* 即通过自定义的RouteDefinitionRepository类,来提供路由配置信息。
*
*/
@EnableDiscoveryClient //开启服务发现
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
3 测试网关
3.1在nacos控制台创建如下配置文件
- data-id: spring-cloud-gateway.json
- group: NAOCS-SPRING-CLOUD-GATEWAY #分组的配置
- 配置格式:json
- 配置内容
[
{
"id": "aliyun_route","uri":"https://www.aliyun.com/","order": 0,
"filters": [],
"predicates":
[{"args": {"pattern":"/product/**"},"name":"Path"}]
},
{
"id": "aliyun_route1","uri":"https://www.aliyun.com/","order": 0,
"filters": [],
"predicates":
[{"args": {"pattern":"/product1/**"},"name":"Path"}]
}
]

3.2 启动GatewayApplication启动类
2019-09-17 17:21:30.714 INFO 10512 --- [ restartedMain] c.l.g.config.NacosGatewayDefineConfig : 从Nacos返回的配置:[
{
"id": "aliyun_route","uri":"https://www.aliyun.com/","order": 0,
"filters": [],
"predicates":
[{"args": {"pattern":"/product/**"},"name":"Path"}]
},
{
"id": "aliyun_route1","uri":"https://www.aliyun.com/","order": 0,
"filters": [],
"predicates":
[{"args": {"pattern":"/product1/**"},"name":"Path"}]
}
]
2019-09-17 17:21:30.727 INFO 10512 --- [ restartedMain] c.l.g.config.NacosGatewayDefineConfig : {"filters":[],"id":"aliyun_route","order":0,"predicates":[{"args":{"pattern":"/product/**"},"name":"Path"}],"uri":"https://www.aliyun.com/"}
2019-09-17 17:21:30.732 INFO 10512 --- [ restartedMain] c.l.g.config.NacosGatewayDefineConfig : {"filters":[],"id":"aliyun_route1","order":0,"predicates":[{"args":{"pattern":"/product1/**"},"name":"Path"}],"uri":"https://www.aliyun.com/"}
从日志我们可以看出,网关已经加载到了路由信息。
现在我们在浏览器访问: http://localhost:9921/actuator/gateway/routes
我们在使用http://localhost:9921/product/ahas访问,返回了如下页面。
到这里基于Nacos配置中心实现Spring Cloud Gateway的动态路由管理就基本ok。欢迎大家评论,一起交流。
源代码已经上传github:https://github.com/lidong1665/spring-cloud-learning-example/tree/master/spring-cloud-gateway-service-dynamic-nacos
最后想一起交流技术的可以加我wx:
更多推荐



所有评论(0)