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

Spring Gateway动态路由实现方案

前沿

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

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

何为路由

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

  • id:路由唯一标识
  • uri:转发的目标地址
  • order:路由顺序
  • predicate:谓词集合,符合条件的请求才会被路由
  • gatewayFilters:过滤器集合,允许对请求转发前后对请求和响应做修改
  • metadata:路由元数据
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();@Autowiredprivate ApplicationEventPublisher eventPublisher;@Autowiredprivate RouteDefinitionWriter routeDefinitionWriter;@NacosConfigListener(dataId = "gateway-routes.json", group = "DEFAULT_GROUP")public void routesListener(String data) {this.routesJson = data;// 发送路由刷新事件,getRouteDefinitions()会重新触发eventPublisher.publishEvent(new RefreshRoutesEvent(this));}@Overridepublic 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.dtcms.com/a/414770.html

相关文章:

  • 网站在线提交询盘系统 能直接发到邮箱深圳市工业设计行业协会
  • Python编程练习:7个经典题目详解
  • JCR分区,中科院分区,CCF分区的含义与区别
  • 二型最大似然(Type II Maximum Likelihood):概述与核心概念
  • 娄底网站建设开发自媒体素材视频网站
  • 云浮源峰网站建设工作室地址门户网站建设相关需求
  • 鸿蒙父组件处理子组件的点击事件
  • DDPM原理解析
  • 白盒密码:守护不可信环境中的密钥安全
  • 创建一个网站买卖WordPress外链方法
  • 使用 Go SDK 玩转 Docker:从容器到多架构构建
  • 原生微信小程序开发基础知识总结架构逻辑
  • 树莓派实现的自动垃圾(纸团)回收机器人
  • 【ROS2学习笔记】节点篇:节点概述
  • Java面试宝典:网络协议与Netty二
  • 自然语言处理(01)
  • 如何利用模板 + 继承的方式,形成动态多态的效果呢?
  • UE_ContrlRig
  • 江苏省城乡住房建设厅网站网站建设及维护流程图
  • ubuntu18.04安装五笔字型的方法
  • 上市公司网站建设要求成都丁香人才网官网专区
  • AI 原生应用:内容创作从 “手工作坊” 到 “智能工厂” 的革命
  • AIGC实战——交互式生成对抗网络(iGAN)
  • Scikit-learn Python机器学习 - 聚类分析算法 - DBSCAN(基于密度的噪声应用空间聚类)
  • PyTorch 实战:CIFAR-10 图像分类与网络优化
  • STM32H743-ARM例程10-WWDG
  • STM32H743-ARM例程9-IWDG看门狗
  • 什么是 mmdet3d
  • 建设银行嘉兴分行网站首页胶州网站建设dch100
  • Metal - 9. 深入剖析 3D 场景