Spring cloud Gateway源码(四) 自定义存储器动态路由实现以及原理

目录

1、基于数据库或者一些缓存组件

1.1 AbstractGatewayControllerEndpoint

1.2 InMemoryRouteDefinitionRepository

1.3 自定义routeDefinition存储器实现动态 

刷新路由


1、基于数据库或者一些缓存组件

        根据之前的源码的理解,我们可以看到gateway是要先加载routeDefinition,然后再转换成对应的route对象。而对应的接口就是RouteDefinitionLocator,我们查看它的子类 发现有个子类叫RouteDefinitionRepository,我们通过名字可以得知它其实就是DAO层的接口,同时RouteDefinitionRepository还继承了RouteDefinitionWriter。当第一次看时我不知道这个具体是做上面的,只是从名字和方法名看出是做routeDefinition的保存和删除的。然后通过查找find链,发现是在 AbstractGatewayControllerEndpoint 调用的。

1.1 AbstractGatewayControllerEndpoint

        它有两个子类,都是controller,也就是当我们访问http://localhost:9001/actuator/gateway/routes 本地gateway链接时的处理层。

它有两个方法:

@PostMapping("/routes/{id}")
	@SuppressWarnings("unchecked")
	public Mono<ResponseEntity<Object>> save(@PathVariable String id, @RequestBody RouteDefinition route) {

		return Mono.just(route).doOnNext(this::validateRouteDefinition)
				.flatMap(routeDefinition -> this.routeDefinitionWriter.save(Mono.just(routeDefinition).map(r 
@DeleteMapping("/routes/{id}")
	public Mono<ResponseEntity<Object>> delete(@PathVariable String id) {
		return this.routeDefinitionWriter.delete(Mono.just(id))
				.then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build())))
				.onErrorResume(t -> t instanceof NotFoundException, t -> Mono.just(ResponseEntity.notFound().build()));
	}

        所以我们可以通过 post -> {baseUrl}/routes/{id} 进行保存路由,delete->  {baseUrl}/routes/{id} 删除路由。

1.2 InMemoryRouteDefinitionRepository

        我们查看RouteDefinitionRepository发现它有个子类,也是在GatewayAutoConfiguration中进行初始化的。所以gateway给我们提供了默认的routeDefinition存储器,但是它是基于内存的,而且默认它是不加载路由信息的。且只有当没有其他 RouteDefinitionRepository存储器才会使用它。

@Bean
	@ConditionalOnMissingBean(RouteDefinitionRepository.class)
	public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() {
		return new InMemoryRouteDefinitionRepository();
	}

1.3 自定义routeDefinition存储器实现动态 

        我是基于内存做的,正常应该是基于redis或者mysql


import com.google.common.collect.Lists;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 
 * @Description:
 * @date 2022/3/19 14:55
 */
@Component
public class MyRouteDefinitionLocator implements RouteDefinitionRepository {

    static List<RouteDefinition> routeDefinitionList = new ArrayList<>(2);

    static {
        for (int i = 0; i < 2; i++) {
            String routeId = "my_" + i;
            RouteDefinition routeDefinition = new RouteDefinition();
            routeDefinition.setId(routeId);
            routeDefinition.setUri(URI.create("https://blog.csdn.net/qGANG/article/details/12350695" + i));

            PredicateDefinition predicateDefinition = new PredicateDefinition();
            predicateDefinition.setName("Path");
            predicateDefinition.addArg("pattern", "/ourGateway" + i + "/**");
            routeDefinition.setPredicates(Lists.newArrayList(predicateDefinition));

            routeDefinitionList.add(routeDefinition);
        }
    }

    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        return Flux.fromIterable(routeDefinitionList);
    }

    @Override
    public Mono<Void> save(Mono<RouteDefinition> route) {
        String routeId = "my_" + System.currentTimeMillis();
        RouteDefinition routeDefinition = new RouteDefinition();
        routeDefinition.setId(routeId);
        routeDefinition.setUri(URI.create("https://blog.csdn.net/qGANG/article/details/123506950"));

        PredicateDefinition predicateDefinition = new PredicateDefinition();
        predicateDefinition.setName("Path");
        predicateDefinition.addArg("pattern", "/ourGateway" + RandomUtils.nextInt(5, 1000) + "/**");
        routeDefinition.setPredicates(Lists.newArrayList(predicateDefinition));

        return Mono.empty();
    }

    @Override
    public Mono<Void> delete(Mono<String> routeId) {
        routeId.subscribe(id -> {
            routeDefinitionList = routeDefinitionList.stream().filter(routeDefinition -> !StringUtils.equals(id, routeDefinition.getId())).collect(Collectors.toList());
        });
        return Mono.empty();
    }
}

创建好后可以先debug看看初始化时 

        因为我是集成了nacos配置中心的,所以还会有其他route,但是我们自定义new出的route已经装载进了route集合。

看到可以正常使用。

删除

我们测下删除功能

        发起http请求后,我们马上查看路由列表,发现我们想要删除的路由还是存在的,delete也没有报错,为什么呢? 我们知道,这个删除只是在内存里删除了我们的routedefinition,路由列表是经过转化的route对象,所以如果不触发重新加载routes的话,肯定是不起作用的。

刷新路由

  • 解决方案1:

       因为我已经集成了nacos,目前nacos是有个任务,默认30000毫秒会从nacos server拉取注册中心服务并发起HeartbeatEvent事件,而RouteRefreshListener监听到此事件后会发起RefreshRoutesEvent,就会重新加载routes了,路由也就刷新了。

        这里也就造成了路由的刷新会有些延迟,但是问题不大。

  •  解决方案2:

        AbstractGatewayControllerEndpoint给我们提供了手动刷新路由的方法

@PostMapping("/refresh")
	public Mono<Void> refresh() {
		this.publisher.publishEvent(new RefreshRoutesEvent(this));
		return Mono.empty();
	}

         也就是我们在删除路由后,可以立即调用刷新方法,它的底层原理也是发起refresh路由事件。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值