导语:Spring Cloud是一套优秀的微服务解决方案,堪称微服务架构集大成者。然而官方提供的API网关中,没有实现动态更新路由这个对大型复杂系统非常重要的功能。在此我们提供了一个完整的技术实现方式,希望能为有类似需求的同仁提供参考。


前言

原理
# yml配置文件形式spring:cloud:gateway:routes:- id: v1uri: http://xxx/v1predicates:- Path=/xxx
// 代码形式@Beanpublic RouteLocator customRouteLocator(RouteLocatorBuilder builder) {return builder.routes().route("v1", r -> r.path("/xxx").uri("http://xxx/v1")).build();}
//GatewayAutoConfiguration@Bean@ConditionalOnMissingBeanpublic PropertiesRouteDefinitionLocatorpropertiesRouteDefinitionLocator(GatewayProperties properties) {return new PropertiesRouteDefinitionLocator(properties);}@Bean@ConditionalOnMissingBean(RouteDefinitionRepository.class)public InMemoryRouteDefinitionRepositoryinMemoryRouteDefinitionRepository() {return new InMemoryRouteDefinitionRepository();}//PropertiesRouteDefinitionLocatorpublic class PropertiesRouteDefinitionLocator implementsRouteDefinitionLocator {private final GatewayProperties properties;...@Overridepublic Flux<RouteDefinition> getRouteDefinitions() {return Flux.fromIterable(this.properties.getRoutes());}}
这样,我们就可以在网关启动时从一个动态数据源(数据库等,而非代码和配置文件)加载配置数据,并通过 RouteDefinitionRepository 的实现类 InMemoryRouteDefinitionRepository 加载为路由信息了。
public class RefreshRoutesEvent extends ApplicationEvent {*** Create a new ApplicationEvent.* @param source the object on which the event initially occurred(never {@code null})*/public RefreshRoutesEvent(Object source) {super(source);}}
@Overridepublic void onApplicationEvent(ApplicationEvent event) {...else if (event instanceof RefreshRoutesEvent && routeLocator != null){forces initializationrouteLocator.ifAvailable(locator ->locator.getRoutes().subscribe());}}
实践

想加载路由数据到gateway内存中,需要调用InMemoryRouteDefinitionRepository 的save()方法。
想触发gateway的动态刷新,需要调用RefreshRoutesEvent事件。
List<GatewayRoute> routeList =gatewayServiceClient.getApiRouteList().getData();
//路由封装routeList.forEach(gatewayRoute -> {RouteDefinition结构RouteDefinition definition = new RouteDefinition();设置基本信息definition.setId(gatewayRoute.getRouteName());definition.setUri(UriComponentsBuilder.fromUriString("lb://" +gatewayRoute.getServiceId()).build().toUri());//设置断言信息List<PredicateDefinition> predicates = Lists.newArrayList();PredicateDefinition predicatePath = new PredicateDefinition();Map<String, String> predicatePathParams = new HashMap<>(8);predicatePath.setName("Path");predicatePathParams.put("name", gatewayRoute.getRouteName());predicatePathParams.put("pattern", gatewayRoute.getPath());predicatePathParams.put("pathPattern", gatewayRoute.getPath());predicatePath.setArgs(predicatePathParams);predicates.add(predicatePath);definition.setPredicates(predicates);//设置过滤器信息List<FilterDefinition> filters = filters(gatewayRoute);if (!CollectionUtils.isEmpty(filters)) {definition.setFilters(filters);}//重点,保存到gateway内存,其实是放在了Map<String, RouteDefinition> routes里 this.repository.save(Mono.just(definition)).subscribe();});
3.2.1 公共gateway刷新方法
public class OpenRestTemplate extends RestTemplate {/*** 刷新网关*/public void refreshGateway() {publisher.publishEvent(new RefreshRemoteApplicationEvent(this,busProperties.getId(),null));}}
/*** 接收业务侧刷新事件** @param event*/@Overridepublic void onApplicationEvent(RefreshRemoteApplicationEvent event) {refresh();}/*** 刷新路由** @return*/public Mono<Void> refresh() {//3.1加载路由的逻辑this.loadRoutes();//触发默认路由刷新事件,刷新缓存路由this.publisher.publishEvent(new RefreshRoutesEvent(this));return Mono.empty();}
结论

感谢阅读「技术创想」第48期文章
领创集团正在春季招聘中
期待你的加入
点击文末
“阅读原文”
获取更多
招聘信息

关于领创集团
领创集团成立于 2016年,致力于通过科技创新的本地化应用,改造和重塑金融和零售行业,以多元化的业务布局打造一个服务于消费者、企业和商户的生态圈。集团旗下包含企业业务和消费者业务两大板块,企业业务包含 ADVANCE.AI 和 Ginee,分别为银行、金融、金融科技、零售和电商行业客户提供基于 AI 技术的数字身份验证、风险管理产品和全渠道电商服务解决方案;消费者业务 Atome Financial 包括亚洲领先的先享后付平台 Atome 和数字金融服务。2021年 9月,领创集团宣布完成超4亿美元 D 轮融资,融资完成后领创集团估值已超 20亿美元,成为新加坡最大的独立科技创业公司之一。
文章转载自领创集团Advance Group,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。








