Spring Cloud Gateway③编码实现动态增加、修改、删除路由

背景

以上文章演示了Gateway的部分功能的自定义开发,我们在实际业务中,很多转发的路由并不是通过配置文件提前配置好的,可能是通过配置中心获取并且会动态变化,新开发好的API需要挂到API网关上,希望通过仅修改配置解决,API网关不需要重启,那么我们就需要基于Gateway动态增加、修改、删除路由的能力。

本文开发环境介绍

开发依赖版本
Spring Boot2.7.0
Spring Cloud2021.0.1
Spring Cloud Alibaba2021.0.1.0

本文继续接着上一篇文章往下讲,代码和配置都是接着上一篇,如有看不懂的地方可以把前面关于Gateway的内容过目一下。

新增路由

实现ApplicationEventPublisherAware接口

import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.SneakyThrows;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.net.URI;
import java.util.ArrayList;

@RequiredArgsConstructor
@Configuration
public class RouteAutoConfiguration implements ApplicationEventPublisherAware {

    private final RouteDefinitionWriter routeDefinitionWriter;
    private final RouteLocator routeLocator;
    @Setter
    private ApplicationEventPublisher applicationEventPublisher;

    @SneakyThrows
    @PostConstruct
    public void init() {
        RouteDefinition routeDefinition = new RouteDefinition();
        routeDefinition.setId("manual-route-1");
        routeDefinition.setUri(new URI("https://news.baidu.com/"));
        routeDefinition.setPredicates(new ArrayList<PredicateDefinition>() {{
            add(new PredicateDefinition("Path=/baidu/news/**"));
        }});
        routeDefinition.setFilters(new ArrayList<FilterDefinition>() {{
            add(new FilterDefinition("StripPrefix=2"));
        }});
        routeDefinition.setOrder(-1);
        routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
        applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this.routeDefinitionWriter));
    }
}

代码新增了一条新的路由转发规则,把/baidu/news/转发到https://news.baidu.com/

验证新路由

在浏览器输入http://localhost:8081/baidu/news
在这里插入图片描述
出现以上图片内容,表示成功转发。

注意

在配置文件中有一个/baidu的转发,新增的/baidu/news//baidu子路径,Gateway会根据顺序进行匹配,先匹配到哪条符合条件的规则就会执行哪个规则,所以新增路由的代码中有一句routeDefinition.setOrder(-1);,这个顺序放在了配置文件中/baidu的前面。

修改路由

在第①篇文章中,演示了Demo过滤器的配置,那么我们现在尝试通过编码的方式动态修改已经配置好的路由规则,把/baidu/Demo过滤器的第3个参数进行修改

源码解析

  • yml文件中配置的路由规则会映射到GatewayProperties这个类中
  • 根据GatewayProperties类会生成PropertiesRouteDefinitionLocator这个Bean
  • 具体的Bean构造可以查看GatewayAutoConfiguration这个类的源码
	@Bean
	@ConditionalOnMissingBean
	public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(GatewayProperties properties) {
		return new PropertiesRouteDefinitionLocator(properties);
	}

看到上面这段代码,我们就可以生成我们自己的PropertiesRouteDefinitionLocator

构建类型为PropertiesRouteDefinitionLocator的Bean

import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.config.PropertiesRouteDefinitionLocator;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.support.NameUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Optional;

@Configuration
public class RouteDefinitionLocatorAutoConfiguration {

    @Bean
    public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(GatewayProperties properties) {
        Optional<RouteDefinition> optional = properties.getRoutes().stream().filter(r->r.getId().equals("baidu")).findAny();
        if(optional.isPresent()) {
            Optional<FilterDefinition> filterOptional = optional.get().getFilters().stream().filter(f->f.getName().equals("Demo")).findAny();
            filterOptional.get().getArgs().put(NameUtils.generateName(2), "manual");
        }
        return new PropertiesRouteDefinitionLocator(properties);
    }
}

上面的代码把配置文件中的world2修改成了manual

验证修改后的路由

  • 在浏览器输入http://localhost:8081/baidu
  • 在控制台可以看到如下输出
    在这里插入图片描述

删除路由

重新修改上面的RouteDefinitionLocatorAutoConfiguration类

@Configuration
public class RouteDefinitionLocatorAutoConfiguration {

    @Bean
    public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(GatewayProperties properties) {
        Optional<RouteDefinition> optional = properties.getRoutes().stream().filter(r->r.getId().equals("baidu")).findAny();
        if(optional.isPresent()) {
            Optional<FilterDefinition> filterOptional = optional.get().getFilters().stream().filter(f->f.getName().equals("Demo")).findAny();
            filterOptional.get().getArgs().put(NameUtils.generateName(2), "manual");
        }

        Optional<RouteDefinition> optional2 = properties.getRoutes().stream().filter(r->r.getId().equals("demoSwagger")).findAny();
        if(optional2.isPresent()) {
            properties.getRoutes().remove(optional2.get());
        }

        return new PropertiesRouteDefinitionLocator(properties);
    }
}

代码中把demoSwagger这个路由删除了

验证删除后的效果

在浏览器再次输入http://localhost:8081/swagger-ui/index.html
在这里插入图片描述

总结

以上示例通过代码的方式对路由进行了动态新增、修改和删除,有了这些动态的操作,基本上可以满足我们很多实际的业务场景了,比如在配置中心修改路由规则后,可以使用RouteDefinitionWriter动态刷新路由,勿需重启Gateway服务;也可以通过构造PropertiesRouteDefinitionLocator方式,结合实际业务规则,修改静态配置的路由。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

太空眼睛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值