为什么需要动态路由?

之前说过 Gateway 的路由配置,常用的有两种方式:

这两者之间因为配置文件的方式修改起来比较灵活,然后通过 Stream+Bus 的方式刷新路由配置,所以大家使用的比较多。

但是如果我们在网关层需要类似于 Canary Release(金丝雀发布,也称灰度发布)这样的能力的话,那么以上两种配置路由的方式就都显得太笨拙了。

矿井中的金丝雀17 世纪,英国矿井工人发现,金丝雀对瓦斯这种气体十分敏感。空气中哪怕有极其微量的瓦斯,金丝雀也会停止歌唱;而当瓦斯含量超过一定限度时,虽然鲁钝的人类毫无察觉,金丝雀却早已毒发身亡。当时在采矿设备相对简陋的条件下,工人们每次下井都会带上一只金丝雀作为 “瓦斯检测指标”,以便在危险状况下紧急撤离。

Spring Cloud Gateway 中虽然已经提供了关于权重的断言,我们在配置文件中可以直接这样配置

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      routes:
      - id: service1_prod
        uri: <http://localhost:8081>
        predicates:
        - Path=/test
        - Weight=service1, 90
      - id: service1_canary
        uri: <http://localhost:8082>
        predicates:
        - Path=/test
        - Weight=service1, 10

以实现 Canary Release 的能力,但是每次发布都配置一遍未免太过麻烦了。

出于“懒”的本性,我们当然希望在发布脚本里能在运行时直接动态修改service1_prodservice1_canary的权重,这样我们就不用手动修改还提心吊胆的担心改错了。

这其实就是“动态路由”了。

Spring Cloud Gateway 在去年 6 月份发布了 2.0 第一个 release 版本,其实已经自带动态路由了, 但是官方文档并没有讲如何动态配置。 不过我们翻看 Spring Cloud Gateway 源码,会发现类 org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint 中提供了网关配置的 RESTful 接口,默认是没有启用的。 在配置类 org.springframework.cloud.gateway.config.GatewayAutoConfiguration 中配置了 GatewayControllerEndpoint

@Configuration
@ConditionalOnClass(Health.class)
protected static class GatewayActuatorConfiguration {

    @Bean
    @ConditionalOnEnabledEndpoint
    public GatewayControllerEndpoint gatewayControllerEndpoint(RouteDefinitionLocator routeDefinitionLocator, List<GlobalFilter> globalFilters,
                                                            List<GatewayFilterFactory> GatewayFilters, RouteDefinitionWriter routeDefinitionWriter,
                                                            RouteLocator routeLocator) {
        return new GatewayControllerEndpoint(routeDefinitionLocator, globalFilters, GatewayFilters, routeDefinitionWriter, routeLocator);
    }
}

也就是说在存在org.springframework.boot.actuate.health.Health时启用,我们想用自带的接口就需要添加 actuator 依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

并且还要将 actuator 的端点暴露出来

management:
  endpoints:
    web:
      exposure:
        include: "*"

然后我们就能通过自带的GatewayControllerEndpoint的 RESTful API 修改运行时的路由了