diff --git a/README.md b/README.md index b977b2b..a9bd9f0 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,7 @@ - [Spring Cloud Commons 源码](docs/SpringCloud/spring-cloud-commons-source-note.md) - [Spring Cloud OpenFeign 源码](docs/SpringCloud/spring-cloud-openfeign-source-note.md) +- [Spring Cloud Gateway 源码](docs/SpringCloud/spring-cloud-gateway-source-note.md) ### SpringSecurity diff --git a/docs/SpringCloud/spring-cloud-gateway-source-note.md b/docs/SpringCloud/spring-cloud-gateway-source-note.md new file mode 100644 index 0000000..ab1a789 --- /dev/null +++ b/docs/SpringCloud/spring-cloud-gateway-source-note.md @@ -0,0 +1,1312 @@ +# 说明 + +Author: [haitaoss](https://github.com/haitaoss) + +源码阅读仓库: [spring-cloud-gateway](https://github.com/haitaoss/spring-cloud-gateway) + +参考资料和需要掌握的知识: + +- [Spring WebFlux 源码分析](https://github.com/haitaoss/spring-framework/blob/source-v5.3.10/note/springwebflux-source-note.md) +- [Spring Cloud Circuit Breaker](https://github.com/haitaoss/spring-cloud-circuitbreaker/blob/source-v2.1.5/note/spring-cloud-circuitbreaker-source-note.md) +- [Spring Cloud Commons](https://github.com/haitaoss/spring-cloud-commons/blob/source-v3.1.5/note/spring-cloud-commons-source-note.md#blockingloadbalancerclientexecute) +- [Spring Cloud Gateway 官网文档](https://docs.spring.io/spring-cloud-gateway/docs/3.1.5/reference/html/) + +# Spring Cloud Gateway 介绍 + +功能:**接收请求**并根据匹配的**路由**进行**转发**。 + +术语: + +- **Route**: 是路由规则的描述。它由 ID、目标 URI、**Predicate 集合**和**Filter 集合**组成。如果 Predicate 为真,则路由匹配。 +- **Predicate**: 这是一个 Java 8 函数接口。输入类型是 `ServerWebExchange` ,所以可以匹配 HTTP 请求中的任何内容,例如 Header 或参数。 +- **Filter**: 这些是使用**特定工厂**构建的 `GatewayFilter` 的实例。使用这个可以在发送下游请求之前或之后修改请求和响应。 + +Spring Cloud Gateway 是基于 Spring WebFlux 实现的,是通过注册 WebFlux 的生命周期组件实现控制请求执行。 + +```properties +# Spring WebFlux 处理请求的生命周期 +客户端请求 -> WebFlux服务 -> WebFilter -> DispatcherHandler -> HandlerMapping -> HandlerAdapter -> 执行Handler方法 +``` + +Gateway 通过注册 [RoutePredicateHandlerMapping](#RoutePredicateHandlerMapping) 实现核心逻辑 + +# Gateway 自动装配 + +`spring-cloud-gateway-server.jar!META-INF/spring.factories`的部分内容 + +```properties +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration,\ + org.springframework.cloud.gateway.config.GatewayAutoConfiguration,\ + org.springframework.cloud.gateway.config.GatewayResilience4JCircuitBreakerAutoConfiguration,\ + org.springframework.cloud.gateway.config.GatewayNoLoadBalancerClientAutoConfiguration,\ + org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration,\ + org.springframework.cloud.gateway.config.GatewayRedisAutoConfiguration,\ + org.springframework.cloud.gateway.discovery.GatewayDiscoveryClientAutoConfiguration,\ + org.springframework.cloud.gateway.config.SimpleUrlHandlerMappingGlobalCorsAutoConfiguration,\ + org.springframework.cloud.gateway.config.GatewayReactiveLoadBalancerClientAutoConfiguration,\ + org.springframework.cloud.gateway.config.GatewayReactiveOAuth2AutoConfiguration +``` + +`spring-cloud-gateway-webflux.jar!META-INF/spring.factories`的内容 + +```properties +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +org.springframework.cloud.gateway.webflux.config.ProxyResponseAutoConfiguration + +org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebFlux=\ +org.springframework.cloud.gateway.webflux.config.ProxyResponseAutoConfiguration +``` + +`spring-cloud-gateway-mvc.jar!META-INF/spring.factories`的内容 + +```properties +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +org.springframework.cloud.gateway.mvc.config.ProxyResponseAutoConfiguration + +org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureWebMvc=\ +org.springframework.cloud.gateway.mvc.config.ProxyResponseAutoConfiguration +``` + +## GatewayClassPathWarningAutoConfiguration + +作用:检验启动环境不能是 SpringMVC + +```java +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true) +public class GatewayClassPathWarningAutoConfiguration { + + @Configuration(proxyBeanMethods = false) + // SpringMVC 会存在这个类,所以条件会满足,这个类就会注册到BeanFactory中 + @ConditionalOnClass(name = "org.springframework.web.servlet.DispatcherServlet") + @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) + protected static class SpringMvcFoundOnClasspathConfiguration { + + public SpringMvcFoundOnClasspathConfiguration() { + // 实例化就直接抛出异常 + throw new MvcFoundOnClasspathException(); + } + + } +} +``` + +## GatewayAutoConfiguration + +### 类图 + +> RouteLocator 是为了得到`Flux`,可以使用 RouteLocatorBuilder 很方便的生成 RouteLocator。 +> +> RouteDefinitionRouteLocator 是会根据 RouteDefinition 生成 Route ,而 RouteDefinition 是由 RouteDefinitionLocator 生成的。 +> +> Route 是由 AsyncPredicate 和 GatewayFilter 组成的。而 AsyncPredicate 由 RoutePredicateFactory 生成,GatewayF 创建 ilter 由 GatewayFilterFactory + +![RouteLocator](../../images/SpringCloud/spring-cloud-gateway-source-note_imgs/RouteLocator.png) + +> RoutePredicateHandlerMapping 通过 RouteLocator 得到的 `Flux` ,遍历执行`Route.getPredicate().apply(ServerWebExchange)` 返回`true`说明命中了路由规则,将命中的 Route 存到 ServerWebExchange 中,然后执行 FilteringWebHandler 。 +> +> FilteringWebHandler 的逻辑就是执行 GlobalFilter + GatewayFilter + +![Route](../../images/SpringCloud/spring-cloud-gateway-source-note_imgs/Route.png) + +### 源码 + +可以自定义这些类型的 bean 实现功能的扩展:**RouteLocator**、**HttpHeaderFilter**、**GlobalFilter** 、**GatewayFilterFactory**、**RoutePredicateFactory** + +默认通过 @Bean 注册了很多的 GlobalFilter、GatewayFilterFactory、RoutePredicateFactory 且都是有条件注解的,可以通过设置属性不进行默认注册。主要是有这[三个条件注解](#conditionalonenabledglobalfilterconditionalonenabledfilterconditionalonenabledpredicate) + +```java +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true) +@ConditionalOnClass(DispatcherHandler.class) +public class GatewayAutoConfiguration { + + /** + * 是工具类,可用来构造出 RouteLocator 实例。若想使用编码的方式配置 Route,推荐使用这个 RouteLocatorBuilder。 + */ + @Bean + public RouteLocatorBuilder routeLocatorBuilder(ConfigurableApplicationContext context) { + return new RouteLocatorBuilder(context); + } + + /** + * 实现 RouteDefinitionLocator 接口,其特点是根据 GatewayProperties(配置文件中定义的route) 的内容返回 List + */ + @Bean + @ConditionalOnMissingBean + public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(GatewayProperties properties) { + return new PropertiesRouteDefinitionLocator(properties); + } + + /** + * 实现 RouteDefinitionRepository 接口,定义如何 save、delete RouteDefinition + * 实现 RouteDefinitionLocator 接口,其特点是从缓存(Map、Redis等等)中得到 List + */ + @Bean + @ConditionalOnMissingBean(RouteDefinitionRepository.class) + public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() { + return new InMemoryRouteDefinitionRepository(); + } + + /** + * 聚合所有的 RouteDefinitionLocator + */ + @Bean + @Primary + public RouteDefinitionLocator routeDefinitionLocator(List routeDefinitionLocators) { + return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators)); + } + + /** + * 是一个工具类,可用来 实例化类、属性绑定和属性校验(JSR303) + * GatewayFilterFactory、RoutePredicateFactory 会使用 ConfigurationService 生成 Config 实例,并完成属性绑定和属性校验(JSR303) + */ + @Bean + public ConfigurationService gatewayConfigurationService(BeanFactory beanFactory, + @Qualifier("webFluxConversionService") ObjectProvider conversionService, + ObjectProvider validator) { + return new ConfigurationService(beanFactory, conversionService, validator); + } + + /** + * RouteLocator 接口是用来生成 Flux 的。 + * + * 依赖 RouteDefinitionLocator 得到 RouteDefinition , 而 RouteDefinition 中定义了 FilterDefinition、PredicateDefinition, + * 会使用 GatewayFilterFactory、RoutePredicateFactory 生成 GatewayFilter、Predicate ,然后配置给 Route 实例 + * 而 GatewayFilterFactory、RoutePredicateFactory 继承这两个接口 ShortcutConfigurable、Configurable,这两个接口是为了得到 Config 。 + * 会使用 ConfigurationService 生成 Config 实例,并完成属性绑定和属性校验(JSR303)。 + * GatewayFilterFactory、RoutePredicateFactory 会根据 Config 来生成 GatewayFilter、Predicate + */ + @Bean + public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties, + List gatewayFilters, List predicates, + RouteDefinitionLocator routeDefinitionLocator, ConfigurationService configurationService) { + return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, gatewayFilters, properties, + configurationService); + } + + /** + * 聚合所有的 RouteLocator 。所以我们可以自定义 RouteLocator 自定义路由 + */ + @Bean + @Primary + @ConditionalOnMissingBean(name = "cachedCompositeRouteLocator") + public RouteLocator cachedCompositeRouteLocator(List routeLocators) { + return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators))); + } + + /** + * 实现 ApplicationListener 接口, + * 收到关心的事件(ContextRefreshedEvent、RefreshScopeRefreshedEvent、InstanceRegisteredEvent、ParentHeartbeatEvent、HeartbeatEvent) + * 就会 发布一个 RefreshRoutesEvent 事件 + */ + @Bean + @ConditionalOnClass(name = "org.springframework.cloud.client.discovery.event.HeartbeatMonitor") + public RouteRefreshListener routeRefreshListener(ApplicationEventPublisher publisher) { + return new RouteRefreshListener(publisher); + } + + /** + * FilteringWebHandler 实现 WebHandler 接口,可以理解成 SpringMVC 中的 handler, + * RoutePredicateHandlerMapping.getHandler() 返回的就是 FilteringWebHandler, + * FilteringWebHandler 就是遍历执行 GlobalFilter + Route配置的WebFilter + */ + @Bean + public FilteringWebHandler filteringWebHandler(List globalFilters) { + return new FilteringWebHandler(globalFilters); + } + + /** + * RoutePredicateHandlerMapping 实现 HandlerMapping 接口。 + * + * RoutePredicateHandlerMapping#getHandler 是根据 RouteLocator 得到的 List 遍历执行 Route.getPredicate().apply(ServerWebExchange) + * 为 true 就说明匹配,会返回 FilteringWebHandler + */ + @Bean + @ConditionalOnMissingBean + public RoutePredicateHandlerMapping routePredicateHandlerMapping(FilteringWebHandler webHandler, + RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties, Environment environment) { + return new RoutePredicateHandlerMapping(webHandler, routeLocator, globalCorsProperties, environment); + } + + // 生成 Predicate 的工厂 + @Bean + @ConditionalOnEnabledPredicate + public AfterRoutePredicateFactory afterRoutePredicateFactory() { + return new AfterRoutePredicateFactory(); + } + + // 生成 GatewayFilter 的 + @Bean + @ConditionalOnEnabledFilter + public AddRequestHeaderGatewayFilterFactory addRequestHeaderGatewayFilterFactory() { + return new AddRequestHeaderGatewayFilterFactory(); + } + + // 实现 HttpHeadersFilter 接口。 NettyRoutingFilter、WebsocketRoutingFilter 会依赖这种类型的bean,用来对 Header 进行修改 + @Bean + @ConditionalOnProperty(name = "spring.cloud.gateway.x-forwarded.enabled", matchIfMissing = true) + public XForwardedHeadersFilter xForwardedHeadersFilter() { + return new XForwardedHeadersFilter(); + } + + // 会使用这个执行 Http、Https 请求,同时依赖 HttpHeadersFilter 用来对 Header 进行修改 + @Bean + @ConditionalOnEnabledGlobalFilter + public NettyRoutingFilter routingFilter(HttpClient httpClient, + ObjectProvider> headersFilters, HttpClientProperties properties) { + return new NettyRoutingFilter(httpClient, headersFilters, properties); + } + // HttpHeaderFilter beans ... + // GlobalFilter beans ... + // Predicate Factory beans ... + // GatewayFilter Factory beans ... + // GatewayActuatorConfiguration 会注册 Endpoint 用于查看、新增、更新、删除 RouteDefinition +} +``` + +## GatewayResilience4JCircuitBreakerAutoConfiguration + +```java +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true) +@ConditionalOnClass({ DispatcherHandler.class, ReactiveResilience4JAutoConfiguration.class, + ReactiveCircuitBreakerFactory.class, ReactiveResilience4JCircuitBreakerFactory.class }) +public class GatewayResilience4JCircuitBreakerAutoConfiguration { + + /** + * SpringCloudCircuitBreakerResilience4JFilterFactory 实现 GatewayFilterFactory 接口, + * 其核心逻辑是使用 ReactiveCircuitBreaker 来执行业务逻辑,当 出现异常 或者 路由请求返回的状态码是期望值 就 + * 直接使用 DispatcherHandler 来执行 fallbackUrl,可以理解成使用 fallbackUrl 重新执行一次请求。 + * 并且会往 ServerWebExchange 设置一个key记录异常对象。 + */ + @Bean + @ConditionalOnBean(ReactiveResilience4JCircuitBreakerFactory.class) + @ConditionalOnEnabledFilter + public SpringCloudCircuitBreakerResilience4JFilterFactory springCloudCircuitBreakerResilience4JFilterFactory( + ReactiveResilience4JCircuitBreakerFactory reactiveCircuitBreakerFactory, + ObjectProvider dispatcherHandler) { + return new SpringCloudCircuitBreakerResilience4JFilterFactory(reactiveCircuitBreakerFactory, dispatcherHandler); + } + + /** + * FallbackHeadersGatewayFilterFactory 实现 GatewayFilterFactory 接口, + * 其核心逻辑:如果请求是 fallbackUrl 执行的(通过异常key判断),那就设置一些请求头 + */ + @Bean + @ConditionalOnMissingBean + @ConditionalOnEnabledFilter + public FallbackHeadersGatewayFilterFactory fallbackHeadersGatewayFilterFactory() { + return new FallbackHeadersGatewayFilterFactory(); + } + +} +``` + +## GatewayNoLoadBalancerClientAutoConfiguration + +```java +@Configuration(proxyBeanMethods = false) +@ConditionalOnMissingClass("org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer") +@ConditionalOnMissingBean(ReactiveLoadBalancer.class) +@EnableConfigurationProperties(GatewayLoadBalancerProperties.class) +@AutoConfigureAfter(GatewayReactiveLoadBalancerClientAutoConfiguration.class) +public class GatewayNoLoadBalancerClientAutoConfiguration { + + /** + * NoLoadBalancerClientFilter 实现 GlobalFilter 接口,也就是每个 Route 的请求都会执行。 + * 功能:路由的Url 有 lb 前缀 就直接抛出异常,也就是说不支持 负载均衡的路由 + * + * BeanFactory 中没有 ReactiveLoadBalancerClientFilter 才会生效。 + */ + @Bean + @ConditionalOnMissingBean(ReactiveLoadBalancerClientFilter.class) + public NoLoadBalancerClientFilter noLoadBalancerClientFilter(GatewayLoadBalancerProperties properties) { + return new NoLoadBalancerClientFilter(properties.isUse404()); + } + + protected static class NoLoadBalancerClientFilter implements GlobalFilter, Ordered { + + private final boolean use404; + + public NoLoadBalancerClientFilter(boolean use404) { + this.use404 = use404; + } + + @Override + public int getOrder() { + return LOAD_BALANCER_CLIENT_FILTER_ORDER; + } + + @Override + @SuppressWarnings("Duplicates") + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); + String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR); + // url 没有 lb 前缀 就放行 + if (url == null || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) { + return chain.filter(exchange); + } + // 不能处理 lb:// 所以 直接报错 + throw NotFoundException.create(use404, "Unable to find instance for " + url.getHost()); + } + + } + +} +``` + +## GatewayMetricsAutoConfiguration + +```java +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty(name = GatewayProperties.PREFIX + ".enabled", matchIfMissing = true) +@EnableConfigurationProperties(GatewayMetricsProperties.class) +@ConditionalOnClass({ DispatcherHandler.class, MeterRegistry.class, MetricsAutoConfiguration.class }) +public class GatewayMetricsAutoConfiguration { + + // 会从 ServerWebExchange 中得到 请求的Method、响应的状态码等 + @Bean + public GatewayHttpTagsProvider gatewayHttpTagsProvider() { + return new GatewayHttpTagsProvider(); + } + + // 会从 ServerWebExchange 中得到 匹配的路由地址 + @Bean + @ConditionalOnProperty(name = GatewayProperties.PREFIX + ".metrics.tags.path.enabled") + public GatewayPathTagsProvider gatewayPathTagsProvider() { + return new GatewayPathTagsProvider(); + } + + // 会从 ServerWebExchange 中得到 routId、route uri + @Bean + public GatewayRouteTagsProvider gatewayRouteTagsProvider() { + return new GatewayRouteTagsProvider(); + } + + // 将 GatewayMetricsProperties 的信息映射成 Tags + @Bean + public PropertiesTagsProvider propertiesTagsProvider(GatewayMetricsProperties properties) { + return new PropertiesTagsProvider(properties.getTags()); + } + + /** + * GatewayMetricsFilter 实现 GlobalFilter 接口, + * 将 List 返回的信息记录到 MeterRegistry 中 + */ + @Bean + @ConditionalOnBean(MeterRegistry.class) + @ConditionalOnProperty(name = GatewayProperties.PREFIX + ".metrics.enabled", matchIfMissing = true) + public GatewayMetricsFilter gatewayMetricFilter(MeterRegistry meterRegistry, + List tagsProviders, GatewayMetricsProperties properties) { + return new GatewayMetricsFilter(meterRegistry, tagsProviders, properties.getPrefix()); + } + + /** + * RouteDefinitionMetrics 实现 ApplicationListener 接口, + * 收到事件的逻辑是 RouteDefinitionLocator.getRouteDefinitions().count() 记录到 MeterRegistry 中 + */ + @Bean + @ConditionalOnBean(MeterRegistry.class) + @ConditionalOnProperty(name = GatewayProperties.PREFIX + ".metrics.enabled", matchIfMissing = true) + public RouteDefinitionMetrics routeDefinitionMetrics(MeterRegistry meterRegistry, + RouteDefinitionLocator routeDefinitionLocator, GatewayMetricsProperties properties) { + return new RouteDefinitionMetrics(meterRegistry, routeDefinitionLocator, properties.getPrefix()); + } + +} +``` + +## GatewayRedisAutoConfiguration + +```java +@Configuration(proxyBeanMethods = false) +@ConditionalOnBean(ReactiveRedisTemplate.class) +@ConditionalOnClass({ RedisTemplate.class, DispatcherHandler.class }) +@ConditionalOnProperty(name = "spring.cloud.gateway.redis.enabled", matchIfMissing = true) +class GatewayRedisAutoConfiguration { + + /** + * RedisRouteDefinitionRepository 实现 RouteDefinitionRepository 接口。 + * 使用 redis 作为缓存层,存储 RouteDefinition + */ + @Bean + @ConditionalOnProperty(value = "spring.cloud.gateway.redis-route-definition-repository.enabled", + havingValue = "true") + @ConditionalOnClass(ReactiveRedisTemplate.class) + public RedisRouteDefinitionRepository redisRouteDefinitionRepository( + ReactiveRedisTemplate reactiveRedisTemplate) { + return new RedisRouteDefinitionRepository(reactiveRedisTemplate); + } + + // ... +} +``` + +## GatewayDiscoveryClientAutoConfiguration + +```java +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true) +@ConditionalOnClass({ DispatcherHandler.class, CompositeDiscoveryClientAutoConfiguration.class }) +@EnableConfigurationProperties +public class GatewayDiscoveryClientAutoConfiguration { + + /** + * 这是一个 PathRoutePredicateFactory,根据 serviceId 进行路由 + * @return + */ + public static List initPredicates() { + ArrayList definitions = new ArrayList<>(); + // TODO: add a predicate that matches the url at /serviceId? + + // add a predicate that matches the url at /serviceId/** + PredicateDefinition predicate = new PredicateDefinition(); + predicate.setName(normalizeRoutePredicateName(PathRoutePredicateFactory.class)); + predicate.addArg(PATTERN_KEY, "'/'+serviceId+'/**'"); + definitions.add(predicate); + return definitions; + } + + /** + * 这是一个 RewritePathGatewayFilterFactory,移除 serviceId 路径前缀 + * @return + */ + public static List initFilters() { + ArrayList definitions = new ArrayList<>(); + + // add a filter that removes /serviceId by default + FilterDefinition filter = new FilterDefinition(); + filter.setName(normalizeFilterFactoryName(RewritePathGatewayFilterFactory.class)); + String regex = "'/' + serviceId + '/?(?.*)'"; + String replacement = "'/${remaining}'"; + filter.addArg(REGEXP_KEY, regex); + filter.addArg(REPLACEMENT_KEY, replacement); + definitions.add(filter); + + return definitions; + } + + /** + * DiscoveryLocatorProperties 类上标注了 @ConfigurationProperties("spring.cloud.gateway.discovery.locator") + * 也就是可以通过配置属性的方式设置属性值,下面的逻辑是设置默认值的意思。 + * DiscoveryClientRouteDefinitionLocator 会使用这两个属性会作为生成 RouteDefinition 的 Predicate 和 Filter + * @return + */ + @Bean + public DiscoveryLocatorProperties discoveryLocatorProperties() { + DiscoveryLocatorProperties properties = new DiscoveryLocatorProperties(); + // 默认就设置 PathRoutePredicateFactory + properties.setPredicates(initPredicates()); + // 默认就设置 RewritePathGatewayFilterFactory + properties.setFilters(initFilters()); + return properties; + } + + @Configuration(proxyBeanMethods = false) + @ConditionalOnProperty(value = "spring.cloud.discovery.reactive.enabled", matchIfMissing = true) + public static class ReactiveDiscoveryClientRouteDefinitionLocatorConfiguration { + + /** + * DiscoveryClientRouteDefinitionLocator 实现 RouteDefinitionLocator。 + * 会根据 ReactiveDiscoveryClient.getServices() 返回的 Flux 生成 Flux + * 每个 RouteDefinition 是由 ServiceInstance + DiscoveryLocatorProperties 的内容 配置 路由Uri、Predicate、Filter + * 大部分属性值是通过解析 SPEL 表达式得到的,其中根对象是 ServiceInstance,所以说 编写的 SPEL 表达式可以引用 ServiceInstance 中的属性 + * + * @param discoveryClient + * @param properties + * @return + */ + @Bean + @ConditionalOnProperty(name = "spring.cloud.gateway.discovery.locator.enabled") + public DiscoveryClientRouteDefinitionLocator discoveryClientRouteDefinitionLocator( + ReactiveDiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) { + return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties); + } + + } + +} +``` + +## SimpleUrlHandlerMappingGlobalCorsAutoConfiguration + +```java +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass(SimpleUrlHandlerMapping.class) +@ConditionalOnProperty(name = "spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping", + matchIfMissing = false) +public class SimpleUrlHandlerMappingGlobalCorsAutoConfiguration { + + @Autowired + private GlobalCorsProperties globalCorsProperties; + + @Autowired + private SimpleUrlHandlerMapping simpleUrlHandlerMapping; + + /** + * 为 SimpleUrlHandlerMapping 配置 跨域配置信息 + */ + @PostConstruct + void config() { + simpleUrlHandlerMapping.setCorsConfigurations(globalCorsProperties.getCorsConfigurations()); + } + +} +``` + +## GatewayReactiveLoadBalancerClientAutoConfiguration + +```java +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass({ ReactiveLoadBalancer.class, LoadBalancerAutoConfiguration.class, DispatcherHandler.class }) +@EnableConfigurationProperties(GatewayLoadBalancerProperties.class) +public class GatewayReactiveLoadBalancerClientAutoConfiguration { + + /** + * ReactiveLoadBalancerClientFilter 实现 GlobalFilter 接口。 + * 作用:url 没有 lb 协议 就放行,有 lb 就使用 LoadBalancerClientFactory 得到负载均衡后的 uri,修改 ServerWebExchange 放行filter + */ + @Bean + @ConditionalOnBean(LoadBalancerClientFactory.class) + @ConditionalOnMissingBean(ReactiveLoadBalancerClientFilter.class) + @ConditionalOnEnabledGlobalFilter + public ReactiveLoadBalancerClientFilter gatewayLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory, + GatewayLoadBalancerProperties properties) { + return new ReactiveLoadBalancerClientFilter(clientFactory, properties); + } + + /** + * LoadBalancerServiceInstanceCookieFilter 实现 GlobalFilter 接口 + * 作用:是负载均衡的路由,就添加一个Cookie键值对 + */ + @Bean + @ConditionalOnBean({ ReactiveLoadBalancerClientFilter.class, LoadBalancerClientFactory.class }) + @ConditionalOnMissingBean + @ConditionalOnEnabledGlobalFilter + public LoadBalancerServiceInstanceCookieFilter loadBalancerServiceInstanceCookieFilter( + LoadBalancerClientFactory loadBalancerClientFactory) { + return new LoadBalancerServiceInstanceCookieFilter(loadBalancerClientFactory); + } + +} +``` + +## GatewayReactiveOAuth2AutoConfiguration + +这是 Spring Security 的组件,没研究过,不知道具体的作用是啥,暂时不管了。 + +## ProxyResponseAutoConfiguration + +ProxyResponseAutoConfiguration 是 HandlerMethodArgumentResolver 接口的实现类,是用来解析方法参数的。它支持解析 `ProxyExchange` 类型的参数。`ProxyExchange` 可用来执行 Http 请求,感觉好鸡肋... + +又因为 SpringWebFlux 和 SpringMVC 的执行流程是类似的,定义的类名也是一样的(包不同),所以就搞了两套。 + +注:HandlerMethodArgumentResolver 是 SpringMVC 、SpringWebFlux 的内容,不细说了。 + +# 核心源码 + +## @ConditionalOnEnabledGlobalFilter、@ConditionalOnEnabledFilter、@ConditionalOnEnabledPredicate + +`类的定义` + +```java +@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.TYPE, ElementType.METHOD }) +@Conditional(OnEnabledGlobalFilter.class) +public @interface ConditionalOnEnabledGlobalFilter { + Class value() default OnEnabledGlobalFilter.DefaultValue.class; +} + +@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.TYPE, ElementType.METHOD }) +@Conditional(OnEnabledFilter.class) +public @interface ConditionalOnEnabledFilter { + Class> value() default OnEnabledFilter.DefaultValue.class; +} + +@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.TYPE, ElementType.METHOD }) +@Conditional(OnEnabledPredicate.class) +public @interface ConditionalOnEnabledPredicate { + Class> value() default OnEnabledPredicate.DefaultValue.class; +} +``` + +因为 @ConditionalOnEnabledGlobalFilter 上标注了 @Conditional,所以在 [ConfigurationClassPostProcessor](https://github.com/haitaoss/spring-framework/blob/source-v5.3.10/note/spring-source-note.md#conditional) 解析配置类时,会执行 `OnEnabledGlobalFilter#matches(ConditionContext,AnnotatedTypeMetadata)` 结果是`true`才会将 bean 注册到 BeanFactory 中 + +![OnEnabledComponent](../../images/SpringCloud/spring-cloud-gateway-source-note_imgs/OnEnabledComponent.png) + +```java +/** + * {@link Condition#matches(ConditionContext, AnnotatedTypeMetadata)} + * {@link SpringBootCondition#matches(ConditionContext, AnnotatedTypeMetadata)} + * {@link OnEnabledComponent#getMatchOutcome(ConditionContext, AnnotatedTypeMetadata)} + * 1. 拿到类型。若注解的 value 不是默认值就返回value值,否则就拿到方法的返回值类型。 + * Class candidate = getComponentType(annotationClass(), context, metadata); + * 2. 确定匹配结果。前缀 + 类处理后的值 + 后缀 作为key,从 Environment 获取值,值是false则不匹配,否则匹配 + * determineOutcome(candidate, context.getEnvironment()) + * + * Tips: OnEnabledComponent 定义了三个抽象方法,由子类决定返回值是啥 + * normalizeComponentName() 得到 类处理后的值 + * annotationClass() 得到 注解 + * defaultValueClass() 得到 默认值 + * */ +``` + +可以通过这种方式让默认注入的失效。 + +```properties +spring.cloud.gateway.global-filter.XX.enabled=false +spring.cloud.gateway.filter.XX.enabled=false +spring.cloud.gateway.predicate.XX.enabled=false +``` + +## NettyRoutingFilter + +是非常重要的 GlobalFilter。Gateway 是通过它执行 http、https 协议的请求,依赖 HttpClient 执行请求。 + +```java +public class NettyRoutingFilter implements GlobalFilter, Ordered { + + @Override + public int getOrder() { + return Integer.MAX_VALUE; // 最大值,说明这是最后要执行的 GatewayFilter + } + + @Override + @SuppressWarnings("Duplicates") + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR); + + String scheme = requestUrl.getScheme(); + // 已经路由 或者 不是 http、https 就放行 + if (isAlreadyRouted(exchange) || (!"http".equalsIgnoreCase(scheme) && !"https".equalsIgnoreCase(scheme))) { + return chain.filter(exchange); + } + // 设置一个属性,标记 已经路由了 + setAlreadyRouted(exchange); + + /** + * 遍历执行所有的 HttpHeadersFilter 得到 HttpHeaders。 + * 也就是说可以对最终要执行的 请求头 进行加工 + * + * 注:HttpHeadersFilter 是从BeanFactory中获取的,所以我们可以自定义 HttpHeadersFilter 达到扩展的目的 + * */ + HttpHeaders filtered = filterRequest(getHeadersFilters(), exchange); + + // 根据 Route 的元数据构造 HttpClient 然后执行请求 + Flux responseFlux = getHttpClient(route, exchange) + .headers() + .responseConnection((res, connection) -> {}); + + // 放行 + return responseFlux.then(chain.filter(exchange)); + } + +} +``` + +## WebsocketRoutingFilter + +是非常重要的 GlobalFilter。Gateway 是通过它执行 ws、wss 协议的请求,依赖 WebSocketService 执行请求。 + +```java +public class WebsocketRoutingFilter implements GlobalFilter, Ordered { + @Override + public int getOrder() { + // 在 NettyRoutingFilter 之前执行 + return Integer.MAX_VALUE - 1; + } + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + // 有请求头 upgrade=WebSocket ,那就将 http、https 转成 ws、wss 协议 + changeSchemeIfIsWebSocketUpgrade(exchange); + + URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR); + String scheme = requestUrl.getScheme(); + + // 已经路由过 或者 不是 ws、wss 协议 就放行 + if (isAlreadyRouted(exchange) || (!"ws".equals(scheme) && !"wss".equals(scheme))) { + return chain.filter(exchange); + } + // 标记路由了 + setAlreadyRouted(exchange); + + HttpHeaders headers = exchange.getRequest().getHeaders(); + /** + * 遍历执行所有的 HttpHeadersFilter 得到 HttpHeaders。 + * 也就是说可以对最终要执行的 请求头 进行加工 + * + * 注:HttpHeadersFilter 是从BeanFactory中获取的,所以我们可以自定义 HttpHeadersFilter 达到扩展的目的 + * */ + HttpHeaders filtered = filterRequest(getHeadersFilters(), exchange); + + List protocols = getProtocols(headers); + + // 使用 webSocketService 执行请求。且不在执行后续的filter + return this.webSocketService.handleRequest(exchange, + new ProxyWebSocketHandler(requestUrl, this.webSocketClient, filtered, protocols)); + } +} +``` + +## RoutePredicateHandlerMapping + +RoutePredicateHandlerMapping 是由 [GatewayAutoConfiguration](#GatewayAutoConfiguration) 注册的。 + +**大致流程**:由 [RouteLocator](#RouteLocator) 得到 `Flux` ,遍历执行 `Route.getPredicate().apply(exchange) == true` + +就将 Route 存到 exchange 中然后返回 [FilteringWebHandler](#FilteringWebHandler),最后会由 HandlerAdapter 执行 [FilteringWebHandler](#FilteringWebHandler) 。 + +```java +public class RoutePredicateHandlerMapping extends AbstractHandlerMapping { + /** + * 通过依赖注入得到这些bean + */ + public RoutePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator, + GlobalCorsProperties globalCorsProperties, Environment environment) { + this.webHandler = webHandler; + this.routeLocator = routeLocator; + /** + * 获取属性作为 order 值,默认是1。从而决定是 DispatcherHandler 使用的第几个 HandlerMapping, + * 因为 HandlerMapping 的特点是能处理就使用,不在使用其他的 HandlerMapping,所以优先级是很重要的。 + * */ + setOrder(environment.getProperty(GatewayProperties.PREFIX + ".handler-mapping.order", Integer.class, 1)); + // 设置同源配置信息 + setCorsConfigurations(globalCorsProperties.getCorsConfigurations()); + } + + /** + * 返回值不是 Mono.empty() 就表示 RoutePredicateHandlerMapping 命中了, + * 就会执行 HandlerAdapter.handle(serverWebExchange,webHandler) + */ + @Override + protected Mono getHandlerInternal(ServerWebExchange exchange) { + // 设置一个属性 + exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getSimpleName()); + /** + * 使用 routeLocator 得到 List 遍历 + * */ + return lookupRoute(exchange) + .flatMap((Function>) r -> { + exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR); + /** + * 将 Route 设置到 exchange 中 + * + * 后续流程会用到 + * {@link FilteringWebHandler#handle(ServerWebExchange)} + * */ + exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r); + /** + * 会由 SimpleHandlerAdapter 处理 + * */ + return Mono.just(webHandler); + }).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> { + exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR); + }))); + } + + protected Mono lookupRoute(ServerWebExchange exchange) { + /** + * 得到 Route,根据 Route 的 Predicate 决定是否匹配 + * */ + return this.routeLocator.getRoutes() + /** + * concatMap 的特点是 返回的内容不是 Mono.empty() 、Flux.empty() 才收集到 Flux 中 + * */ + .concatMap(route -> Mono.just(route).filterWhen(r -> { + // exchange 中存一下 routeId + exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId()); + /** + * 执行谓词 + * {@link AsyncPredicate.AndAsyncPredicate#apply(Object)} + * */ + return r.getPredicate().apply(exchange); + }).onErrorResume(e -> Mono.empty())) + // 拿到第一个。所以 Route 的顺序会决定最终的方法的执行 + .next(); + } + +} +``` + +## RouteLocator + +RouteLocator 是由 [GatewayAutoConfiguration](#GatewayAutoConfiguration) 注册的,因为标注了 @Primary,所以 [RoutePredicateHandlerMapping](#RoutePredicateHandlerMapping) 通过依赖注入拿到的是 CachingRouteLocator。 + +CachingRouteLocator 是对 CompositeRouteLocator 的代理。功能: + +- 使用 ConcurrentHashMap 缓存结果。 +- 它实现 `ApplicationListener `接口,接收事件的处理逻辑是 **更新缓存结果**,然后发布 RefreshRoutesResultEvent 事件 + +CompositeRouteLocator 是用来聚合容器中所有的 RouteLocator 的,默认的 RouteLocator 是 [RouteDefinitionRouteLocator](#RouteDefinitionRouteLocator) + +Tips:若想通过编码的方式生成 RouteLocator 可以使用 RouteLocatorBuilder。 + +```java +public class CachingRouteLocator + implements Ordered, RouteLocator, ApplicationListener, ApplicationEventPublisherAware { + + private final RouteLocator delegate; + private final Map cache = new ConcurrentHashMap<>(); + + private ApplicationEventPublisher applicationEventPublisher; + + public CachingRouteLocator(RouteLocator delegate) { + // 是这个 CompositeRouteLocator + this.delegate = delegate; + // 使用 ConcurrentHashMap 缓存 Route,缓存中没有就执行 fetch 方法得到 + routes = CacheFlux.lookup(cache, CACHE_KEY, Route.class).onCacheMissResume(this::fetch); + } + + private Flux fetch() { + // 通过 delegate 得到,然后再对 Route 进行排序 + return this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE); + } + + @Override + public Flux getRoutes() { + return this.routes; + } + + public Flux refresh() { + // 清空缓存 + this.cache.clear(); + return this.routes; + } + + @Override + public void onApplicationEvent(RefreshRoutesEvent event) { + // 收到事件,就执行 fetch() 也就是会会重新解析得到 List + fetch().collect(Collectors.toList()).subscribe( + list -> Flux.fromIterable(list).materialize().collect(Collectors.toList()).subscribe(signals -> { + // 发布 RefreshRoutesResultEvent 事件 + applicationEventPublisher.publishEvent(new RefreshRoutesResultEvent(this)); + // 重新设置缓存内容 + cache.put(CACHE_KEY, signals); + }, this::handleRefreshError), this::handleRefreshError); + } + +} +``` + +## RouteDefinitionRouteLocator + +RouteDefinitionRouteLocator 是由 [GatewayAutoConfiguration](#GatewayAutoConfiguration) 注册的。它依赖 [RouteDefinitionLocator](#RouteDefinitionLocator) 得到 `Flux`。根据 RouteDefinition 记录的 PredicateDefinition 的 name 得到 `RoutePredicateFactory`, 使用 ConfigurationService 用来 实例化、属性绑定、属性校验得到泛型 Config 的实例对象,最后 RoutePredicateFactory 根据 Config 生成 AsyncPredicate。 + +根据 RouteDefinition 记录的 FilterDefinition 的 name 得到 `GatewayFilterFactory`, 使用 ConfigurationService 用来 实例化、属性绑定、属性校验得到泛型 Config 的实例对象,最后 GatewayFilterFactory 根据 Config 生成 GatewayFilter。 + +然后使用 AsyncPredicate、GatewayFilter 构造出 Route 实例 + +```java +public class RouteDefinitionRouteLocator implements RouteLocator { + /** + * 通过依赖注入得到 + */ + public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator, + List predicates, List gatewayFilterFactories, + GatewayProperties gatewayProperties, ConfigurationService configurationService) { + this.routeDefinitionLocator = routeDefinitionLocator; + this.configurationService = configurationService; + /** + * 将 List 转成 Map,key 是执行 {@link RoutePredicateFactory#name()} 得到的。 + * 默认的逻辑是 类名去除 RoutePredicateFactory + * 比如 AddHeadRoutePredicateFactory 的key是 AddHead + * */ + initFactories(predicates); + /** + * 逻辑同上 {@link GatewayFilterFactory#name()} + * 默认的逻辑是 类名去除 GatewayFilterFactory + * 比如 AddRequestHeaderGatewayFilterFactory 的key是 AddRequestHeader + * */ + gatewayFilterFactories.forEach(factory -> this.gatewayFilterFactories.put(factory.name(), factory)); + this.gatewayProperties = gatewayProperties; + } + + @Override + public Flux getRoutes() { + /** + * 通过 RouteDefinitionLocator 得到 RouteDefinition ,然后根据 RouteDefinition 转成 Route + * */ + Flux routes = this.routeDefinitionLocator.getRouteDefinitions().map(this::convertToRoute); + return routes; + } + + private Route convertToRoute(RouteDefinition routeDefinition) { + /** + * 会根据定义 predicates 的顺序,遍历处理。根据 predicate.getName() 找到 RoutePredicateFactory, + * 再使用 factory 生成 AsyncPredicate + * */ + AsyncPredicate predicate = combinePredicates(routeDefinition); + /** + * 先处理通过属性定义的 默认Filter(spring.cloud.gateway.defaultFilters),再根据定义 filters 的顺序,遍历处理。根据 filter.getName() 找到 GatewayFilterFactory, + * 再使用 factory 生成 GatewayFilter + * + * 最后会根据 order 进行排序。 + * */ + List gatewayFilters = getFilters(routeDefinition); + + // 构造出 Route + return Route.async(routeDefinition).asyncPredicate(predicate).replaceFilters(gatewayFilters).build(); + } + + @SuppressWarnings("unchecked") + List loadGatewayFilters(String id, List filterDefinitions) { + ArrayList ordered = new ArrayList<>(filterDefinitions.size()); + for (int i = 0; i < filterDefinitions.size(); i++) { + FilterDefinition definition = filterDefinitions.get(i); + // 根据 definition.getName() 拿到 GatewayFilterFactory + GatewayFilterFactory factory = this.gatewayFilterFactories.get(definition.getName()); + if (factory == null) { + throw new IllegalArgumentException( + "Unable to find GatewayFilterFactory with name " + definition.getName()); + } + + /** + * 使用 configurationService 生成 configuration + * + * 和这个是一样的的,看这里就知道了 + * {@link RouteDefinitionRouteLocator#lookup(RouteDefinition, PredicateDefinition)} + * */ + // @formatter:off + Object configuration = this.configurationService.with(factory) + .name(definition.getName()) + .properties(definition.getArgs()) + .eventFunction((bound, properties) -> new FilterArgsEvent( + // TODO: why explicit cast needed or java compile fails + RouteDefinitionRouteLocator.this, id, (Map) properties)) + .bind(); + // @formatter:on + + if (configuration instanceof HasRouteId) { + HasRouteId hasRouteId = (HasRouteId) configuration; + // 设置 routeId + hasRouteId.setRouteId(id); + } + + // factory 根据 configuration 生成 GatewayFilter + GatewayFilter gatewayFilter = factory.apply(configuration); + if (gatewayFilter instanceof Ordered) { + ordered.add(gatewayFilter); + } + else { + // 默认的 order 值 就是 定义 filter 的顺序 + ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1)); + } + } + + return ordered; + } + + private List getFilters(RouteDefinition routeDefinition) { + List filters = new ArrayList<>(); + + if (!this.gatewayProperties.getDefaultFilters().isEmpty()) { + /** + * 先添加通过属性定义的默认Filter + * spring.cloud.gateway.defaultFilters=[f1,f2] + * */ + filters.addAll(loadGatewayFilters(routeDefinition.getId(), + new ArrayList<>(this.gatewayProperties.getDefaultFilters()))); + } + + final List definitionFilters = routeDefinition.getFilters(); + if (!CollectionUtils.isEmpty(definitionFilters)) { + // 再添加 RouteDefinition 定义的 filter + filters.addAll(loadGatewayFilters(routeDefinition.getId(), definitionFilters)); + } + + // 排序 + AnnotationAwareOrderComparator.sort(filters); + return filters; + } + + private AsyncPredicate combinePredicates(RouteDefinition routeDefinition) { + List predicates = routeDefinition.getPredicates(); + // routeDefinition 没有定义 predicate ,就设置一个返回 ture 的 AsyncPredicate + if (predicates == null || predicates.isEmpty()) { + // this is a very rare case, but possible, just match all + return AsyncPredicate.from(exchange -> true); + } + + /** + * 获取 AsyncPredicate。 + * + * 会根据 predicate.getName() 拿到 RoutePredicateFactory,执行 RoutePredicateFactory.apply(config) 生成 AsyncPredicate + * */ + AsyncPredicate predicate = lookup(routeDefinition, predicates.get(0)); + // 遍历剩下的 predicate + for (PredicateDefinition andPredicate : predicates.subList(1, predicates.size())) { + AsyncPredicate found = lookup(routeDefinition, andPredicate); + /** + * and 的连接多个 predicate。返回的是这个类型 AndAsyncPredicate + * + * 其实就是不断的套娃。 + * */ + predicate = predicate.and(found); + } + + return predicate; + } + + @SuppressWarnings("unchecked") + private AsyncPredicate lookup(RouteDefinition route, PredicateDefinition predicate) { + /** + * predicates 是根据 BeanFactory 中 RoutePredicateFactory 类型的 bean 生成的。 + * 所以可以理解成是从 BeanFactory 中得到 RoutePredicateFactory。 + * */ + RoutePredicateFactory factory = this.predicates.get(predicate.getName()); + if (factory == null) { + throw new IllegalArgumentException("Unable to find RoutePredicateFactory with name " + predicate.getName()); + } + if (logger.isDebugEnabled()) { + logger.debug("RouteDefinition " + route.getId() + " applying " + predicate.getArgs() + " to " + + predicate.getName()); + } + + /** + * factory 实现 ShortcutConfigurable 接口,规定如何生成的 属性绑定的Map + * factory 实现 Configurable 接口,规定使用 config 是啥 + * + * configurationService 会依赖 factory 生成 属性绑定的Map 得到 Config 的类型 + * 然后使用 属性绑定的Map + ConversionsService + Validator 实例化 Config ,并且会对 Config 进行属性绑定和属性校验(JSR303) + * */ + // @formatter:off + Object config = this.configurationService.with(factory) + .name(predicate.getName()) + // 设置属性。会根据这个生成用于属性绑定的Map + .properties(predicate.getArgs()) + // 定义事件。对 config 完成属性绑定完后,会发布这个事件 + .eventFunction((bound, properties) -> new PredicateArgsEvent( + RouteDefinitionRouteLocator.this, route.getId(), properties)) + .bind(); + // @formatter:on + + // 根据 config 使用 factory 生成 AsyncPredicate + return factory.applyAsync(config); + } + +} +``` + +## RouteDefinitionLocator + +RouteDefinitionLocator 是由 [GatewayAutoConfiguration](#GatewayAutoConfiguration) 注册的,因为标注了 @Primary,所以 [RouteDefinitionRouteLocator](#RouteDefinitionRouteLocator) 通过依赖注入拿到的是 CompositeRouteDefinitionLocator。 + +CompositeRouteDefinitionLocator 的作用是聚合容器中所有的 RouteDefinitionLocator。默认是注册了 [PropertiesRouteDefinitionLocator](#PropertiesRouteDefinitionLocator) 和 [InMemoryRouteDefinitionRepository](#InMemoryRouteDefinitionRepository) + +## PropertiesRouteDefinitionLocator + +PropertiesRouteDefinitionLocator 是由 [GatewayAutoConfiguration](#GatewayAutoConfiguration) 注册的。 + +```java +public class PropertiesRouteDefinitionLocator implements RouteDefinitionLocator { + public PropertiesRouteDefinitionLocator(GatewayProperties properties) { + /** + * 依赖注入得到的 + * + * GatewayProperties 标注了 @ConfigurationProperties("spring.cloud.gateway") + * 所以会通过属性绑定设置值 + * */ + this.properties = properties; + } + + @Override + public Flux getRouteDefinitions() { + // 直接返回 properties.getRoutes() + return Flux.fromIterable(this.properties.getRoutes()); + } +} +``` + +看 PredicateDefinition、FilterDefinition 的构造器,就能明白属性文件为啥可以写 `Weight=group1,8` + +![image-20230428141218057](../../images/SpringCloud/spring-cloud-gateway-source-note_imgs/image-20230428141218057-1682662351478.png) + +## InMemoryRouteDefinitionRepository + +InMemoryRouteDefinitionRepository 是由 [GatewayAutoConfiguration](#GatewayAutoConfiguration) 注册的。它主要是实现了 RouteDefinitionRepository 接口,而 RouteDefinitionRepository 继承 RouteDefinitionLocator,RouteDefinitionWriter 接口。 + +RouteDefinitionRepository 的职责是通过缓存的方式记录 RouteDefinition,而不是通过属性 映射成 RouteDefinition。而 [AbstractGatewayControllerEndpoint](#AbstractGatewayControllerEndpoint) 会依赖 RouteDefinitionWriter 的实例,用来缓存通过接口方式注册的 RouteDefinition。 + +![RouteDefinitionRepository](../../images/SpringCloud/spring-cloud-gateway-source-note_imgs/RouteDefinitionRepository.png) + +```java +public class InMemoryRouteDefinitionRepository implements RouteDefinitionRepository { + // 线程安全的 + private final Map routes = synchronizedMap(new LinkedHashMap()); + + @Override + public Mono save(Mono route) { + /** + * Gateway Endpoint 会依赖 RouteDefinitionRepository 类型的bean 记录通过 Endpoint 动态添加的 RouteDefinition + * + * 源码在这里 + * {@link AbstractGatewayControllerEndpoint#save(String, RouteDefinition)} + * */ + return route.flatMap(r -> { + if (ObjectUtils.isEmpty(r.getId())) { + return Mono.error(new IllegalArgumentException("id may not be empty")); + } + // 存到缓存中 + routes.put(r.getId(), r); + return Mono.empty(); + }); + } + + @Override + public Mono delete(Mono routeId) { + return routeId.flatMap(id -> { + if (routes.containsKey(id)) { + // 从缓存中移除 + routes.remove(id); + return Mono.empty(); + } + return Mono.defer(() -> Mono.error(new NotFoundException("RouteDefinition not found: " + routeId))); + }); + } + + @Override + public Flux getRouteDefinitions() { + // 返回缓存中的值 + Map routesSafeCopy = new LinkedHashMap<>(routes); + return Flux.fromIterable(routesSafeCopy.values()); + } + +} +``` + +## AbstractGatewayControllerEndpoint + +GatewayControllerEndpoint 和 GatewayLegacyControllerEndpoint 是由 [GatewayAutoConfiguration](#GatewayAutoConfiguration) 注册的,默认是注册 GatewayControllerEndpoint ,可以设置属性`spring.cloud.gateway.actuator.verbose.enabled=false` 变成让 GatewayLegacyControllerEndpoint 生效。 + +主要是提供这些功能:查看 RouteDefinitions、Routes、GlobalFilters、routefilters、routepredicates、更新或者新增 RouteDefinition、刷新 RouteDefinition。 + +更新或新增 RouteDefinition 是依赖 [RouteDefinitionRepository](#InMemoryRouteDefinitionRepository) 进行缓存。 + +刷新 RouteDefinition 是会发布 RefreshRoutesEvent 事件,该事件会有 [CachingRouteLocator](#RouteLocator) 处理 + +![AbstractGatewayControllerEndpoint](../../images/SpringCloud/spring-cloud-gateway-source-note_imgs/AbstractGatewayControllerEndpoint.png) + +## RouteRefreshListener + +RouteRefreshListener 是由 [GatewayAutoConfiguration](#GatewayAutoConfiguration) 注册的 + +```java +public class RouteRefreshListener implements ApplicationListener { + + @Override + public void onApplicationEvent(ApplicationEvent event) { + // 这是 IOC 容器 refresh 的最后阶段会发布的事件 + if (event instanceof ContextRefreshedEvent) { + ContextRefreshedEvent refreshedEvent = (ContextRefreshedEvent) event; + // 不是 + if (!WebServerApplicationContext.hasServerNamespace(refreshedEvent.getApplicationContext(), "management")) { + /** + * 重设 + * + * 其实就是发布一个 RefreshRoutesEvent 事件, + * 该事件会由 CachingRouteLocator 接收,会对 List 进行缓存 + * {@link CachingRouteLocator#onApplicationEvent(RefreshRoutesEvent)} + * */ + reset(); + } + } + else if (event instanceof RefreshScopeRefreshedEvent || event instanceof InstanceRegisteredEvent) { + reset(); + } + else if (event instanceof ParentHeartbeatEvent) { + ParentHeartbeatEvent e = (ParentHeartbeatEvent) event; + resetIfNeeded(e.getValue()); + } + else if (event instanceof HeartbeatEvent) { + HeartbeatEvent e = (HeartbeatEvent) event; + resetIfNeeded(e.getValue()); + } + } + + private void resetIfNeeded(Object value) { + if (this.monitor.update(value)) { + reset(); + } + } + + private void reset() { + this.publisher.publishEvent(new RefreshRoutesEvent(this)); + } + +} +``` + +## FilteringWebHandler + +FilteringWebHandler 是由 [GatewayAutoConfiguration](#GatewayAutoConfiguration) 注册的。 + +拿到容器中的 `List + Route.getFilters()` 对 Filter 进行排序,紧接着按顺序执行所有的 GatewayFilter。这么说是不准确的,只有每个 filter 都执行`chain.fiter` 才会执行所有的 GatewayFilter,这其实就是责任链模式。 + +优先级最高(最后执行) 的 Filter 是 [NettyRoutingFilter](#NettyRoutingFilter) ,它是用来执行 http、https 请求的,也就是完成路由的职责。 + +```java +public class FilteringWebHandler implements WebHandler { + public FilteringWebHandler(List globalFilters) { + // 这是依赖注入得到的 + this.globalFilters = loadFilters(globalFilters); + } + + private static List loadFilters(List filters) { + return filters.stream().map(filter -> { + // 装饰成 GatewayFilter 类型 + GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter); + if (filter instanceof Ordered) { + int order = ((Ordered) filter).getOrder(); + return new OrderedGatewayFilter(gatewayFilter, order); + } + return gatewayFilter; + }).collect(Collectors.toList()); + } + @Override + public Mono handle(ServerWebExchange exchange) { + /** + * 拿到 Route 实例。这个是在前一个步骤设置的 + * {@link RoutePredicateHandlerMapping#getHandlerInternal(ServerWebExchange)} + * */ + Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR); + // 拿到 Route 的 GatewayFilter + List gatewayFilters = route.getFilters(); + + // 先添加 globalFilter + List combined = new ArrayList<>(this.globalFilters); + // 再添加 route 定义的 Filter + combined.addAll(gatewayFilters); + + // 排序 + AnnotationAwareOrderComparator.sort(combined); + + /** + * 装饰成 DefaultGatewayFilterChain 执行。 + * + * 其实就是遍历执行所有的 GatewayFilter + * */ + return new DefaultGatewayFilterChain(combined).filter(exchange); + } + + private static class DefaultGatewayFilterChain implements GatewayFilterChain { + @Override + public Mono filter(ServerWebExchange exchange) { + return Mono.defer(() -> { + if (this.index < filters.size()) { + GatewayFilter filter = filters.get(this.index); + // 套娃行为 + DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this, this.index + 1); + // 执行 + return filter.filter(exchange, chain); + } + else { + return Mono.empty(); // complete + } + }); + } + + } +} +``` diff --git a/images/SpringCloud/spring-cloud-gateway-source-note_imgs/AbstractGatewayControllerEndpoint.png b/images/SpringCloud/spring-cloud-gateway-source-note_imgs/AbstractGatewayControllerEndpoint.png new file mode 100644 index 0000000..cc8da3d Binary files /dev/null and b/images/SpringCloud/spring-cloud-gateway-source-note_imgs/AbstractGatewayControllerEndpoint.png differ diff --git a/images/SpringCloud/spring-cloud-gateway-source-note_imgs/OnEnabledComponent.png b/images/SpringCloud/spring-cloud-gateway-source-note_imgs/OnEnabledComponent.png new file mode 100644 index 0000000..8428ea5 Binary files /dev/null and b/images/SpringCloud/spring-cloud-gateway-source-note_imgs/OnEnabledComponent.png differ diff --git a/images/SpringCloud/spring-cloud-gateway-source-note_imgs/Route.png b/images/SpringCloud/spring-cloud-gateway-source-note_imgs/Route.png new file mode 100644 index 0000000..ac6398c Binary files /dev/null and b/images/SpringCloud/spring-cloud-gateway-source-note_imgs/Route.png differ diff --git a/images/SpringCloud/spring-cloud-gateway-source-note_imgs/RouteDefinitionRepository.png b/images/SpringCloud/spring-cloud-gateway-source-note_imgs/RouteDefinitionRepository.png new file mode 100644 index 0000000..e06ef29 Binary files /dev/null and b/images/SpringCloud/spring-cloud-gateway-source-note_imgs/RouteDefinitionRepository.png differ diff --git a/images/SpringCloud/spring-cloud-gateway-source-note_imgs/RouteLocator.png b/images/SpringCloud/spring-cloud-gateway-source-note_imgs/RouteLocator.png new file mode 100644 index 0000000..bf6add0 Binary files /dev/null and b/images/SpringCloud/spring-cloud-gateway-source-note_imgs/RouteLocator.png differ diff --git a/images/SpringCloud/spring-cloud-gateway-source-note_imgs/image-20230428141218057-1682662351478.png b/images/SpringCloud/spring-cloud-gateway-source-note_imgs/image-20230428141218057-1682662351478.png new file mode 100644 index 0000000..31fbb25 Binary files /dev/null and b/images/SpringCloud/spring-cloud-gateway-source-note_imgs/image-20230428141218057-1682662351478.png differ diff --git a/images/SpringCloud/spring-cloud-gateway-source-note_imgs/image-20230428141218057.png b/images/SpringCloud/spring-cloud-gateway-source-note_imgs/image-20230428141218057.png new file mode 100644 index 0000000..31fbb25 Binary files /dev/null and b/images/SpringCloud/spring-cloud-gateway-source-note_imgs/image-20230428141218057.png differ