当前位置: 首页> 财经> 金融 > Spring Cloud Gateway动态路由及路由插件实现方案

Spring Cloud Gateway动态路由及路由插件实现方案

时间:2025/8/24 2:07:03来源:https://blog.csdn.net/wxgxgp/article/details/141299739 浏览次数:0次

前言

sim-framework之前使用Zuul作为网关,结合Eureka实现了动态路由及灰度路由,但是存在以下几个问题:

  • 性能问题:Zuul基于线程隔离,一个请求需要一个线程处理,而Gateway基于事件驱动,少量线程即可支持大量并发(仅仅是并发度和吞吐量,并不能提高业务处理效率),在性能不是很好的服务器上,太多的线程数反而会降低并发度。
  • 流行度:Zuul在后期的SpringCloud版本中将不会再继续集成,所以有必要更换为Gateway。
  • 版本问题:除了Zuul在SpringCloud版本中的集成问题,另一个是因为SpringBoot对Java版本的支持问题,SpringBoot3.x后仅支持JDK17及以后的版本,而早期的Zuul并不能使用上JDK17后的新特性;其次后续也可以以更小的成本升级到JDK21,使用它的虚拟线程,进一步提高性能。

因此对网关进行了重构,更新了基础依赖版本,引入了Gateway和Nacos,移除了Eureka。

动态路由实现

所谓动态路由,是指在不停机的情况下,动态的配置路由地址。之前使用Zuul时,通过扩展ZuulProperties.ZuulRoute实现了一个DatabaseRouteLocator[源码],通过从数据库中加载路由配置,然后发布RoutesRefreshedEvent 事件去触发路由更新,整体流程如下:
在这里插入图片描述
更换为Gateway后,整体流程如下:
在这里插入图片描述

其中最主要的是增加了使用redis监听消息来更新路由,相比于定时任务,提高了配置变更的实时性,并且在网关重启的时候,会从redis中拉取全量的路由配置,并加载到内存中,同时也保留了定时任务刷新配置,因为redis的发布订阅是不可靠的,可能会因为连接异常或者网关节点停机等原因,导致无法收到消息,无法触发路由更新,所以仍然需要定时任务定时刷新作为补偿。这里为什么不适用消息队列呢?因为作为一个轻量级的框架,就尽可能的少引入外部组件,所以这里暂且使用redis作为路由配置更新通知的中间件。

Gateway的动态路由实现方式很简单,Gateway中有一个RouteDefinitionRepository,它保存着路由的配置信息,并且它提供了savedelete方法,我们只需要调用这两个方法更新路由后,再发布RefreshRoutesEvent事件即可。

public Mono<Void> add(RouteDefinition route) {log.info("Add route: {}", route);return routeDefinitionRepository.save(Mono.just(route)).thenEmpty((v) -> publisher.publishEvent(new RefreshRoutesEvent(this)));}public Mono<Void> update(RouteDefinition route) {log.info("Update route: {}", route);return routeDefinitionRepository.save(Mono.just(route)).thenEmpty((v) -> publisher.publishEvent(new RefreshRoutesEvent(this)));}public Mono<Void> delete(String id) {log.info("Delete route, Route ID: {}", id);return this.routeDefinitionRepository.delete(Mono.just(id)).onErrorResume(NotFoundException.class, e -> Mono.empty()).thenEmpty((v) -> publisher.publishEvent(new RefreshRoutesEvent(this)));}

sim-framework提供了可视化的路由配置,再不重启网关的情况下可实现动态路由配置。
在这里插入图片描述

路由插件实现

Gateway提供了很多的PredicateFilter,但是总有一些场景无法满足我们,所以我们可以通过自定义插件的方式,将通用的、可复用的、无强业务相关性的流程,封装为一个网关插件,比如鉴权、限流、流量分发等等。

Gateway提供了AbstractRoutePredicateFactoryAbstractGatewayFilterFactory,分别对应路由的匹配器和过滤器,我们可以通过继承它来实现一个自定义的匹配器或者过滤器。

以下,实现一个自定义的过滤器,它的作用是将获取到的权限信息,再经过权限验证后,转发到下游服务时,将它移除掉,避免暴露过多的信息给下游服务:

@Component
public class RemovePermissionGatewayFilterFactory extends AbstractGatewayFilterFactory<RemovePermissionGatewayFilterFactory.Config> implements GatewayFilterSupport {public RemovePermissionGatewayFilterFactory(AuthProperties properties) {super(Config.class);this.properties = properties;}private final AuthProperties properties;@Overridepublic GatewayFilter apply(Config config) {return (exchange, chain) -> {ServerHttpRequest request = exchange.getRequest();if (!shouldFilter(request, properties)) {return chain.filter(exchange);}return exchange.getPrincipal().map(principal -> (UserPrincipal) principal).doOnNext(user -> {user.setPermissions(null);exchange.mutate().principal(Mono.justOrEmpty(user));}).then(chain.filter(exchange));};}public static class Config {//ignore}
}

apply方法中,我可以拿到exchange,通过它可以获取到请求对象和响应对象,从而可以对请求以及响应做任何处理。

将自定义的过滤器注册为Bean即可在配置文件中使用它:

spring:cloud:gateway:routes:- id: sim-service-adminuri: lb://sim-service-adminpredicates:- Path=/server/**filters:- StripPrefix=1- Authentication- Permission- RemovePermission

注意命名规范:SpringCloud建议,自定义过滤器类名以GatewayFilterFactory结尾,我们应当遵循该规范,避免日后出现问题。同理匹配器也一样,建议以RoutePredicateFactory结尾。

以上,是对于路由的静态扩展(提前编写好插件代码),考虑到作为一个网关,承载了整个系统的流量入口,并不能频繁的重启,所以需要提供动态扩展的能力,也就是动态加载插件的能力。对于动态扩展,我们目前使用动态加载jar包的形式,利用Spring的Bean动态注册,将自定义的插件注册为Bean后即可配置使用(该部分目前正在开发中)。

在这里插入图片描述
对于插件这部分,我们将其抽象为网关组件,并提供组件管理功能,可以注册自定义组件,也可以新增内置组件(对网关二次开发),并且支持组件的版本管理和回退。

整体大致流程如下:
在这里插入图片描述
这部分的实现细节,后续单独补充。

关键字:Spring Cloud Gateway动态路由及路由插件实现方案

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: