Spring Cloud Gateway 源码剖析之配置初始化

老周聊架构

共 20077字,需浏览 41分钟

 · 2021-03-31

点击上方老周聊架构关注我



一、前言

相信大多数公司都会有自己公司的专有网关服务,虽说是自研,但很少会有从零开始开发一个网关服务,基本上是基于市面上比较流行的网关组件像 Zuul、Gateway、Soul 等进行二次封装的。那如果自己公司有相应的网关业务需求的话,这就要对网关产品的底层原理要比较熟悉了,这样做起来也比较顺手。本系列只针对 Gateway 组件进行源码剖析,我在想第一篇如何说会比较好,本来想直接说核心流程的,想了想还是先从配置初始化说起。话不多说,接下来我们就开始 Spring Cloud Gateway 源码剖析之旅吧。

二、揭秘配置初始化

那我们就从项目中 maven 依赖的 jar 包开始吧。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

直接跟进去发现有三个依赖,凭借我们的开发经验,盲猜一下 spring-cloud-gateway-core 就是 gateway 的核心依赖。

直接找到对应的 maven 依赖包:


不知道大家有没有什么疑问,找了这个包,然后 gateway 是怎么与 Spring Boot 项目相关联的呢?

相信有一定经验的朋友知道 Spring Boot 启动的时候有个 @SpringBootApplication 注解,而这个注解包含了一个非常重要的子注解 @EnableAutoConfiguration,该注解表示开启自动配置功能,是 Spring Boot 框架最重要的注解,也是实现自动化配置的注解。

Spring Cloud Gateway 也是同样的套路,那我们直接找到 /META-INF/spring.factories 文件,果不其然。

// Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
// 依赖包的校验配置
org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration,
// 网关的核心配置
org.springframework.cloud.gateway.config.GatewayAutoConfiguration,
// 负载均衡相关依赖配置信息
org.springframework.cloud.gateway.config.GatewayLoadBalancerClientAutoConfiguration,
// 度量相关依赖配置信息
org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration,
// 流控的依赖配置信息
org.springframework.cloud.gateway.config.GatewayRedisAutoConfiguration,
// 注册中心相关的依赖配置
org.springframework.cloud.gateway.discovery.GatewayDiscoveryClientAutoConfiguration

分析上面的自动配置之前,我们先来了解下 SpringBoot 常用注解的含义。

// 执行顺序
@AutoConfigureBefore:在指定的配置类初始化前加载
@AutoConfigureAfter:在指定的配置类初始化后再加载
@AutoConfigureOrder:数越小越先初始化
// 条件配置
@ConditionalOnClass :classpath中存在该类时起效
@ConditionalOnMissingClass :classpath中不存在该类时起效
@ConditionalOnBean :DI容器中存在该类型Bean时起效
@ConditionalOnMissingBean :DI容器中不存在该类型Bean时起效
@ConditionalOnSingleCandidate :DI容器中该类型Bean只有一个或@Primary的只有一个时起效
@ConditionalOnExpression :SpEL表达式结果为true时
@ConditionalOnProperty :参数设置或者值一致时起效
@ConditionalOnResource :指定的文件存在时起效
@ConditionalOnJndi :指定的JNDI存在时起效
@ConditionalOnJava :指定的Java版本存在时起效
@ConditionalOnWebApplication :Web应用环境下起效
@ConditionalOnNotWebApplication :非Web应用环境下起效

1、GatewayClassPathWarningAutoConfiguration

GatewayClassPathWarningAutoConfiguration 用于检查项目是否正确导入 spring-boot-starter-webflux 依赖,而不是错误导入 spring-boot-starter-web 依赖,同时 GatewayClassPathWarningAutoConfiguration 在 EnableAutoConfiguration 配置加载前加载。

@Configuration
// 执行顺序注解
// 当前注解标识需要在GatewayAutoConfiguration前加载此配置
@AutoConfigureBefore({GatewayAutoConfiguration.class})
public class GatewayClassPathWarningAutoConfiguration {
    private static final Log log = LogFactory.getLog(GatewayClassPathWarningAutoConfiguration.class);
    private static final String BORDER = "\n\n**********************************************************\n\n";

    public GatewayClassPathWarningAutoConfiguration() {
    }

    @Configuration
    // 条件判断注解
    // classpath中不存在org.springframework.web.reactive.DispatcherHandler时起效,标识项目未导入了spring-boot-starter-webflux包
    @ConditionalOnMissingClass({"org.springframework.web.reactive.DispatcherHandler"})
    protected static class WebfluxMissingFromClasspathConfiguration {
        public WebfluxMissingFromClasspathConfiguration() {
            // 当前项目未导入了spring-boot-starter-webflux依赖时,打印警告日志
            GatewayClassPathWarningAutoConfiguration.log.warn("\n\n**********************************************************\n\nSpring Webflux is missing from the classpath, which is required for Spring Cloud Gateway at this time. Please add spring-boot-starter-webflux dependency.\n\n**********************************************************\n\n");
        }
    }

    @Configuration
    // 条件判断注解
    // classpath中存在org.springframework.web.servlet.DispatcherServlet时起效,标识项目导入了spring-boot-starter-web包
    @ConditionalOnClass(
        name = {"org.springframework.web.servlet.DispatcherServlet"}
    )
    protected static class SpringMvcFoundOnClasspathConfiguration {
        public SpringMvcFoundOnClasspathConfiguration() {
            // 当前项目导入了spring-boot-starter-web依赖时,打印警告日志
            GatewayClassPathWarningAutoConfiguration.log.warn("\n\n**********************************************************\n\nSpring MVC found on classpath, which is incompatible with Spring Cloud Gateway at this time. Please remove spring-boot-starter-web dependency.\n\n**********************************************************\n\n");
        }
    }
}


2、GatewayAutoConfiguration

GatewayAutoConfiguration 是 Spring Cloud Gateway 核心配置类,这里只列几个核心的,初始化如下 :

  • RoutePredicateHandlerMapping:查找匹配到 Route 并进行处理

  • GatewayProperties:加载网关配置

  • RouteDefinitionRouteLocator:创建一个根据 RouteDefinition 转换的路由定位器

  • GatewayWebfluxEndpoint:管理网关的 HTTP API


@Configuration
// 条件注解
// 通过 spring.cloud.gateway.enabled 配置网关的开启与关闭
// matchIfMissing = true => 网关默认开启。
@ConditionalOnProperty(
    name = {"spring.cloud.gateway.enabled"},
    matchIfMissing = true
)
@EnableConfigurationProperties
@AutoConfigureBefore({HttpHandlerAutoConfiguration.class, WebFluxAutoConfiguration.class})
@AutoConfigureAfter({GatewayLoadBalancerClientAutoConfiguration.class, GatewayClassPathWarningAutoConfiguration.class})
@ConditionalOnClass({DispatcherHandler.class})
public class GatewayAutoConfiguration {
    public GatewayAutoConfiguration() {
    }

    @Bean
    public StringToZonedDateTimeConverter stringToZonedDateTimeConverter() {
        return new StringToZonedDateTimeConverter();
    }

    @Bean
    public RouteLocatorBuilder routeLocatorBuilder(ConfigurableApplicationContext context) {
        return new RouteLocatorBuilder(context);
    }

    @Bean
    @ConditionalOnMissingBean
    public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(GatewayProperties properties) {
        return new PropertiesRouteDefinitionLocator(properties);
    }

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

    @Bean
    @Primary
    public RouteDefinitionLocator routeDefinitionLocator(List<RouteDefinitionLocator> routeDefinitionLocators) {
        return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators));
    }

    @Bean
    public 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
    public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
        return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
    }

    @Bean
    public RouteRefreshListener routeRefreshListener(ApplicationEventPublisher publisher) {
        return new RouteRefreshListener(publisher);
    }

    @Bean
    public FilteringWebHandler filteringWebHandler(List<GlobalFilter> globalFilters) {
        return new FilteringWebHandler(globalFilters);
    }

    @Bean
    public GlobalCorsProperties globalCorsProperties() {
        return new GlobalCorsProperties();
    }

    @Bean
    public RoutePredicateHandlerMapping routePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties, Environment environment) {
        return new RoutePredicateHandlerMapping(webHandler, routeLocator, globalCorsProperties, environment);
    }

    @Bean
    public GatewayProperties gatewayProperties() {
        return new GatewayProperties();
    }
    ...
}


3、GatewayLoadBalancerClientAutoConfiguration

GatewayLoadBalancerClientAutoConfiguration 作用是初始化 LoadBalancerClientFilter 路由的负载均衡拦截器

@Configuration
// 条件判断注解
// classpath中存在LoadBalancerClient和RibbonAutoConfiguration和DispatcherHandler时此配置起效
@ConditionalOnClass({LoadBalancerClient.class, RibbonAutoConfiguration.class, DispatcherHandler.class})
// 执行顺序注解
@AutoConfigureAfter({RibbonAutoConfiguration.class})
@EnableConfigurationProperties({LoadBalancerProperties.class})
public class GatewayLoadBalancerClientAutoConfiguration {
    public GatewayLoadBalancerClientAutoConfiguration() {
    }

    @Bean
    // 条件判断注解
    // DI容器中存在LoadBalancerClient类型Bean时起效
    @ConditionalOnBean({LoadBalancerClient.class})
    @ConditionalOnMissingBean({LoadBalancerClientFilter.class})
    public LoadBalancerClientFilter loadBalancerClientFilter(LoadBalancerClient client, LoadBalancerProperties properties) {
        return new LoadBalancerClientFilter(client, properties);
    }
}

4、GatewayMetricsAutoConfiguration

GatewayMetricsAutoConfiguration 作用是初始化 GatewayMetricsFilter 路由的度量拦截器

@Configuration
@ConditionalOnProperty(
    name = {"spring.cloud.gateway.enabled"},
    matchIfMissing = true
)
@AutoConfigureBefore({HttpHandlerAutoConfiguration.class})
@AutoConfigureAfter({MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class})
@ConditionalOnClass({DispatcherHandler.class, MeterRegistry.class, MetricsAutoConfiguration.class})
public class GatewayMetricsAutoConfiguration {
    public GatewayMetricsAutoConfiguration() {
    }

    @Bean
    @ConditionalOnBean({MeterRegistry.class})
    @ConditionalOnProperty(
        name = {"spring.cloud.gateway.metrics.enabled"},
        matchIfMissing = true
    )
    public GatewayMetricsFilter gatewayMetricFilter(MeterRegistry meterRegistry) {
        return new GatewayMetricsFilter(meterRegistry);
    }
}

5、GatewayRedisAutoConfiguration

GatewayRedisAutoConfiguration 配置作用是初始化初始化 RedisRateLimiter 限流功能的,RequestRateLimiterGatewayFilterFactory 基于 RedisRateLimiter 实现网关的限流功能。

@Configuration
@AutoConfigureAfter({RedisReactiveAutoConfiguration.class})
@AutoConfigureBefore({GatewayAutoConfiguration.class})
@ConditionalOnBean({ReactiveRedisTemplate.class})
@ConditionalOnClass({RedisTemplate.class, DispatcherHandler.class})
class GatewayRedisAutoConfiguration {
    GatewayRedisAutoConfiguration() {
    }

    @Bean
    public RedisScript redisRequestRateLimiterScript() {
        DefaultRedisScript redisScript = new DefaultRedisScript();
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("META-INF/scripts/request_rate_limiter.lua")));
        redisScript.setResultType(List.class);
        return redisScript;
    }

    @Bean
    public ReactiveRedisTemplate<String, String> stringReactiveRedisTemplate(ReactiveRedisConnectionFactory reactiveRedisConnectionFactory) {
        RedisSerializer<String> serializer = new StringRedisSerializer();
        RedisSerializationContext<String, String> serializationContext = RedisSerializationContext.newSerializationContext().key(serializer).value(serializer).hashKey(serializer).hashValue(serializer).build();
        return new ReactiveRedisTemplate(reactiveRedisConnectionFactory, serializationContext);
    }

    @Bean
    @ConditionalOnMissingBean
    public RedisRateLimiter redisRateLimiter(ReactiveRedisTemplate<String, String> redisTemplate, @Qualifier("redisRequestRateLimiterScript") RedisScript<List<Long>> redisScript, Validator validator) {
        return new RedisRateLimiter(redisTemplate, redisScript, validator);
    }
}

6、GatewayDiscoveryClientAutoConfiguration

GatewayDiscoveryClientAutoConfiguration 的作用是初始化配置路由中的注册发现服务信息

@Configuration
// 同2
@ConditionalOnProperty(
    name = {"spring.cloud.gateway.enabled"},
    matchIfMissing = true
)
@AutoConfigureBefore({GatewayAutoConfiguration.class})
@AutoConfigureAfter({CompositeDiscoveryClientAutoConfiguration.class})
@ConditionalOnClass({DispatcherHandler.class, DiscoveryClient.class})
@EnableConfigurationProperties
public class GatewayDiscoveryClientAutoConfiguration {
    public GatewayDiscoveryClientAutoConfiguration() {
    }

    @Bean
    // 当classpath中存在DiscoveryClient起效
    @ConditionalOnBean({DiscoveryClient.class})
    // 通过spring.cloud.gateway.discovery.locator.enabled配置注册中心查找的开启与关闭
    @ConditionalOnProperty(
        name = {"spring.cloud.gateway.discovery.locator.enabled"}
    )
    public DiscoveryClientRouteDefinitionLocator discoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) {
        return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties);
    }

    @Bean
    public DiscoveryLocatorProperties discoveryLocatorProperties() {
        DiscoveryLocatorProperties properties = new DiscoveryLocatorProperties();
        properties.setPredicates(initPredicates());
        properties.setFilters(initFilters());
        return properties;
    }

    public static List<PredicateDefinition> initPredicates() {
        ArrayList<PredicateDefinition> definitions = new ArrayList();
        PredicateDefinition predicate = new PredicateDefinition();
        predicate.setName(NameUtils.normalizeRoutePredicateName(PathRoutePredicateFactory.class));
        predicate.addArg("pattern""'/'+serviceId+'/**'");
        definitions.add(predicate);
        return definitions;
    }

    public static List<FilterDefinition> initFilters() {
        ArrayList<FilterDefinition> definitions = new ArrayList();
        FilterDefinition filter = new FilterDefinition();
        filter.setName(NameUtils.normalizeFilterFactoryName(RewritePathGatewayFilterFactory.class));
        String regex = "'/' + serviceId + '/(?<remaining>.*)'";
        String replacement = "'/${remaining}'";
        filter.addArg("regexp", regex);
        filter.addArg("replacement", replacement);
        definitions.add(filter);
        return definitions;
    }
}

三、总结

通过自动加载初始化上述六个配置实例,Spring Cloud Gateway 就完成自身的加载和初始化工作。跟着老周下来应该还算清晰吧,下一篇我们来说一下 Route 数据模型,敬请期待~



欢迎大家关注我的公众号【老周聊架构】,Java后端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。

喜欢的话,点赞、再看、分享三连。

点个在看你最好看



浏览 13
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报