基于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)