Nacos实现Spring Cloud Gateway的动态路由
前言
方案参考:https://www.cnblogs.com/zlt2000/p/11712943.html
网关的核心概念就是路由配置和路由规则,而作为所有请求流量的入口,在实际生产环境中为了保证高可靠和高可用,是尽量要避免重启的,所以实现动态路由是非常有必要的;本文主要介绍Spring Cloud Gateway
实现的思路,并且以Nacos
为数据源来讲解
实现要点
要实现动态路由只需关注下面4个点
- 网关启动时,
动态路由
的数据怎样加载进来 静态路由
与动态路由
以哪个为准,ps:静态路由
指的是配置文件里写死的路由配置- 监听
动态路由
的数据源变化 - 数据有变化时怎样
通知gateway
刷新路由
具体实现
Spring Cloud Gateway
中加载路由信息分别由以下几个类负责
PropertiesRouteDefinitionLocator
:从配置文件中读取路由信息(如YML、Properties等)RouteDefinitionRepository
:从存储器中读取路由信息(如内存、配置中心、Redis、MySQL等)DiscoveryClientRouteDefinitionLocator
:从注册中心中读取路由信息(如Nacos、Eurka、Zookeeper等)
我们可以通过自定义RouteDefinitionRepository
的实现类来实现动态路由的目的
添加依赖
<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>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
静态路由示例
spring:
cloud:
gateway:
discovery:
locator:
lowerCaseServiceId: true
enabled: true
routes:
- id: auth
uri: lb://uaa-server
predicates:
- Path=/api-uaa/**
filters:
- StripPrefix=1
- PreserveHostHeader
实现动态路由的数据加载
创建一个Nacos
的RouteDefinitionRepository
实现类
- 重写
getRouteDefinitions
方法实现路由信息的读取 - 配置
Nacos
监听器,监听路由配置信息的变化 - 路由变化只需要往
ApplicationEventPublisher
推送一个RefreshRoutesEvent
事件即刻,Gateway
会自动监听该事件并调用getRouteDefinitions
方法更新路由信息
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.context.ApplicationEventPublisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
/**
* nacos路由数据源
*/
@Slf4j
public class NacosRouteDefinitionRepository implements RouteDefinitionRepository {
private static final String SCG_DATA_ID = "scg-routes";
private static final String SCG_GROUP_ID = "SCG_GATEWAY";
private ApplicationEventPublisher publisher;
private NacosConfigProperties nacosConfigProperties;
public NacosRouteDefinitionRepository(ApplicationEventPublisher publisher, NacosConfigProperties nacosConfigProperties) {
this.publisher = publisher;
this.nacosConfigProperties = nacosConfigProperties;
addListener();
}
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
try {
String content = nacosConfigProperties.configServiceInstance().getConfig(SCG_DATA_ID, SCG_GROUP_ID,5000);
List<RouteDefinition> routeDefinitions = getListByStr(content);
return Flux.fromIterable(routeDefinitions);
} catch (NacosException e) {
log.error("getRouteDefinitions by nacos error", e);
}
return Flux.fromIterable(CollUtil.newArrayList());
}
/**
* 添加Nacos监听
*/
private void addListener() {
try {
nacosConfigProperties.configServiceInstance().addListener(SCG_DATA_ID, SCG_GROUP_ID, new Listener() {
@Override
public Executor getExecutor() {
return null;
}
@Override
public void receiveConfigInfo(String configInfo) {
publisher.publishEvent(new RefreshRoutesEvent(this));
}
});
} catch (NacosException e) {
log.error("nacos-addListener-error", e);
}
}
@Override
public Mono<Void> save(Mono<RouteDefinition> route) {
return null;
}
@Override
public Mono<Void> delete(Mono<String> routeId) {
return null;
}
private List<RouteDefinition> getListByStr(String content) {
if (StrUtil.isNotEmpty(content)) {
return JSONObject.parseArray(content, RouteDefinition.class);
}
return new ArrayList<>(0);
}
}
创建配置类
创建配置类DynamicRouteConfig
import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.central.gateway.route.NacosRouteDefinitionRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 动态路由配置
*/
@Configuration
@ConditionalOnProperty(prefix = "appblog.gateway.dynamicRoute", name = "enabled", havingValue = "true")
public class DynamicRouteConfig {
@Autowired
private ApplicationEventPublisher publisher;
/**
* Nacos实现方式
*/
@Configuration
@ConditionalOnProperty(prefix = "appblog.gateway.dynamicRoute", name = "dataType", havingValue = "nacos", matchIfMissing = true)
public class NacosDynRoute {
@Autowired
private NacosConfigProperties nacosConfigProperties;
@Bean
public NacosRouteDefinitionRepository nacosRouteDefinitionRepository() {
return new NacosRouteDefinitionRepository(publisher, nacosConfigProperties);
}
}
}
添加Nacos路由配置
新增配置项:
Data Id
:scg-routes
Group
:SCG_GATEWAY
添加两条路由数据,配置内容:
[
{
"id": "appblog",
"predicates": [{
"name": "Path",
"args": {
"pattern": "/appblog/**"
}
}],
"uri": "http://www.appblog.cn/",
"filters": []
},
{
"id": "github",
"predicates": [{
"name": "Path",
"args": {
"pattern": "/github/**"
}
}],
"uri": "https://github.com/",
"filters": []
}
]
测试
启动网关通过/actuator/gateway/routes
端点查看当前路由信息
可以看到Nacos
里配置的两条路由信息
版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/03/26/nacos-implements-dynamic-routing-for-spring-cloud-gateway/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。
共有 0 条评论