Spring Cloud GateWay系列(三):路由规则动态刷新
Spring Cloud Gateway旨在提供一种简单而有效的方式来路由API,并为它们提供横切关注点,例如:安全性、监控/指标和弹性。Route(路由)是网关的基本单元,由唯一标识符ID、目标地址URI、一组Predicate、一组Filter组成,根据Predicate进行匹配转发。使用配置文件构建路由规则的简单样例如下:
#指定监控和管理端口
management.server.port=9090
#指定需要公开的监控端点
management.endpoints.web.exposure.include=*#路由规则
spring.cloud.gateway.routes[0].id=csdn-service
spring.cloud.gateway.routes[0].uri=https://blog.csdn.net/
#断言器 路由转发的判断条件,目前SpringCloud Gateway支持多种方式,常见如:Path、Query、Method、Header等
spring.cloud.gateway.routes[0].predicates[0]= Path=/csdn/**,/csdnblog/**
#使用Gateway内置路径重写过滤器实现路径重写
spring.cloud.gateway.routes[0].filters[0]= RewritePath=/csdn(blog)?/(?<segment>.*), /${segment}
引入spring-boot-starter-actuator包之后,我们可以在http://localhost:9090/actuator/gateway/routes路径下查看已配置的路由规则。
从org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint类中可以找到对应的Handler处理器方法:
@RestControllerEndpoint(id = "gateway")
public class GatewayControllerEndpoint extends AbstractGatewayControllerEndpoint {public GatewayControllerEndpoint(List<GlobalFilter> globalFilters,List<GatewayFilterFactory> gatewayFilters,List<RoutePredicateFactory> routePredicates,RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {super(null, globalFilters, gatewayFilters, routePredicates, routeDefinitionWriter,routeLocator);}// TODO: Flush out routes without a definition@GetMapping("/routes")public Flux<Map<String, Object>> routes() {/*从Gateway的核心配置类GatewayAutoConfiguration中可以看到Spring容器中注入的RouteLocator实例this.routeLocator属性是CachingRouteLocator的实例*/return this.routeLocator.getRoutes().map(this::serialize);}
}
SpringBoot自动装配的核心就是在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,使自动配置类生效,帮我们进行自动配置工作。
CachingRouteLocator通过RouteDefinitionRouteLocator类最终调用的是RouteDefinitionLocator的getRouteDefinitions()方法
public class RouteDefinitionRouteLocatorimplements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware {@Overridepublic Flux<Route> getRoutes() {return this.routeDefinitionLocator.getRouteDefinitions().map(this::convertToRoute)// TODO: error handling.map(route -> {if (logger.isDebugEnabled()) {logger.debug("RouteDefinition matched: " + route.getId());}return route;});/** TODO: trace logging if (logger.isTraceEnabled()) {* logger.trace("RouteDefinition did not match: " + routeDefinition.getId()); }*/}private Route convertToRoute(RouteDefinition routeDefinition) {AsyncPredicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);return Route.async(routeDefinition).asyncPredicate(predicate).replaceFilters(gatewayFilters).build();}
}
通过Gateway自动化配置类可以清晰的看到有两个实现类PropertiesRouteDefinitionLocator和InMemoryRouteDefinitionRepository类,通过自定义RouteDefinitionRepository的实现类将路由规则存储在MySQL数据库中并配合actuator URL /actuator/gateway/refresh实现在不重启Gateway的情况下动态刷新路由规则。
public class GatewayAutoConfiguration {@Beanpublic StringToZonedDateTimeConverter stringToZonedDateTimeConverter() {return new StringToZonedDateTimeConverter();}@Beanpublic RouteLocatorBuilder routeLocatorBuilder(ConfigurableApplicationContext context) {return new RouteLocatorBuilder(context);}/*加载application.properties文件中配置的路由规则*/@Bean@ConditionalOnMissingBeanpublic PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(GatewayProperties properties) {return new PropertiesRouteDefinitionLocator(properties);}/*InMemory加载内存中配置的路由规则 Application启动的时候内存中是没有路由规则的且Application关闭的时候内存中保存的路由规则也会丢失我们可以通过自定义RouteDefinitionRepository类替换掉InMemoryRouteDefinitionRepository实现将路由规则保存到数据库中数据库表格的设计可通过分析PropertiesRouteDefinitionLocator类抽象出模型*/@Bean@ConditionalOnMissingBean(RouteDefinitionRepository.class)public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() {return new InMemoryRouteDefinitionRepository();}@Bean@Primarypublic RouteDefinitionLocator routeDefinitionLocator(List<RouteDefinitionLocator> routeDefinitionLocators) {return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators));}@Beanpublic RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,List<GatewayFilterFactory> gatewayFilters,List<RoutePredicateFactory> predicates,RouteDefinitionLocator routeDefinitionLocator,@Qualifier("webFluxConversionService") ConversionService conversionService) {return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates,gatewayFilters, properties, conversionService);}@Bean@Primary@ConditionalOnMissingBean(name = "cachedCompositeRouteLocator")// TODO: property to disable composite?public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));}
}
下面将会详细分析 PropertiesRouteDefinitionLocator加载路由规则的代码实现,并抽象出MySQL数据库routes的创建语句。
public class PropertiesRouteDefinitionLocator implements RouteDefinitionLocator {private final GatewayProperties properties;public PropertiesRouteDefinitionLocator(GatewayProperties properties) {this.properties = properties;}@Overridepublic Flux<RouteDefinition> getRouteDefinitions() {/*获取GatewayProperties中的routes属性*/return Flux.fromIterable(this.properties.getRoutes());}}/*注入application.properties文件中配置的路由规则*/
@ConfigurationProperties("spring.cloud.gateway")
@Validated
public class GatewayProperties {private final Log logger = LogFactory.getLog(getClass());/*** List of Routes.*/@NotNull@Validprivate List<RouteDefinition> routes = new ArrayList<>();
}/*无论通过propertities配置文件,
还是通过自定义RouteDefinitionRepository实现从MySQL中加载加载路由规则
最终都是将定义的路由规则转换为RouteDefinition*/
@Validated
public class RouteDefinition {private String id;/*RouteDefinition和PredicateDefinition/FilterDefinition是一对多的映射关系PredicateDefinition和FilterDefinition的构造函数如出一辙,可以抽象到同一张表中*/@NotEmpty@Validprivate List<PredicateDefinition> predicates = new ArrayList<>();@Validprivate List<FilterDefinition> filters = new ArrayList<>();
}
进一步分析 PredicateDefinition和FilterDefinition
public class PredicateDefinition {@NotNullprivate String name;private Map<String, String> args = new LinkedHashMap<>();public PredicateDefinition() {}/*下面将会参照当前构造函数实现从数据库中加载路由规则配置并封装成Predicate*/public PredicateDefinition(String text) {int eqIdx = text.indexOf('=');/*properties文件中定义的路由规则 断言器 中必须包含 =*/if (eqIdx <= 0) {throw new ValidationException("Unable to parse PredicateDefinition text '"+ text + "'" + ", must be of the form name=value");}setName(text.substring(0, eqIdx));String[] args = tokenizeToStringArray(text.substring(eqIdx + 1), ",");for (int i = 0; i < args.length; i++) {this.args.put(NameUtils.generateName(i), args[i]);}}
}
通过上面对RouteDefinition PredicateDefinition和FilterDefinition的分析,我们抽象出route_rule和route_args两张表。
-- 路由规则表
CREATE TABLE `route_rule` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键',`route_id` varchar(255) DEFAULT NULL COMMENT '路由ID ',`uri` varchar(255) DEFAULT NULL COMMENT '目标地址',`ordered` int(11) DEFAULT NULL COMMENT '加载顺序',`created_at` datetime DEFAULT NULL COMMENT '创建时间',`updated_at` datetime DEFAULT NULL COMMENT '修改时间',`version` bigint(20) DEFAULT NULL COMMENT '数据版本',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;-- 路由参数表 主要用来存储 断言器Predicates和过滤器Filter
-- route_rule与route_args 存在一对多的映射关系
CREATE TABLE `route_args` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`type` int(11) DEFAULT NULL COMMENT '参数类型 0:断言器 1:过滤器',`name` varchar(255) DEFAULT NULL COMMENT '断言器名称 例如: Path RewritePath',`args_name` varchar(255) DEFAULT NULL COMMENT '参数名称',`args_value` varchar(255) DEFAULT NULL COMMENT '参数值',`route_id` varchar(255) DEFAULT NULL COMMENT '表route_id中的字段route_id',`created_at` datetime DEFAULT NULL COMMENT '创建时间',`updated_at` datetime DEFAULT NULL COMMENT '修改时间',`version` bigint(20) DEFAULT NULL COMMENT '数据版本',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty,接下来的数据库访问操作将会基于r2dbc-mysql。
通过自定义RouteDefinitionRepository来替换InMemoryRouteDefinitionRepository,从而更改actuator监控端口 POST actuator/gateway/routes/{id} 的保存地址,下面代码由于时间关系并没有完善,以后将会进一步完善并将代码放置在GitHub上面.....
@Service
public class GatewayService implements RouteDefinitionRepository {@Autowiredprivate RouteRuleRepository routeRuleRepository;/*** @return*/@Override public Flux<RouteDefinition> getRouteDefinitions() {return null;}/*** @param route* @return*/@Override public Mono<Void> save(Mono<RouteDefinition> route) {return route.flatMap(r -> routeRuleRepository.findByRouteId(r.getId()).flatMap(routeRule -> {routeRule.setUri(r.getUri().toString());routeRule.setOrdered(r.getOrder());return Mono.just(routeRule);}).switchIfEmpty(Mono.just(RouteRule.builder().routeId(r.getId()).uri(r.getUri().toString()).ordered(r.getOrder()).build())).flatMap(routeRule -> routeRuleRepository.save(routeRule)).flatMap(routeRule -> {if (!CollectionUtils.isEmpty(r.getPredicates())) {List<RouteArgs> modelList = new ArrayList<>();for (PredicateDefinition definition : r.getPredicates()) {Map<String, String> args = definition.getArgs();if (CollectionUtils.isEmpty(args)) {continue;}/*TODO:先判断数据库中是否存在 存在时更新 不存在时新增*/args.forEach((argName, argValue) -> {RouteArgs argsModel =RouteArgs.builder().type(0).name(definition.getName()).argsName(argName).argsValue(argValue).build();modelList.add(argsModel);});}if (!CollectionUtils.isEmpty(modelList)) {return Mono.just(routeRule);}}return Mono.just(routeRule);})).then();}
}
Spring Cloud GateWay系列(三):路由规则动态刷新相关推荐
- 基于Nacos配置中心实现Spring Cloud Gateway的动态路由管理
前面我们了解过了Sentinel 网关流量控制之Spring Cloud Gateway实战,今天带给大家是基于Nacos配置中心实现Spring Cloud Gateway的动态路由管理. 1.为什 ...
- 基于Nacos实现Spring Cloud Gateway实现动态路由
简介 该文档主要介绍以Nacos为配置中心,实现Spring Cloud GateWay 实现动态路由的功能.Spring Cloud Gateway启动时候,就将路由配置和规则加载到内存里,无法做到 ...
- Spring Cloud Gateway –配置简单路由
可以将Spring Cloud Gateway视为Spring Cloud Netflix Zuul项目的后续产品,并有助于在微服务环境中实现Gateway模式 . 它建立在 Spring Boot ...
- Spring Cloud Gateway系列【4】初始化加载流程源码解析
文章目录 核心源码 Route类 AsyncPredicate接口 ServerWebExchange 接口 GatewayFilter RouteLocator RouteDefinitionLoc ...
- spring cloud gateway 之 动态路由改造
目录 gateway本地文件常规路由配置 本地文件配置对业务造成的痛点 动态路由改造 1 gateway本地文件常规路由配置 我们先大致看下gateway中的常规概念 Route(路由):路由是网关的 ...
- spring cloud gateway 之动态路由
前面分别对 Spring Cloud Zuul 与 Spring Cloud Gateway 进行了简单的说明,它门是API网关,API网关负责服务请求路由.组合及协议转换,客户端的所有请求都首先经过 ...
- 网关服务Spring Cloud Gateway(三)
上篇文章介绍了 Gataway 和注册中心的使用,以及 Gataway 中 Filter 的基本使用,这篇文章我们将继续介绍 Filter 的一些常用功能. 修改请求路径的过滤器 StripPrefi ...
- 三分钟了解Spring Cloud Gateway路由转发之自动路由
文章目录 一.前言 二.路由配置 1. 静态路由 2. 动态路由 3. 自动路由 三.Spring Cloud Gateway 是如何实现动态路由 工作原理 源码解析 路由转发原理 路由转发源码解析 ...
- 使用Nacos实现Spring Cloud Zuul的动态路由
一.前言 Nacos是阿里巴巴开源的致力于服务发现与管理.动态配置管理,以及动态DNS服务的中间件,目前已发布至0.5.0版本,除了与Spring Cloud更加紧密结合以外,还丧心病狂地支持JDK1 ...
最新文章
- 2013年蓝桥杯省赛C/C++A组真题解析
- 趋势科技:黑客勒索瞄准智能电视
- 人工智能助力资深内容营销人员
- 工信部副部长刘利华:推动5G全球统一标准 加强国际合作
- MySQL5.5的安装,连接和操作
- python调用zabbixapi接口_python3 + zabbix api 的使用
- python是偏向bs还是cs_CS与BS架构区别、比较、及现状与趋势分析
- python全栈学习--django基础入门
- 充电宝国产品牌推荐,国产充电宝排名
- O(lgn)计算斐波那契数
- 轻音乐-Bandari(班得瑞)
- MATLAB 函数查询
- 洛谷P1551亲戚题解
- altera fpga 型号说明_ALTERA之FPGA主流芯片选型指导.doc
- 代码真的可以体现一个人的智商吗?
- 4、PIC32系列 GPIO介绍
- “功夫高考”之高考状元的七种武器
- python-django的JsonResponse返回中文数据编码问题
- linux命令安装ssl证书
- 虚拟同步发电机_对逆变电源下垂控制与虚拟同步机控制的再认识