一、前言
相信大多数公司都会有自己公司的专有网关服务,虽说是自研,但很少会有从零开始开发一个网关服务,基本上是基于市面上比较流行的网关组件像 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后端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。
喜欢的话,点赞、再看、分享三连。