[TOC]
开启功能需要,修改`zuul`的配置文件
~~~
pigframe.gateway.dynamicRoute.enabled=true
~~~
## 1. 实现动态路由的数据加载
* 重写`SimpleRouteLocator`类的`locateRoutes`方法,此方法是加载路由配置的,父类中是获取properties中的路由配置,可以通过扩展此方法,达到动态获取配置的目的
* 这里采用`静态路由`与`动态路由`共存,相同路由id以`动态路由`优先覆盖的实现方式
AbstractDynRouteLocator抽象类
~~~
public abstract class AbstractDynRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {
private ZuulProperties properties;
public AbstractDynRouteLocator(String servletPath, ZuulProperties properties) {
super(servletPath, properties);
this.properties = properties;
}
/**
* 刷新路由
*/
@Override
public void refresh() {
doRefresh();
}
@Override
protected Map<String, ZuulRoute> locateRoutes() {
LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<>();
// 从application.properties中加载静态路由信息
routesMap.putAll(super.locateRoutes());
// 从数据源中加载动态路由信息
routesMap.putAll(loadDynamicRoute());
// 优化一下配置
LinkedHashMap<String, ZuulRoute> values = new LinkedHashMap<>();
for (Map.Entry<String, ZuulRoute> entry : routesMap.entrySet()) {
String path = entry.getKey();
// Prepend with slash if not already present.
if (!path.startsWith("/")) {
path = "/" + path;
}
if (StringUtils.hasText(this.properties.getPrefix())) {
path = this.properties.getPrefix() + path;
if (!path.startsWith("/")) {
path = "/" + path;
}
}
values.put(path, entry.getValue());
}
return values;
}
/**
* 加载路由配置,由子类去实现
*/
public abstract Map<String, ZuulRoute> loadDynamicRoute();
}
~~~
> 由于动态路由的数据可以有很多种途径,如:Nacos、Redis、Zookeeper、DB等,所以这里定义一个抽象类,由具体的实现类去定义`loadDynamicRoute`方法
## 2. Nacos路由实现类
NacosDynRouteLocator类
### 2.1. 实现`loadDynamicRoute`方法获取动态数据
~~~
@Override
public Map<String, ZuulProperties.ZuulRoute> loadDynamicRoute() {
Map<String, ZuulRoute> routes = new LinkedHashMap<>();
if (zuulRouteEntities == null) {
zuulRouteEntities = getNacosConfig();
}
for (ZuulRouteEntity result : zuulRouteEntities) {
if (StringUtils.isBlank(result.getPath()) || !result.isEnabled()) {
continue;
}
ZuulRoute zuulRoute = new ZuulRoute();
BeanCopyUtils.copyProperties(result, zuulRoute);
routes.put(zuulRoute.getPath(), zuulRoute);
}
return routes;
}
/**
* 查询zuul的路由配置
*/
private List<ZuulRouteEntity> getNacosConfig() {
try {
String content = nacosConfigProperties.configServiceInstance().getConfig(ZUUL_DATA_ID, ZUUL_GROUP_ID,5000);
return getListByStr(content);
} catch (NacosException e) {
log.error("listenerNacos-error, {}", e.getMessage());
}
return new ArrayList<>(0);
}
~~~
### 2.2. 增加`NacosListener`监听路由数据变化
~~~
/**
* 添加Nacos监听
*/
private void addListener() {
try {
nacosConfigProperties.configServiceInstance().addListener(ZUUL_DATA_ID, ZUUL_GROUP_ID, new Listener() {
@Override
public Executor getExecutor() {
return null;
}
@Override
public void receiveConfigInfo(String configInfo) {
//赋值路由信息
locator.setZuulRouteEntities(getListByStr(configInfo));
RoutesRefreshedEvent routesRefreshedEvent = new RoutesRefreshedEvent(locator);
publisher.publishEvent(routesRefreshedEvent);
}
});
} catch (NacosException e) {
log.error("nacos-addListener-error, {}", e.getMessage());
}
}
~~~
>注意路由数据变化后不需要自己手动刷新路由,只需要给`zuul`发送一个`RoutesRefreshedEvent`事件即可,`zuul`自己有个`ZuulRefreshListener类`会监听事件帮我们刷新路由
### 2.3. 该类完整代码
~~~
@Slf4j
public class NacosDynRouteLocator extends AbstractDynRouteLocator {
private static final String ZUUL_DATA_ID = "zuul-routes";
private static final String ZUUL_GROUP_ID = "ZUUL_GATEWAY";
private NacosConfigProperties nacosConfigProperties;
private ApplicationEventPublisher publisher;
private NacosDynRouteLocator locator;
@Setter
private List<ZuulRouteEntity> zuulRouteEntities;
public NacosDynRouteLocator(NacosConfigProperties nacosConfigProperties, ApplicationEventPublisher publisher,
String servletPath, ZuulProperties properties) {
super(servletPath, properties);
this.nacosConfigProperties = nacosConfigProperties;
this.publisher = publisher;
this.locator = this;
addListener();
}
/**
* 添加Nacos监听
*/
private void addListener() {
try {
nacosConfigProperties.configServiceInstance().addListener(ZUUL_DATA_ID, ZUUL_GROUP_ID, new Listener() {
@Override
public Executor getExecutor() {
return null;
}
@Override
public void receiveConfigInfo(String configInfo) {
//赋值路由信息
locator.setZuulRouteEntities(getListByStr(configInfo));
RoutesRefreshedEvent routesRefreshedEvent = new RoutesRefreshedEvent(locator);
publisher.publishEvent(routesRefreshedEvent);
}
});
} catch (NacosException e) {
log.error("nacos-addListener-error, {}", e.getMessage());
}
}
@Override
public Map<String, ZuulProperties.ZuulRoute> loadDynamicRoute() {
Map<String, ZuulRoute> routes = new LinkedHashMap<>();
if (zuulRouteEntities == null) {
zuulRouteEntities = getNacosConfig();
}
for (ZuulRouteEntity result : zuulRouteEntities) {
if (StringUtils.isBlank(result.getPath()) || !result.isEnabled()) {
continue;
}
ZuulRoute zuulRoute = new ZuulRoute();
BeanCopyUtils.copyProperties(result, zuulRoute);
routes.put(zuulRoute.getPath(), zuulRoute);
}
return routes;
}
/**
* 查询zuul的路由配置
*/
private List<ZuulRouteEntity> getNacosConfig() {
try {
String content = nacosConfigProperties.configServiceInstance().getConfig(ZUUL_DATA_ID, ZUUL_GROUP_ID,5000);
return getListByStr(content);
} catch (NacosException e) {
log.error("listenerNacos-error, {}", e.getMessage());
}
return new ArrayList<>(0);
}
public List<ZuulRouteEntity> getListByStr(String content) {
if (StringUtils.isNotEmpty(content)) {
return JSONObject.parseArray(content, ZuulRouteEntity.class);
}
return new ArrayList<>(0);
}
}
~~~
## 3. 配置类创建`NacosDynRouteLocator`的Bean
DynamicZuulRouteConfig类
~~~
@Configuration
@ConditionalOnProperty(prefix = "pigframe.gateway.dynamicRoute", name = "enabled", havingValue = "true")
public class DynamicZuulRouteConfig {
@Autowired
private ZuulProperties zuulProperties;
@Autowired
private DispatcherServletPath dispatcherServletPath;
/**
* Nacos实现方式
*/
@Configuration
@ConditionalOnProperty(prefix = "pigframe.gateway.dynamicRoute", name = "dataType", havingValue = "nacos", matchIfMissing = true)
public class NacosZuulRoute {
@Autowired
private NacosConfigProperties nacosConfigProperties;
@Autowired
private ApplicationEventPublisher publisher;
@Bean
public NacosDynRouteLocator nacosDynRouteLocator() {
return new NacosDynRouteLocator(nacosConfigProperties, publisher, dispatcherServletPath.getPrefix(), zuulProperties);
}
}
}
~~~
>这里通过自定义配置来控制是否开启`动态路由功能`
## 4. 添加`Nacos`路由配置
新增配置项:
* Data Id:zuul-routes
* Group:ZUUL\_GATEWAY
* 配置内容:
~~~
[
{
"enabled":true,
"id":"gitee",
"path":"/gitee/**",
"retryable":false,
"stripPrefix":true,
"url":"https://www.gitee.com/"
}, {
"enabled":true,
"id":"github",
"path":"/github/**",
"retryable":false,
"stripPrefix":true,
"url":"http://github.com/"
}
]
~~~
## 5. 测试路由
>启动网关通过`/actuator/routes`端点查看网关所有路由信息
可以看到静态路由和`Nacos`里配置的两条路由信息并存显示
修改`Nacos`配置,关闭`gitee`路由("enabled":false)
刷新查看网关的路由信息
`gitee`的路由已经看不到了,实现了动态改变路由配置
- 微服务介绍
- 软件架构设计
- 系统简介
- 运行环境
- 模块介绍
- 启动部署命令参数
- 打包说明
- 开发说明
- Java8
- 认证理论
- 有网络隔离
- 无网络隔离
- token自动续签设计
- url级权限控制
- 单点登录
- 登录
- 用户名密码(+验证码)登录
- 通过openId获取token
- 通过手机号获取token
- 第三方系统接口对接
- 第三方系统单点登录
- 通用刷新token
- 账号登出接口
- 统一异常处理
- 日志埋点工具
- 审计日志
- yml自定义配置自动提示
- Redis使用
- CacheManager集成
- 搜索中心
- 网关zuul动态路由
- swagger
- 解决开发环境的服务冲突和实例乱窜
- 多租户理论
- 多租户实现
- 分布式锁
- 分布式id生成器
- 分布式事务
- 分库分表sharding-sphere
- 消息队列
- 系统幂等性
- X 实时搜索系统设计
- Spring Cloud性能调优
- 链路跟踪
- JWT的RSA非对称密钥生成
- jdk
- Oracle
- OpenJDK
- Dragonwell
- JVM介绍
- 常见JVM内存错误及解决方案
- JVM分析工具详解
- nexus
- docker
- 安装
- docker-compose安装
- 私有仓库搭建
- 指定数据储存目录
- 添加私有仓库
- 提交拉取镜像
- X 免Dockerfile文件构建项目镜像
- 持续集成部署CI/CD
- Confluence知识管理
- JIRA事务与项目跟踪软件
- Gitlab代码仓库
- Jenkins自动化部署
- SonarQube代码检测
- Rancher容器管理平台
- nacos
- 部署
- Rancher部署nacos
- 注册中心
- 配置中心
- 多环境
- 生产部署方案
- X 通过Nginx来实现环境隔离
- Sentinel详解
- 动态规则扩展
- 在生产环境中使用 Sentinel
- 启动配置项
- X 网关流控
- ELK日志系统和慢查询SQL
- docker安装
- 普通安装
- Filebeat安装部署
- ES数据定期删除
- 没数据问题排查思路
- X Logstash的grok语法调试
- 慢查询sql
- 审计日志
- Elasticsearch性能优化
- APM监控
- SkyWalking介绍
- Zipkin,Pinpoint,SkyWalking三种服务链路监控组件分析
- 服务端部署
- 客户端部署
- 日志清理
- Metrics监控
- 二进制包部署
- docker部署
- docker安装各组件监控
- Grafana仪表盘和告警配置
- JMeter压力测试工具
- 使用说明
- 分布式事务
- TX-LCN(同步)
- X SEATA(同步)
- X RocketMQ(异步)
- 消息队列
- RocketMQ
- RocketMQ安装部署
- RocketMQ常见异常处理
- SpringCloud-Stream
- RabbitMQ
- Redis
- 单机安装
- 主从复制
- 主从复制+哨兵
- cluster集群
- 持久化方案
- MySQL
- 单机安装
- 主从复制
- 主从切换
- 主主复制
- 高可用
- 分表分库
- Canal数据库日志解析消费
- Canal安装
- 实时同步数据到ElasticSearch
- FastDFS
- docker安装
- centos安装
- 项目管理系统
- 禅道
- 111