一、说明

网关的核心概念就是路由配置和路由规则,而作为所有请求流量的入口,在实际生产环境中为了保证高可靠和高可用,是尽量要避免重启的,所以实现动态路由是非常有必要的;本文主要介绍实现的思路,并且以Nacos为数据源来讲解

 

二、实现要点

要实现动态路由只需关注下面4个点

  1. 网关启动时,动态路由的数据怎样加载进来
  2. 静态路由动态路由以那个为准,ps:静态路由指的是配置文件里写死的路由配置
  3. 监听动态路由的数据源变化
  4. 数据有变化时怎样通知zuul刷新路由

 

三、具体实现

3.1. 实现动态路由的数据加载

  • 重写SimpleRouteLocator类的locateRoutes方法,此方法是加载路由配置的,父类中是获取properties中的路由配置,可以通过扩展此方法,达到动态获取配置的目的
  • 这里采用静态路由动态路由共存,相同路由id以动态路由优先覆盖的实现方式

AbstractDynRouteLocator类可查看:AbstractDynRouteLocator.java

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方法

 

3.2. Nacos路由实现类

NacosDynRouteLocator类完整的代码实现可查看:NacosDynRouteLocator.java

3.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 (StrUtil.isBlank(result.getPath()) || !result.isEnabled()) {
continue;
}
ZuulRoute zuulRoute = new ZuulRoute();
BeanUtil.copyProperties(result, zuulRoute);
routes.put(zuulRoute.getPath(), zuulRoute);
}
return routes;
} 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);
}
return new ArrayList<>(0);
}

3.2.2. 增加NacosListener监听路由数据变化

    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);
}
}

注意路由数据变化后不需要自己手动刷新路由,只需要给zuul发送一个RoutesRefreshedEvent事件即可,zuul自己有个ZuulRefreshListener类会监听事件帮我们刷新路由

 

3.3. 配置类创建NacosDynRouteLocator的Bean

DynamicZuulRouteConfig可查看:NacosDynRouteLocator.java

@Configuration
@ConditionalOnProperty(prefix = "zlt.gateway.dynamicRoute", name = "enabled", havingValue = "true")
public class DynamicZuulRouteConfig {
@Autowired
private ZuulProperties zuulProperties; @Autowired
private DispatcherServletPath dispatcherServletPath; /**
* Nacos实现方式
*/
@Configuration
@ConditionalOnProperty(prefix = "zlt.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);
}
}
}

这里通过自定义配置来控制是否开启动态路由功能

 

3.4. 添加Nacos路由配置



新增配置项:

  • Data Id:zuul-routes
  • Group:ZUUL_GATEWAY
  • 配置内容:
[
{
"enabled":true,
"id":"csdn",
"path":"/csdn/**",
"retryable":false,
"stripPrefix":true,
"url":"https://www.csdn.net/"
}, {
"enabled":true,
"id":"github",
"path":"/github/**",
"retryable":false,
"stripPrefix":true,
"url":"http://github.com/"
}
]

添加两条路由数据

 

四、测试

  • 启动网关通过/actuator/routes端点查看当前路由信息

可以看到静态路由和Nacos里配置的两条路由信息并存显示

  • 修改Nacos配置,关闭csdn路由

  • 刷新查看网关的路由信息

csdn的路由已经看不到了,实现了动态改变路由配置

 

推荐阅读

 

请扫码关注我的公众号

Spring Cloud Zuul的动态路由怎样做?集成Nacos实现很简单的更多相关文章

  1. Spring Cloud Gateway的动态路由怎样做?集成Nacos实现很简单

    一.说明 网关的核心概念就是路由配置和路由规则,而作为所有请求流量的入口,在实际生产环境中为了保证高可靠和高可用,是尽量要避免重启的,所以实现动态路由是非常有必要的:本文主要介绍 Spring Clo ...

  2. Spring Cloud Zuul实现动态路由

    1.添加依赖 2.启动类上添加注解 3.配置文件 zuul.ignored-services配置需要忽略的服务,多个用逗号分隔 注释zuul.ignored-services 前: 注释zuul.ig ...

  3. Spring Cloud Gateway之动态路由(数据库版)

    1.实现动态路由的关键是RouteDefinitionRepository接口,该接口存在一个默认实现(InMemoryRouteDefinitionRepository) 通过名字我们应该也知道该实 ...

  4. spring cloud学习(四) 动态路由

    Zuul的主要功能是路由和过滤器.路由功能是微服务的一部分,zuul实现了负载均衡. 1.1 新建模块zuul pom.xml <?xml version="1.0" enc ...

  5. Spring Cloud同步场景分布式事务怎样做?试试Seata

    一.概述 在微服务架构下,虽然我们会尽量避免分布式事务,但是只要业务复杂的情况下这是一个绕不开的问题,如何保证业务数据一致性呢?本文主要介绍同步场景下使用Seata的AT模式来解决一致性问题. Sea ...

  6. Spring Cloud异步场景分布式事务怎样做?试试RocketMQ

    一.背景 在微服务架构中,我们常常使用异步化的手段来提升系统的 吞吐量 和 解耦 上下游,而构建异步架构最常用的手段就是使用 消息队列(MQ),那异步架构怎样才能实现数据一致性呢?本文主要介绍如何使用 ...

  7. Spring Cloud Zuul API服务网关之请求路由

    目录 一.Zuul 介绍 二.构建Spring Cloud Zuul网关 构建网关 请求路由 请求过滤 三.路由详解 一.Zuul 介绍 ​ 通过前几篇文章的介绍,我们了解了Spring Cloud ...

  8. 笔记:Spring Cloud Zuul 快速入门

    Spring Cloud Zuul 实现了路由规则与实例的维护问题,通过 Spring Cloud Eureka 进行整合,将自身注册为 Eureka 服务治理下的应用,同时从 Eureka 中获取了 ...

  9. Spring Cloud Zuul 快速入门

    Spring Cloud Zuul 实现了路由规则与实例的维护问题,通过 Spring Cloud Eureka 进行整合,将自身注册为 Eureka 服务治理下的应用,同时从 Eureka 中获取了 ...

随机推荐

  1. 记一次愚蠢的经历--String不可变性

    前言 只有光头才能变强. 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y 记录一次在写代码时愚蠢的操作,本文涉及到的知识点:S ...

  2. 《ElasticSearch6.x实战教程》之实战ELK日志分析系统、多数据源同步

    第十章-实战:ELK日志分析系统 ElasticSearch.Logstash.Kibana简称ELK系统,主要用于日志的收集与分析. 一个完整的大型分布式系统,会有很多与业务不相关的系统,其中日志系 ...

  3. java - 如何使一个类不能被继承

    使用final关键字: 使用私有构造器: public final class InitTest{ private InitTest(){} }

  4. CSS布局定位基础-盒模型和定位机制

    1. 盒模型 2. 外边距合并 3. 定位机制 4. Float 5. Position:属性有哪些取值,它们的行为是什么? 无依赖绝对定位? 6. Display:常见属性值有哪些取值? 7. 对B ...

  5. H5 video自定义视频控件

    1.自定义效果截图 2.效果源码 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"& ...

  6. 【iOS】UILabel 常用属性设置

    UILabel 的一些常用属性,示例代码如下: // 字体大小 label.font = [UIFont systemFontOfSize:14.0]; label.font = [UIFont fo ...

  7. 【iOS】删除 main.storyboard 的问题

    一直没用 main.storyboard, 后来索性把它删了,结果还出了问题: Terminating app due to uncaught exception 'NSInvalidArgument ...

  8. unity3d立方体碰撞检测(c#代码实现)

    由于unity自带的碰撞组件特别耗费性能,网上的unity物体碰撞的c#代码实现比较少,没有适合的,只能自己写一个来用: using System; using System.Collections. ...

  9. Echarts图表插件(4.x版本)使用(二、带分类筛选的多个图表/实例化多个ECharts,以关系图/force为例)

    导读 如果想在一个页面里实例化带分类筛选的多个Echarts该怎么做呢? 曾探讨了带分类选择的关系图显示为自定义图片的需求实现,传送门ECharts图表插件(4.x版本)使用(一.关系图force节点 ...

  10. Angualr6表单提交验证并跳转

    在Angular6中,使用NG-ZRROR作为前端开发框架,在进行表单开发时遇到了一些问题,最后解决了,在此记录. 1.表单构造: 引入forms: import { FormGroup, FormB ...