当前位置: 首页 > news >正文

Spring Gateway动态路由实现方案 - 详解

Spring Gateway动态路由实现方案 - 详解

前沿

Spring Cloud Gateway (下文简称 Gateway)作为微服务网关,动态配置路由是刚需,毕竟没人想路由一改就要重启网关。

本文结合源码,提供一种动态路由配置的思路。

何为路由

在 Gateway 中,路由对应的类是org.springframework.cloud.gateway.route.Route,各属性含义:

public class Route implements Ordered {
private final String id;
private final URI uri;
private final int order;
private final AsyncPredicate<ServerWebExchange> predicate;private final List<GatewayFilter> gatewayFilters;private final Map<String, Object> metadata;}

路由是怎么来的?

Gateway 提供了org.springframework.cloud.gateway.route.RouteLocator接口用来构建路由。

public interface RouteLocator {
Flux<Route> getRoutes();}

画板

子类有三个:

CachingRouteLocator

通过 ConcurrentHashMap 来缓存路由对象,避免路由对象反复构建,通过监听 RefreshRoutesEvent 事件来刷新缓存,主要用于提升性能。


CompositeRouteLocator

RouteLocator 的装饰器,将一组 RouteLocator 合并成一个。


RouteDefinitionRouteLocator

路由的实际构建者,根据路由定义对象 RouteDefinition 来构建路由,内部会依赖 RoutePredicateFactory,GatewayFilterFactory 工厂类。

RouteDefinition

为了构建路由,Gateway 提供了org.springframework.cloud.gateway.route.RouteDefinition路由定义类,根据 RouteDefinition 来构建 Route。

RouteDefinition 是怎么来的?

和构建路由一样,Gateway 也提供了 RouteDefinitionLocator 接口来发现 RouteDefinition。

public interface RouteDefinitionLocator {
Flux<RouteDefinition> getRouteDefinitions();}

画板

常见的子类有:

PropertiesRouteDefinitionLocator

基于配置文件的实现,它会从配置文件中读取spring.cloud.gateway.routes来构建 RouteDefinition,当然也支持从配置中心读取。

InMemoryRouteDefinitionRepository

基于内存的实现,内部持有一个 Map 对象来管理 RouteDefinition,默认是空的。额外实现了 RouteDefinitionWriter 接口,允许新增和删除 RouteDefinition。

DiscoveryClientRouteDefinitionLocator

基于服务发现的实现,支持从服务注册中心发现服务,并将服务构建成 RouteDefinition。

CompositeRouteDefinitionLocator

装饰器实现,用于将多个 RouteDefinitionLocator 合并成一个。

RouteLocator工作流程

路由的构建依赖两个核心组件:RouteLocator 和 RouteDefinitionLocator。

简单来说,RouteDefinitionLocator 负责提供 RouteDefinition 路由定义对象,RouteLocator 再根据 RouteDefinition 构建路由对象。

画板

项目引入spring-cloud-starter-gateway依赖后,Spring 启动时会触发 Gateway 的自动装配,

org.springframework.cloud.gateway.config.GatewayAutoConfiguration类。

Gateway 路由定义发现依赖一组 RouteDefinitionLocator,再把他们包装成 CompositeRouteDefinitionLocator

@Bean
@Primary
public RouteDefinitionLocator routeDefinitionLocator(List<RouteDefinitionLocator> routeDefinitionLocators) {return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators));}

默认的 RouteDefinitionLocator 有两个,分别是:PropertiesRouteDefinitionLocator 和 InMemoryRouteDefinitionRepository。前者读取配置文件来生成 RouteDefinition,后者使用 Map 来维护,默认是空的。

@Bean
@ConditionalOnMissingBean
public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(GatewayProperties properties) {
return new PropertiesRouteDefinitionLocator(properties);
}
@Bean
@ConditionalOnMissingBean(RouteDefinitionRepository.class)
public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() {
return new InMemoryRouteDefinitionRepository();
}

RouteDefinitionLocator 有了,接下来就是 RouteLocator。默认会自动装配 RouteDefinitionRouteLocator 和 CachingRouteLocator,前者根据 RouteDefinition 来构建路由,后者提供缓存优化性能。

@Bean
public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
List<GatewayFilterFactory> gatewayFilters, List<RoutePredicateFactory> predicates,RouteDefinitionLocator routeDefinitionLocator, ConfigurationService configurationService) {return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, gatewayFilters, properties,configurationService);}@Bean@Primary@ConditionalOnMissingBean(name = "cachedCompositeRouteLocator")public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));}

实现动态路由,CachingRouteLocator 是关键。

默认情况下,Gateway 会通过 CachingRouteLocator 来优化性能。因为根据 RouteDefinition 来构建路由还是有不小开销的,CachingRouteLocator 会把构建好的 List 缓存下来。

同时,它实现了 ApplicationListener,通过监听 RefreshRoutesEvent 事件来刷新路由。

动态路由实现

综上所述,实现动态路由的思路就是:

实现一个自定义的 RouteDefinitionLocator 从远程加载配置并构建 RouteDefinition,路由一旦有变更,就发送一个 RefreshRoutesEvent 事件通知 CachingRouteLocator 刷新路由,即可生效。

路由配置推荐 Nacos,因为它有变更通知。通过监听配置,即可在配置变更时,第一时间刷新路由,让新的路由生效。

首先,引入依赖

<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-config</artifactId>
</dependency>

然后,实现自定义的 RouteDefinitionLocator,目的是从 Nacos 读取路由配置,构建 RouteDefinition。这里以 JSON 格式配置,routesJson启动时由 Nacos 负责初始化配置,routesListener()方法在配置变更时触发,关键是变更后发送 RefreshRoutesEvent 事件,否则缓存不会刷新。

@Component
public class DynamicRouteDefinitionLocator implements RouteDefinitionLocator {
@NacosConfig(dataId = "gateway-routes.json", group = "DEFAULT_GROUP")
private String routesJson;
private final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Autowired
private ApplicationEventPublisher eventPublisher;
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
@NacosConfigListener(dataId = "gateway-routes.json", group = "DEFAULT_GROUP")
public void routesListener(String data) {
this.routesJson = data;
// 发送路由刷新事件,getRouteDefinitions()会重新触发
eventPublisher.publishEvent(new RefreshRoutesEvent(this));
}
@Override
public Flux<RouteDefinition> getRouteDefinitions() {try {return Flux.fromIterable(OBJECT_MAPPER.readerForListOf(RouteDefinition.class).readValue(routesJson));} catch (JsonProcessingException e) {return Flux.empty();}}}

在 Nacos 中配置,Data ID=gateway-routes.json,Group=DEFAULT_GROUP,内容如下:

[
{
"id": "tb_route",
"uri": "https://www.taobao.com",
"predicates": [
{
"name": "Path",
"args": {
"pattern": "/tb/**"
}
}
],
"filters": [
{
"name": "StripPrefix",
"args": {
"parts": "1"
}
}
]
}
]

网关启动,访问127.0.0.1:8080/tb的请求会被转发到www.taobao.com

修改路由配置如下,网关无需重启,访问127.0.0.1:8080/jd的请求会被转发到www.jd.com

[
{
"id": "jd_route",
"uri": "https://www.jd.com/",
"predicates": [
{
"name": "Path",
"args": {
"pattern": "/jd/**"
}
}
],
"filters": [
{
"name": "StripPrefix",
"args": {
"parts": "1"
}
}
]
}
]

尾巴

Spring Cloud Gateway 动态路由实现的核心思路,是通过自定义 RouteDefinitionLocator 从 Nacos 等配置中心动态加载路由定义,并在配置变更时发布 RefreshRoutesEvent 事件触发 CachingRouteLocator 刷新路由缓存。

http://www.gsyq.cn/news/13971.html

相关文章:

  • 调用setState 之后发生了什么?
  • 树形dp [POI 2013] LUK-Triumphal arch
  • 万象EXCEL开发(三)格式解读calcChain.xml——东方仙盟练气期 - 指南
  • 使用 ShedLock 实现多实例定时任务单执行的常见错误及解决办法
  • PiXYZ Studio 2021下载地址与安装教程
  • 深入解析:在 C# .NETCore 中使用 MongoDB(第 2 部分):使用过滤子句检索文档
  • 深入解析:4、urbane-commerce 认证请求 DTO 设计规范
  • 选对强大的技术底座:一篇文章讲透虚拟机与容器核心差异
  • 深入解析:招聘:解决方案架构师 - 中国北京(混合办公)
  • 自然灾害vr学习机:山体滑坡+泥石流避险+洪涝逃生+地震逃生+台风避险+雷电避险 - 详解
  • 【面板材料】A股上市公司增发股票及配股相关资料(1991-2024年)
  • BindingList的应用与改进
  • 谷歌 SEO 新词 xx animate 等实操教程
  • 完整教程:【读书笔记】架构整洁之道 P6 实现细节
  • init.tcl
  • ffmpeg一些使用记录,防止忘记
  • 生产者-消费者问题
  • QAction的使用
  • flow.tcl
  • sg.测试 PySimpleGUI 取值方法
  • 剥开参数看本质:2025 年手机性能与处理器深度解析 - 详解
  • HT-SBTC-2-10L 是成都恒利泰推出的 5–1000 MHz
  • Gitee DevOps:本土化基因驱动中国企业研发效能革命
  • 每周读书与学习-初识JMeter 元件(三)
  • 划分子网与连通性
  • Gitee:中国开发者生态的崛起与数字化转型新动能
  • 详细介绍:HDFS和MapReduce——Hadoop的两大核心技
  • 完整教程:大模型agent综述:A Survey on Large Language Model based Autonomous Agents
  • 三脚电感在报警器芯片里的实际作用与用法
  • jenkins maven nacos springboot profile实现多环境配置