一、说明

网关的核心概念就是路由配置和路由规则,而作为所有请求流量的入口,在实际生产环境中为了保证高可靠和高可用,是尽量要避免重启的,所以实现动态路由是非常有必要的;本文主要介绍实现的思路,并且以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. C#5.0新增功能02 调用方信息

    连载目录    [已更新最新开发文章,点击查看详细] 通过使用调用方信息特性,可获取有关方法的调用方的信息. 可以获取源代码的文件路径.源代码中的行号和调用方的成员名称. 此信息有助于跟踪.调试和创建 ...

  2. C#3.0新增功能09 LINQ 基础06 LINQ 查询操作中的类型关系

    连载目录    [已更新最新开发文章,点击查看详细] 若要有效编写查询,应了解完整的查询操作中的变量类型是如何全部彼此关联的. 如果了解这些关系,就能够更容易地理解文档中的 LINQ 示例和代码示例. ...

  3. 跟着大彬读源码 - Redis 6 - 对象和数据类型(下)

    继续撸我们的对象和数据类型. 上节我们一起认识了字符串和列表,接下来还有哈希.集合和有序集合. 1 哈希对象 哈希对象的可选编码分别是:ziplist 和 hashtable. 1.1 ziplist ...

  4. 关于Hack术语方面

    1.肉鸡       所谓“肉鸡”是一种很形象的比喻,比喻那些可以随意被我们控制的电脑,对方可以是WINDOWS系统,也可以是UNIX/LINUX系统,可以是普通的个人电脑,也可以是大型的服务器,我们 ...

  5. web设计之无懈可击

    无懈可击的web设计旨在尽可能地考虑页面元素在各个情况下都能够呈现最好的效果. 1. 思路总览 2. 灵活的文字 3. 可伸缩的导航栏 4. 可扩展的行 5. 自由的框式组件 6. 图片/标题/说明文 ...

  6. shiro 和 spring boot 的集成

    1 添加依赖 使用 shiro-spring-boot-web-starter 在 spring boot 中集成 shiro 只需要再添加一个依赖 <dependency> <gr ...

  7. Vue 报错 listen EADDRINUSE :::8080

    今天在重启vue项目的时候,发现报了错, listen EADDRINUSE :::8080错误提示 原因:因为另一个项目占用了8080端口,我直接在命令行npm run dev第二个项目,就给出了这 ...

  8. poj 3253 Fence Repair(优先队列+huffman树)

    一个很长的英文背景,其他不说了,就是告诉你锯一个长度为多少的木板就要花多少的零钱,把一块足够长(不是无限长)的木板锯成n段,每段长度都告诉你了,让你求最小花费. 明显的huffman树,优先队列是个很 ...

  9. 【POJ - 2456】Aggressive cows(二分)

    Aggressive cows 直接上中文了 Descriptions 农夫 John 建造了一座很长的畜栏,它包括N (2 <= N <= 100,000)个隔间,这些小隔间依次编号为x ...

  10. 使用Junit测试一个 spring静态工厂实例化bean 的例子,所有代码都没有问题,但是出现java.lang.IllegalArgumentException异常

    使用Junit测试一个spring静态工厂实例化bean的例子,所有代码都没有问题,但是出现 java.lang.IllegalArgumentException 异常, 如下图所示: 开始以为是代码 ...