zoukankan      html  css  js  c++  java
  • gateway网关原理

      Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。其核心逻辑是路由转发+执行过滤器链。Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。

      Gateway 的目标,不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。

      Gateway的三个核心组件: Route(路由)、Predicate(断言)、Filter(过滤器)。

    1. 项目配置

      路由配置的时候可以配置断言、以及过滤器。

    1. 路由配置

    路由配置有两种方式,一种是yml 配置, 另一种是代码配置

    1. yml 配置

    server:
      port: 9527
    
    spring:
      application:
        name: cloud-gateway
      cloud:
        gateway:
          routes:
            - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
    #          uri: http://localhost:8081          #匹配后提供服务的路由地址
              uri: lb://cloud-payment-service          #根据服务名称进行负载均衡替换
              predicates:
                - Path=/pay/listAll/**         # 断言,路径相匹配的进行路由
                - Host=**.com
    
            - id: cloud-provider-hystrix-payment #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
              uri: lb://cloud-provider-hystrix-payment          #根据服务名称进行负载均衡替换
              predicates:
                - Path=/hystrix/**         # 断言,路径相匹配的进行路由
              filters:
                # 限流过滤器,使用gateway内置令牌算法
                - name: RequestRateLimiter
                  args:
                    # 令牌桶每秒填充平均速率,即等价于允许用户每秒处理多少个请求平均数
                    redis-rate-limiter.replenishRate: 1
                    # 令牌桶的容量,允许在一秒钟内完成的最大请求数
                    redis-rate-limiter.burstCapacity: 2
                    # 用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象。
                    key-resolver: "#{@apiKeyResolver}"
    
            - id: payment_routh2 #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
    #          uri: http://localhost:8081          #匹配后提供服务的路由地址
              uri: lb://cloud-payment-service          #根据服务名称进行负载均衡替换
              predicates:
                - Path=/pay/getServerPort/**         # 断言,路径相匹配的进行路由
    #            - After=2020-03-12T15:44:15.064+08:00[Asia/Shanghai] #日期后面(用于判断日期)
    #            - Before=2020-03-12T15:44:15.064+08:00[Asia/Shanghai] #日期后面(用于判断日期)
    #            - Between=2020-03-12T15:44:15.064+08:00[Asia/Shanghai], 2021-03-12T15:44:15.064+08:00[Asia/Shanghai] #日期之间(用于判断日期)
    #            - Cookie=uname,zs   #带Cookie,并且uname的值为zs
                - Header=X-Request-Id,\d+ #请求头要有 X-Request-Id属性并且值为整数的正则表达式
                - Header=X-Request-Id2,\d+ #请求头要有 X-Request-Id属性并且值为整数的正则表达式
    
    eureka:
      instance:
        hostname: cloud-gateway-service
      client: #服务提供者provider注册进eureka服务列表内
        service-url:
          register-with-eureka: true
          fetch-registry: true
          defaultZone: http://localhost:7001/eureka

    2. 代码配置

    package cn.qz.cloud.config;
    
    import org.springframework.cloud.gateway.route.RouteLocator;
    import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class GatewayConfig {
        /**
         * 配置了一个id为route-name的路由规则
         * 当访问地址 http://localhost:9527/guonei时会自动转发到地址: http://news.baidu.com/guonei
         *
         * @param builder
         * @return
         */
        @Bean
        public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
            RouteLocatorBuilder.Builder routes = builder.routes();
            routes.route("path_route_eiletxie",
                    r -> r.path("/guonei")
                            .uri("http://news.baidu.com/guonei")).build();
            return routes.build();
        }
    
        @Bean
        public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) {
            RouteLocatorBuilder.Builder routes = builder.routes();
            routes.route("path_route_eiletxie2",
                    r -> r.path("/guoji")
                            .uri("http://news.baidu.com/guoji")).build();
            return routes.build();
        }
    }

      代码内部的配置,相当于没有使用lb 负载均衡,相当于直接是请求转发的功能。

    2. 全局过滤器配置(会应用到每个路由中)

      Gateway 也可以单独增加全局的filter, 如下:

    package cn.qz.cloud.filter;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.core.Ordered;
    import org.springframework.core.io.buffer.DataBuffer;
    import org.springframework.core.io.buffer.DataBufferUtils;
    import org.springframework.http.HttpMethod;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
    import org.springframework.stereotype.Component;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Flux;
    import reactor.core.publisher.Mono;
    
    import java.nio.charset.StandardCharsets;
    import java.util.Map;
    import java.util.stream.Collectors;
    
    @Component
    public class LoggerFilter implements GlobalFilter, Ordered {
    
        private static final Logger log = LoggerFactory.getLogger(LoggerFilter.class);
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            ServerHttpRequest request = exchange.getRequest();
            String method = request.getMethodValue();
    
            if (HttpMethod.POST.matches(method)) {
                return DataBufferUtils.join(exchange.getRequest().getBody())
                        .flatMap(dataBuffer -> {
                            byte[] bytes = new byte[dataBuffer.readableByteCount()];
                            dataBuffer.read(bytes);
                            String bodyString = new String(bytes, StandardCharsets.UTF_8);
                            logtrace(exchange, bodyString);
                            exchange.getAttributes().put("POST_BODY", bodyString);
                            DataBufferUtils.release(dataBuffer);
                            Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
                                DataBuffer buffer = exchange.getResponse().bufferFactory()
                                        .wrap(bytes);
                                return Mono.just(buffer);
                            });
    
                            ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(
                                    exchange.getRequest()) {
                                @Override
                                public Flux<DataBuffer> getBody() {
                                    return cachedFlux;
                                }
                            };
                            return chain.filter(exchange.mutate().request(mutatedRequest)
                                    .build());
                        });
            } else if (HttpMethod.GET.matches(method)) {
                Map m = request.getQueryParams();
                logtrace(exchange, m.toString());
            }
            return chain.filter(exchange);
        }
    
        /**
         * 日志信息
         *
         * @param exchange
         * @param param    请求参数
         */
        private void logtrace(ServerWebExchange exchange, String param) {
            ServerHttpRequest serverHttpRequest = exchange.getRequest();
            String hostString = serverHttpRequest.getRemoteAddress().getHostString();
            String path = serverHttpRequest.getURI().getPath();
            String method = serverHttpRequest.getMethodValue();
            String headers = serverHttpRequest.getHeaders().entrySet()
                    .stream()
                    .map(entry -> "            " + entry.getKey() + ": [" + String.join(";", entry.getValue()) + "]")
                    .collect(Collectors.joining("\n"));
            log.info("\n" + "----------------             ----------------             ---------------->>\n" +
                            "HttpMethod : {}\n" +
                            "requestHost : {}\n" +
                            "Uri        : {}\n" +
                            "Param      : {}\n" +
                            "Headers    : \n" +
                            "{}\n" +
                            "\"<<----------------             ----------------             ----------------"
                    , method, hostString, path, param, headers);
        }
    
        @Override
        public int getOrder() {
            return -1;
        }
    }

      下面研究其作用过程。

     2. 启动过程查看

      按照Springboot 的套路,查看一个配置从AutoConfiguration 类查看。

    1. 配置累和properties 查看

    org.springframework.cloud.gateway.config.GatewayAutoConfiguration 自动配置类源码如下:(可以看到ConditionalOnProperty、AutoConfigureBefore、AutoConfigureAfter、ConditionalOnClass、ConditionalOnMissingBean 等条件注解)

    /*
     * Copyright 2013-2019 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      https://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.cloud.gateway.config;
    
    import java.security.cert.X509Certificate;
    import java.util.List;
    
    import com.netflix.hystrix.HystrixObservableCommand;
    import io.netty.channel.ChannelOption;
    import io.netty.handler.ssl.SslContextBuilder;
    import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import reactor.core.publisher.Flux;
    import reactor.netty.http.client.HttpClient;
    import reactor.netty.resources.ConnectionProvider;
    import reactor.netty.tcp.ProxyProvider;
    import rx.RxReactiveStreams;
    
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.ObjectProvider;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
    import org.springframework.boot.actuate.health.Health;
    import org.springframework.boot.autoconfigure.AutoConfigureAfter;
    import org.springframework.boot.autoconfigure.AutoConfigureBefore;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.boot.autoconfigure.condition.NoneNestedConditions;
    import org.springframework.boot.autoconfigure.web.ServerProperties;
    import org.springframework.boot.autoconfigure.web.embedded.NettyWebServerFactoryCustomizer;
    import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration;
    import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.boot.context.properties.PropertyMapper;
    import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
    import org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint;
    import org.springframework.cloud.gateway.actuate.GatewayLegacyControllerEndpoint;
    import org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter;
    import org.springframework.cloud.gateway.filter.ForwardPathFilter;
    import org.springframework.cloud.gateway.filter.ForwardRoutingFilter;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.cloud.gateway.filter.NettyRoutingFilter;
    import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
    import org.springframework.cloud.gateway.filter.RemoveCachedBodyFilter;
    import org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter;
    import org.springframework.cloud.gateway.filter.WebsocketRoutingFilter;
    import org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter;
    import org.springframework.cloud.gateway.filter.factory.AddRequestHeaderGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.AddRequestParameterGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.AddResponseHeaderGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.DedupeResponseHeaderGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.FallbackHeadersGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.HystrixGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.MapRequestHeaderGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.PrefixPathGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.PreserveHostHeaderGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.RedirectToGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.RemoveRequestHeaderGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.RemoveRequestParameterGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.RemoveResponseHeaderGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.RequestHeaderSizeGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.RequestHeaderToRequestUriGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.RequestSizeGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.RetryGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.RewriteLocationResponseHeaderGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.RewritePathGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.RewriteResponseHeaderGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.SaveSessionGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.SecureHeadersGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.SecureHeadersProperties;
    import org.springframework.cloud.gateway.filter.factory.SetPathGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.SetRequestHeaderGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.SetResponseHeaderGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.SetStatusGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.StripPrefixGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyResponseBodyGatewayFilterFactory;
    import org.springframework.cloud.gateway.filter.headers.ForwardedHeadersFilter;
    import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter;
    import org.springframework.cloud.gateway.filter.headers.RemoveHopByHopHeadersFilter;
    import org.springframework.cloud.gateway.filter.headers.XForwardedHeadersFilter;
    import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
    import org.springframework.cloud.gateway.filter.ratelimit.PrincipalNameKeyResolver;
    import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter;
    import org.springframework.cloud.gateway.handler.FilteringWebHandler;
    import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping;
    import org.springframework.cloud.gateway.handler.predicate.AfterRoutePredicateFactory;
    import org.springframework.cloud.gateway.handler.predicate.BeforeRoutePredicateFactory;
    import org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactory;
    import org.springframework.cloud.gateway.handler.predicate.CloudFoundryRouteServiceRoutePredicateFactory;
    import org.springframework.cloud.gateway.handler.predicate.CookieRoutePredicateFactory;
    import org.springframework.cloud.gateway.handler.predicate.HeaderRoutePredicateFactory;
    import org.springframework.cloud.gateway.handler.predicate.HostRoutePredicateFactory;
    import org.springframework.cloud.gateway.handler.predicate.MethodRoutePredicateFactory;
    import org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory;
    import org.springframework.cloud.gateway.handler.predicate.QueryRoutePredicateFactory;
    import org.springframework.cloud.gateway.handler.predicate.ReadBodyPredicateFactory;
    import org.springframework.cloud.gateway.handler.predicate.RemoteAddrRoutePredicateFactory;
    import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
    import org.springframework.cloud.gateway.handler.predicate.WeightRoutePredicateFactory;
    import org.springframework.cloud.gateway.route.CachingRouteLocator;
    import org.springframework.cloud.gateway.route.CompositeRouteDefinitionLocator;
    import org.springframework.cloud.gateway.route.CompositeRouteLocator;
    import org.springframework.cloud.gateway.route.InMemoryRouteDefinitionRepository;
    import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
    import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
    import org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator;
    import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
    import org.springframework.cloud.gateway.route.RouteLocator;
    import org.springframework.cloud.gateway.route.RouteRefreshListener;
    import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
    import org.springframework.cloud.gateway.support.ConfigurationService;
    import org.springframework.cloud.gateway.support.StringToZonedDateTimeConverter;
    import org.springframework.context.ApplicationEventPublisher;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Conditional;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.DependsOn;
    import org.springframework.context.annotation.Primary;
    import org.springframework.core.convert.ConversionService;
    import org.springframework.core.env.Environment;
    import org.springframework.http.codec.ServerCodecConfigurer;
    import org.springframework.util.StringUtils;
    import org.springframework.validation.Validator;
    import org.springframework.web.reactive.DispatcherHandler;
    import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient;
    import org.springframework.web.reactive.socket.client.WebSocketClient;
    import org.springframework.web.reactive.socket.server.WebSocketService;
    import org.springframework.web.reactive.socket.server.support.HandshakeWebSocketService;
    
    import static org.springframework.cloud.gateway.config.HttpClientProperties.Pool.PoolType.DISABLED;
    import static org.springframework.cloud.gateway.config.HttpClientProperties.Pool.PoolType.FIXED;
    
    /**
     * @author Spencer Gibb
     * @author Ziemowit Stolarczyk
     */
    @Configuration(proxyBeanMethods = false)
    @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 {
    
        @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 ConfigurationService gatewayConfigurationService(BeanFactory beanFactory,
                @Qualifier("webFluxConversionService") ConversionService conversionService,
                Validator validator) {
            return new ConfigurationService(beanFactory, conversionService, validator);
        }
    
        @Bean
        public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
                List<GatewayFilterFactory> gatewayFilters,
                List<RoutePredicateFactory> predicates,
                RouteDefinitionLocator routeDefinitionLocator,
                ConfigurationService configurationService) {
            return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates,
                    gatewayFilters, properties, configurationService);
        }
    
        @Bean
        @Primary
        @ConditionalOnMissingBean(name = "cachedCompositeRouteLocator")
        // TODO: property to disable composite?
        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();
        }
    
        // ConfigurationProperty beans
    
        @Bean
        public SecureHeadersProperties secureHeadersProperties() {
            return new SecureHeadersProperties();
        }
    
        @Bean
        @ConditionalOnProperty(name = "spring.cloud.gateway.forwarded.enabled",
                matchIfMissing = true)
        public ForwardedHeadersFilter forwardedHeadersFilter() {
            return new ForwardedHeadersFilter();
        }
    
        // HttpHeaderFilter beans
    
        @Bean
        public RemoveHopByHopHeadersFilter removeHopByHopHeadersFilter() {
            return new RemoveHopByHopHeadersFilter();
        }
    
        @Bean
        @ConditionalOnProperty(name = "spring.cloud.gateway.x-forwarded.enabled",
                matchIfMissing = true)
        public XForwardedHeadersFilter xForwardedHeadersFilter() {
            return new XForwardedHeadersFilter();
        }
    
        // GlobalFilter beans
    
        @Bean
        public AdaptCachedBodyGlobalFilter adaptCachedBodyGlobalFilter() {
            return new AdaptCachedBodyGlobalFilter();
        }
    
        @Bean
        public RemoveCachedBodyFilter removeCachedBodyFilter() {
            return new RemoveCachedBodyFilter();
        }
    
        @Bean
        public RouteToRequestUrlFilter routeToRequestUrlFilter() {
            return new RouteToRequestUrlFilter();
        }
    
        @Bean
        public ForwardRoutingFilter forwardRoutingFilter(
                ObjectProvider<DispatcherHandler> dispatcherHandler) {
            return new ForwardRoutingFilter(dispatcherHandler);
        }
    
        @Bean
        public ForwardPathFilter forwardPathFilter() {
            return new ForwardPathFilter();
        }
    
        @Bean
        public WebSocketService webSocketService() {
            return new HandshakeWebSocketService();
        }
    
        @Bean
        public WebsocketRoutingFilter websocketRoutingFilter(WebSocketClient webSocketClient,
                WebSocketService webSocketService,
                ObjectProvider<List<HttpHeadersFilter>> headersFilters) {
            return new WebsocketRoutingFilter(webSocketClient, webSocketService,
                    headersFilters);
        }
    
        @Bean
        public WeightCalculatorWebFilter weightCalculatorWebFilter(
                ConfigurationService configurationService,
                ObjectProvider<RouteLocator> routeLocator) {
            return new WeightCalculatorWebFilter(routeLocator, configurationService);
        }
    
        @Bean
        public AfterRoutePredicateFactory afterRoutePredicateFactory() {
            return new AfterRoutePredicateFactory();
        }
    
        /*
         * @Bean //TODO: default over netty? configurable public WebClientHttpRoutingFilter
         * webClientHttpRoutingFilter() { //TODO: WebClient bean return new
         * WebClientHttpRoutingFilter(WebClient.routes().build()); }
         *
         * @Bean public WebClientWriteResponseFilter webClientWriteResponseFilter() { return
         * new WebClientWriteResponseFilter(); }
         */
    
        // Predicate Factory beans
    
        @Bean
        public BeforeRoutePredicateFactory beforeRoutePredicateFactory() {
            return new BeforeRoutePredicateFactory();
        }
    
        @Bean
        public BetweenRoutePredicateFactory betweenRoutePredicateFactory() {
            return new BetweenRoutePredicateFactory();
        }
    
        @Bean
        public CookieRoutePredicateFactory cookieRoutePredicateFactory() {
            return new CookieRoutePredicateFactory();
        }
    
        @Bean
        public HeaderRoutePredicateFactory headerRoutePredicateFactory() {
            return new HeaderRoutePredicateFactory();
        }
    
        @Bean
        public HostRoutePredicateFactory hostRoutePredicateFactory() {
            return new HostRoutePredicateFactory();
        }
    
        @Bean
        public MethodRoutePredicateFactory methodRoutePredicateFactory() {
            return new MethodRoutePredicateFactory();
        }
    
        @Bean
        public PathRoutePredicateFactory pathRoutePredicateFactory() {
            return new PathRoutePredicateFactory();
        }
    
        @Bean
        public QueryRoutePredicateFactory queryRoutePredicateFactory() {
            return new QueryRoutePredicateFactory();
        }
    
        @Bean
        public ReadBodyPredicateFactory readBodyPredicateFactory() {
            return new ReadBodyPredicateFactory();
        }
    
        @Bean
        public RemoteAddrRoutePredicateFactory remoteAddrRoutePredicateFactory() {
            return new RemoteAddrRoutePredicateFactory();
        }
    
        @Bean
        @DependsOn("weightCalculatorWebFilter")
        public WeightRoutePredicateFactory weightRoutePredicateFactory() {
            return new WeightRoutePredicateFactory();
        }
    
        @Bean
        public CloudFoundryRouteServiceRoutePredicateFactory cloudFoundryRouteServiceRoutePredicateFactory() {
            return new CloudFoundryRouteServiceRoutePredicateFactory();
        }
    
        // GatewayFilter Factory beans
    
        @Bean
        public AddRequestHeaderGatewayFilterFactory addRequestHeaderGatewayFilterFactory() {
            return new AddRequestHeaderGatewayFilterFactory();
        }
    
        @Bean
        public MapRequestHeaderGatewayFilterFactory mapRequestHeaderGatewayFilterFactory() {
            return new MapRequestHeaderGatewayFilterFactory();
        }
    
        @Bean
        public AddRequestParameterGatewayFilterFactory addRequestParameterGatewayFilterFactory() {
            return new AddRequestParameterGatewayFilterFactory();
        }
    
        @Bean
        public AddResponseHeaderGatewayFilterFactory addResponseHeaderGatewayFilterFactory() {
            return new AddResponseHeaderGatewayFilterFactory();
        }
    
        @Bean
        public ModifyRequestBodyGatewayFilterFactory modifyRequestBodyGatewayFilterFactory() {
            return new ModifyRequestBodyGatewayFilterFactory();
        }
    
        @Bean
        public DedupeResponseHeaderGatewayFilterFactory dedupeResponseHeaderGatewayFilterFactory() {
            return new DedupeResponseHeaderGatewayFilterFactory();
        }
    
        @Bean
        public ModifyResponseBodyGatewayFilterFactory modifyResponseBodyGatewayFilterFactory(
                ServerCodecConfigurer codecConfigurer) {
            return new ModifyResponseBodyGatewayFilterFactory(codecConfigurer);
        }
    
        @Bean
        public PrefixPathGatewayFilterFactory prefixPathGatewayFilterFactory() {
            return new PrefixPathGatewayFilterFactory();
        }
    
        @Bean
        public PreserveHostHeaderGatewayFilterFactory preserveHostHeaderGatewayFilterFactory() {
            return new PreserveHostHeaderGatewayFilterFactory();
        }
    
        @Bean
        public RedirectToGatewayFilterFactory redirectToGatewayFilterFactory() {
            return new RedirectToGatewayFilterFactory();
        }
    
        @Bean
        public RemoveRequestHeaderGatewayFilterFactory removeRequestHeaderGatewayFilterFactory() {
            return new RemoveRequestHeaderGatewayFilterFactory();
        }
    
        @Bean
        public RemoveRequestParameterGatewayFilterFactory removeRequestParameterGatewayFilterFactory() {
            return new RemoveRequestParameterGatewayFilterFactory();
        }
    
        @Bean
        public RemoveResponseHeaderGatewayFilterFactory removeResponseHeaderGatewayFilterFactory() {
            return new RemoveResponseHeaderGatewayFilterFactory();
        }
    
        @Bean(name = PrincipalNameKeyResolver.BEAN_NAME)
        @ConditionalOnBean(RateLimiter.class)
        @ConditionalOnMissingBean(KeyResolver.class)
        public PrincipalNameKeyResolver principalNameKeyResolver() {
            return new PrincipalNameKeyResolver();
        }
    
        @Bean
        @ConditionalOnBean({ RateLimiter.class, KeyResolver.class })
        public RequestRateLimiterGatewayFilterFactory requestRateLimiterGatewayFilterFactory(
                RateLimiter rateLimiter, KeyResolver resolver) {
            return new RequestRateLimiterGatewayFilterFactory(rateLimiter, resolver);
        }
    
        @Bean
        public RewritePathGatewayFilterFactory rewritePathGatewayFilterFactory() {
            return new RewritePathGatewayFilterFactory();
        }
    
        @Bean
        public RetryGatewayFilterFactory retryGatewayFilterFactory() {
            return new RetryGatewayFilterFactory();
        }
    
        @Bean
        public SetPathGatewayFilterFactory setPathGatewayFilterFactory() {
            return new SetPathGatewayFilterFactory();
        }
    
        @Bean
        public SecureHeadersGatewayFilterFactory secureHeadersGatewayFilterFactory(
                SecureHeadersProperties properties) {
            return new SecureHeadersGatewayFilterFactory(properties);
        }
    
        @Bean
        public SetRequestHeaderGatewayFilterFactory setRequestHeaderGatewayFilterFactory() {
            return new SetRequestHeaderGatewayFilterFactory();
        }
    
        @Bean
        public SetResponseHeaderGatewayFilterFactory setResponseHeaderGatewayFilterFactory() {
            return new SetResponseHeaderGatewayFilterFactory();
        }
    
        @Bean
        public RewriteResponseHeaderGatewayFilterFactory rewriteResponseHeaderGatewayFilterFactory() {
            return new RewriteResponseHeaderGatewayFilterFactory();
        }
    
        @Bean
        public RewriteLocationResponseHeaderGatewayFilterFactory rewriteLocationResponseHeaderGatewayFilterFactory() {
            return new RewriteLocationResponseHeaderGatewayFilterFactory();
        }
    
        @Bean
        public SetStatusGatewayFilterFactory setStatusGatewayFilterFactory() {
            return new SetStatusGatewayFilterFactory();
        }
    
        @Bean
        public SaveSessionGatewayFilterFactory saveSessionGatewayFilterFactory() {
            return new SaveSessionGatewayFilterFactory();
        }
    
        @Bean
        public StripPrefixGatewayFilterFactory stripPrefixGatewayFilterFactory() {
            return new StripPrefixGatewayFilterFactory();
        }
    
        @Bean
        public RequestHeaderToRequestUriGatewayFilterFactory requestHeaderToRequestUriGatewayFilterFactory() {
            return new RequestHeaderToRequestUriGatewayFilterFactory();
        }
    
        @Bean
        public RequestSizeGatewayFilterFactory requestSizeGatewayFilterFactory() {
            return new RequestSizeGatewayFilterFactory();
        }
    
        @Bean
        public RequestHeaderSizeGatewayFilterFactory requestHeaderSizeGatewayFilterFactory() {
            return new RequestHeaderSizeGatewayFilterFactory();
        }
    
        @Configuration(proxyBeanMethods = false)
        @ConditionalOnClass(HttpClient.class)
        protected static class NettyConfiguration {
    
            protected final Log logger = LogFactory.getLog(getClass());
    
            @Bean
            @ConditionalOnProperty(name = "spring.cloud.gateway.httpserver.wiretap")
            public NettyWebServerFactoryCustomizer nettyServerWiretapCustomizer(
                    Environment environment, ServerProperties serverProperties) {
                return new NettyWebServerFactoryCustomizer(environment, serverProperties) {
                    @Override
                    public void customize(NettyReactiveWebServerFactory factory) {
                        factory.addServerCustomizers(httpServer -> httpServer.wiretap(true));
                        super.customize(factory);
                    }
                };
            }
    
            @Bean
            @ConditionalOnMissingBean
            public HttpClient gatewayHttpClient(HttpClientProperties properties) {
    
                // configure pool resources
                HttpClientProperties.Pool pool = properties.getPool();
    
                ConnectionProvider connectionProvider;
                if (pool.getType() == DISABLED) {
                    connectionProvider = ConnectionProvider.newConnection();
                }
                else if (pool.getType() == FIXED) {
                    connectionProvider = ConnectionProvider.fixed(pool.getName(),
                            pool.getMaxConnections(), pool.getAcquireTimeout(),
                            pool.getMaxIdleTime());
                }
                else {
                    connectionProvider = ConnectionProvider.elastic(pool.getName(),
                            pool.getMaxIdleTime());
                }
    
                HttpClient httpClient = HttpClient.create(connectionProvider)
                        .tcpConfiguration(tcpClient -> {
    
                            if (properties.getConnectTimeout() != null) {
                                tcpClient = tcpClient.option(
                                        ChannelOption.CONNECT_TIMEOUT_MILLIS,
                                        properties.getConnectTimeout());
                            }
    
                            // configure proxy if proxy host is set.
                            HttpClientProperties.Proxy proxy = properties.getProxy();
    
                            if (StringUtils.hasText(proxy.getHost())) {
    
                                tcpClient = tcpClient.proxy(proxySpec -> {
                                    ProxyProvider.Builder builder = proxySpec
                                            .type(ProxyProvider.Proxy.HTTP)
                                            .host(proxy.getHost());
    
                                    PropertyMapper map = PropertyMapper.get();
    
                                    map.from(proxy::getPort).whenNonNull().to(builder::port);
                                    map.from(proxy::getUsername).whenHasText()
                                            .to(builder::username);
                                    map.from(proxy::getPassword).whenHasText()
                                            .to(password -> builder.password(s -> password));
                                    map.from(proxy::getNonProxyHostsPattern).whenHasText()
                                            .to(builder::nonProxyHosts);
                                });
                            }
                            return tcpClient;
                        });
    
                HttpClientProperties.Ssl ssl = properties.getSsl();
                if ((ssl.getKeyStore() != null && ssl.getKeyStore().length() > 0)
                        || ssl.getTrustedX509CertificatesForTrustManager().length > 0
                        || ssl.isUseInsecureTrustManager()) {
                    httpClient = httpClient.secure(sslContextSpec -> {
                        // configure ssl
                        SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();
    
                        X509Certificate[] trustedX509Certificates = ssl
                                .getTrustedX509CertificatesForTrustManager();
                        if (trustedX509Certificates.length > 0) {
                            sslContextBuilder = sslContextBuilder
                                    .trustManager(trustedX509Certificates);
                        }
                        else if (ssl.isUseInsecureTrustManager()) {
                            sslContextBuilder = sslContextBuilder
                                    .trustManager(InsecureTrustManagerFactory.INSTANCE);
                        }
    
                        try {
                            sslContextBuilder = sslContextBuilder
                                    .keyManager(ssl.getKeyManagerFactory());
                        }
                        catch (Exception e) {
                            logger.error(e);
                        }
    
                        sslContextSpec.sslContext(sslContextBuilder)
                                .defaultConfiguration(ssl.getDefaultConfigurationType())
                                .handshakeTimeout(ssl.getHandshakeTimeout())
                                .closeNotifyFlushTimeout(ssl.getCloseNotifyFlushTimeout())
                                .closeNotifyReadTimeout(ssl.getCloseNotifyReadTimeout());
                    });
                }
    
                if (properties.isWiretap()) {
                    httpClient = httpClient.wiretap(true);
                }
    
                return httpClient;
            }
    
            @Bean
            public HttpClientProperties httpClientProperties() {
                return new HttpClientProperties();
            }
    
            @Bean
            public NettyRoutingFilter routingFilter(HttpClient httpClient,
                    ObjectProvider<List<HttpHeadersFilter>> headersFilters,
                    HttpClientProperties properties) {
                return new NettyRoutingFilter(httpClient, headersFilters, properties);
            }
    
            @Bean
            public NettyWriteResponseFilter nettyWriteResponseFilter(
                    GatewayProperties properties) {
                return new NettyWriteResponseFilter(properties.getStreamingMediaTypes());
            }
    
            @Bean
            public ReactorNettyWebSocketClient reactorNettyWebSocketClient(
                    HttpClientProperties properties, HttpClient httpClient) {
                ReactorNettyWebSocketClient webSocketClient = new ReactorNettyWebSocketClient(
                        httpClient);
                if (properties.getWebsocket().getMaxFramePayloadLength() != null) {
                    webSocketClient.setMaxFramePayloadLength(
                            properties.getWebsocket().getMaxFramePayloadLength());
                }
                return webSocketClient;
            }
    
        }
    
        @Configuration(proxyBeanMethods = false)
        @ConditionalOnClass({ HystrixObservableCommand.class, RxReactiveStreams.class })
        protected static class HystrixConfiguration {
    
            @Bean
            public HystrixGatewayFilterFactory hystrixGatewayFilterFactory(
                    ObjectProvider<DispatcherHandler> dispatcherHandler) {
                return new HystrixGatewayFilterFactory(dispatcherHandler);
            }
    
            @Bean
            @ConditionalOnMissingBean(FallbackHeadersGatewayFilterFactory.class)
            public FallbackHeadersGatewayFilterFactory fallbackHeadersGatewayFilterFactory() {
                return new FallbackHeadersGatewayFilterFactory();
            }
    
        }
    
        @Configuration(proxyBeanMethods = false)
        @ConditionalOnClass(Health.class)
        protected static class GatewayActuatorConfiguration {
    
            @Bean
            @ConditionalOnProperty(name = "spring.cloud.gateway.actuator.verbose.enabled",
                    matchIfMissing = true)
            @ConditionalOnAvailableEndpoint
            public GatewayControllerEndpoint gatewayControllerEndpoint(
                    List<GlobalFilter> globalFilters,
                    List<GatewayFilterFactory> gatewayFilters,
                    List<RoutePredicateFactory> routePredicates,
                    RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
                return new GatewayControllerEndpoint(globalFilters, gatewayFilters,
                        routePredicates, routeDefinitionWriter, routeLocator);
            }
    
            @Bean
            @Conditional(OnVerboseDisabledCondition.class)
            @ConditionalOnAvailableEndpoint
            public GatewayLegacyControllerEndpoint gatewayLegacyControllerEndpoint(
                    RouteDefinitionLocator routeDefinitionLocator,
                    List<GlobalFilter> globalFilters,
                    List<GatewayFilterFactory> gatewayFilters,
                    List<RoutePredicateFactory> routePredicates,
                    RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
                return new GatewayLegacyControllerEndpoint(routeDefinitionLocator,
                        globalFilters, gatewayFilters, routePredicates, routeDefinitionWriter,
                        routeLocator);
            }
    
        }
    
        private static class OnVerboseDisabledCondition extends NoneNestedConditions {
    
            OnVerboseDisabledCondition() {
                super(ConfigurationPhase.REGISTER_BEAN);
            }
    
            @ConditionalOnProperty(name = "spring.cloud.gateway.actuator.verbose.enabled",
                    matchIfMissing = true)
            static class VerboseDisabled {
    
            }
    
        }
    
    }
    View Code

    org.springframework.cloud.gateway.config.GatewayProperties 配置类源码如下:(yml 配置的 routes 直接注入到该对象的list 属性中, 这里可以看出也可以配置默认的过滤器, 相当于全局过滤器, 会应用到每个路由中)

    /*
     * Copyright 2013-2019 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      https://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.cloud.gateway.config;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    import javax.validation.Valid;
    import javax.validation.constraints.NotNull;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.cloud.gateway.filter.FilterDefinition;
    import org.springframework.cloud.gateway.route.RouteDefinition;
    import org.springframework.http.MediaType;
    import org.springframework.validation.annotation.Validated;
    
    /**
     * @author Spencer Gibb
     */
    @ConfigurationProperties("spring.cloud.gateway")
    @Validated
    public class GatewayProperties {
    
        private final Log logger = LogFactory.getLog(getClass());
    
        /**
         * List of Routes.
         */
        @NotNull
        @Valid
        private List<RouteDefinition> routes = new ArrayList<>();
    
        /**
         * List of filter definitions that are applied to every route.
         */
        private List<FilterDefinition> defaultFilters = new ArrayList<>();
    
        private List<MediaType> streamingMediaTypes = Arrays
                .asList(MediaType.TEXT_EVENT_STREAM, MediaType.APPLICATION_STREAM_JSON);
    
        public List<RouteDefinition> getRoutes() {
            return routes;
        }
    
        public void setRoutes(List<RouteDefinition> routes) {
            this.routes = routes;
            if (routes != null && routes.size() > 0 && logger.isDebugEnabled()) {
                logger.debug("Routes supplied from Gateway Properties: " + routes);
            }
        }
    
        public List<FilterDefinition> getDefaultFilters() {
            return defaultFilters;
        }
    
        public void setDefaultFilters(List<FilterDefinition> defaultFilters) {
            this.defaultFilters = defaultFilters;
        }
    
        public List<MediaType> getStreamingMediaTypes() {
            return streamingMediaTypes;
        }
    
        public void setStreamingMediaTypes(List<MediaType> streamingMediaTypes) {
            this.streamingMediaTypes = streamingMediaTypes;
        }
    
        @Override
        public String toString() {
            return "GatewayProperties{" + "routes=" + routes + ", defaultFilters="
                    + defaultFilters + ", streamingMediaTypes=" + streamingMediaTypes + '}';
        }
    
    }
    View Code

    可以看到自动配置累注入了几个重要的对象:

    1》 org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder    路由坐标构造器,用于业务直接使用, 比如上面代码配置中方法上自动注入的对象就是这里的Builder

    2》 PropertiesRouteDefinitionLocator、InMemoryRouteDefinitionRepository 是两个RouteDefinitionLocator 对象, 最后再作为组合对象注入到CompositeRouteDefinitionLocator 内部。

    3》 一些RoutePredicateFactory 断言工厂, 和 一些 GatewayFilterFactory 工厂

    4》 一些内置的GateWayFilter, 在处理请求转发过程中也是这些过滤器在发挥重要作用

    5》 routeDefinitionRouteLocator 对象,这个负责解析 properties 文件配置的routes 对象; 然后和之前代码注入的两个RouteLocator, 一起作为一个集合返回一个CachingRouteLocator 对象,如下方法:

        @Bean
        @Primary
        @ConditionalOnMissingBean(name = "cachedCompositeRouteLocator")
        // TODO: property to disable composite?
        public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
            return new CachingRouteLocator(
                    new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
        }

    到这个方法传递的参数如下:

    这里最后会解析所有的RoteDefinition 对象,解析为Route 对象, 并且维护到: org.springframework.cloud.gateway.route.CachingRouteLocator#routes 属性中。

    解析链如下: 核心入库是org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator

    /*
     * Copyright 2013-2019 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      https://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.cloud.gateway.route;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.LinkedHashMap;
    import java.util.List;
    import java.util.Map;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import reactor.core.publisher.Flux;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.BeanFactoryAware;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.gateway.config.GatewayProperties;
    import org.springframework.cloud.gateway.event.FilterArgsEvent;
    import org.springframework.cloud.gateway.event.PredicateArgsEvent;
    import org.springframework.cloud.gateway.filter.FilterDefinition;
    import org.springframework.cloud.gateway.filter.GatewayFilter;
    import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
    import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
    import org.springframework.cloud.gateway.handler.AsyncPredicate;
    import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
    import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
    import org.springframework.cloud.gateway.support.ConfigurationService;
    import org.springframework.cloud.gateway.support.HasRouteId;
    import org.springframework.context.ApplicationEventPublisher;
    import org.springframework.context.ApplicationEventPublisherAware;
    import org.springframework.core.Ordered;
    import org.springframework.core.annotation.AnnotationAwareOrderComparator;
    import org.springframework.core.convert.ConversionService;
    import org.springframework.validation.Validator;
    import org.springframework.web.server.ServerWebExchange;
    
    /**
     * {@link RouteLocator} that loads routes from a {@link RouteDefinitionLocator}.
     *
     * @author Spencer Gibb
     */
    public class RouteDefinitionRouteLocator
            implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware {
    
        /**
         * Default filters name.
         */
        public static final String DEFAULT_FILTERS = "defaultFilters";
    
        protected final Log logger = LogFactory.getLog(getClass());
    
        private final RouteDefinitionLocator routeDefinitionLocator;
    
        private final ConfigurationService configurationService;
    
        private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap<>();
    
        private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap<>();
    
        private final GatewayProperties gatewayProperties;
    
        @Deprecated
        public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,
                List<RoutePredicateFactory> predicates,
                List<GatewayFilterFactory> gatewayFilterFactories,
                GatewayProperties gatewayProperties, ConversionService conversionService) {
            this.routeDefinitionLocator = routeDefinitionLocator;
            this.configurationService = new ConfigurationService();
            this.configurationService.setConversionService(conversionService);
            initFactories(predicates);
            gatewayFilterFactories.forEach(
                    factory -> this.gatewayFilterFactories.put(factory.name(), factory));
            this.gatewayProperties = gatewayProperties;
        }
    
        public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,
                List<RoutePredicateFactory> predicates,
                List<GatewayFilterFactory> gatewayFilterFactories,
                GatewayProperties gatewayProperties,
                ConfigurationService configurationService) {
            this.routeDefinitionLocator = routeDefinitionLocator;
            this.configurationService = configurationService;
            initFactories(predicates);
            gatewayFilterFactories.forEach(
                    factory -> this.gatewayFilterFactories.put(factory.name(), factory));
            this.gatewayProperties = gatewayProperties;
        }
    
        @Override
        @Deprecated
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            if (this.configurationService.getBeanFactory() == null) {
                this.configurationService.setBeanFactory(beanFactory);
            }
        }
    
        @Autowired
        @Deprecated
        public void setValidator(Validator validator) {
            if (this.configurationService.getValidator() == null) {
                this.configurationService.setValidator(validator);
            }
        }
    
        @Override
        @Deprecated
        public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
            if (this.configurationService.getPublisher() == null) {
                this.configurationService.setApplicationEventPublisher(publisher);
            }
        }
    
        private void initFactories(List<RoutePredicateFactory> predicates) {
            predicates.forEach(factory -> {
                String key = factory.name();
                if (this.predicates.containsKey(key)) {
                    this.logger.warn("A RoutePredicateFactory named " + key
                            + " already exists, class: " + this.predicates.get(key)
                            + ". It will be overwritten.");
                }
                this.predicates.put(key, factory);
                if (logger.isInfoEnabled()) {
                    logger.info("Loaded RoutePredicateFactory [" + key + "]");
                }
            });
        }
    
        @Override
        public Flux<Route> getRoutes() {
            return this.routeDefinitionLocator.getRouteDefinitions().map(this::convertToRoute)
                    // TODO: error handling
                    .map(route -> {
                        if (logger.isDebugEnabled()) {
                            logger.debug("RouteDefinition matched: " + route.getId());
                        }
                        return route;
                    });
    
            /*
             * TODO: trace logging if (logger.isTraceEnabled()) {
             * logger.trace("RouteDefinition did not match: " + routeDefinition.getId()); }
             */
        }
    
        private Route convertToRoute(RouteDefinition routeDefinition) {
            AsyncPredicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);
            List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);
    
            return Route.async(routeDefinition).asyncPredicate(predicate)
                    .replaceFilters(gatewayFilters).build();
        }
    
        @SuppressWarnings("unchecked")
        List<GatewayFilter> loadGatewayFilters(String id,
                List<FilterDefinition> filterDefinitions) {
            ArrayList<GatewayFilter> ordered = new ArrayList<>(filterDefinitions.size());
            for (int i = 0; i < filterDefinitions.size(); i++) {
                FilterDefinition definition = filterDefinitions.get(i);
                GatewayFilterFactory factory = this.gatewayFilterFactories
                        .get(definition.getName());
                if (factory == null) {
                    throw new IllegalArgumentException(
                            "Unable to find GatewayFilterFactory with name "
                                    + definition.getName());
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("RouteDefinition " + id + " applying filter "
                            + definition.getArgs() + " to " + definition.getName());
                }
    
                // @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<String, Object>) properties))
                        .bind();
                // @formatter:on
    
                // some filters require routeId
                // TODO: is there a better place to apply this?
                if (configuration instanceof HasRouteId) {
                    HasRouteId hasRouteId = (HasRouteId) configuration;
                    hasRouteId.setRouteId(id);
                }
    
                GatewayFilter gatewayFilter = factory.apply(configuration);
                if (gatewayFilter instanceof Ordered) {
                    ordered.add(gatewayFilter);
                }
                else {
                    ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1));
                }
            }
    
            return ordered;
        }
    
        private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
            List<GatewayFilter> filters = new ArrayList<>();
    
            // TODO: support option to apply defaults after route specific filters?
            if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
                filters.addAll(loadGatewayFilters(DEFAULT_FILTERS,
                        this.gatewayProperties.getDefaultFilters()));
            }
    
            if (!routeDefinition.getFilters().isEmpty()) {
                filters.addAll(loadGatewayFilters(routeDefinition.getId(),
                        routeDefinition.getFilters()));
            }
    
            AnnotationAwareOrderComparator.sort(filters);
            return filters;
        }
    
        private AsyncPredicate<ServerWebExchange> combinePredicates(
                RouteDefinition routeDefinition) {
            List<PredicateDefinition> predicates = routeDefinition.getPredicates();
            AsyncPredicate<ServerWebExchange> predicate = lookup(routeDefinition,
                    predicates.get(0));
    
            for (PredicateDefinition andPredicate : predicates.subList(1,
                    predicates.size())) {
                AsyncPredicate<ServerWebExchange> found = lookup(routeDefinition,
                        andPredicate);
                predicate = predicate.and(found);
            }
    
            return predicate;
        }
    
        @SuppressWarnings("unchecked")
        private AsyncPredicate<ServerWebExchange> lookup(RouteDefinition route,
                PredicateDefinition predicate) {
            RoutePredicateFactory<Object> 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());
            }
    
            // @formatter:off
            Object config = this.configurationService.with(factory)
                    .name(predicate.getName())
                    .properties(predicate.getArgs())
                    .eventFunction((bound, properties) -> new PredicateArgsEvent(
                            RouteDefinitionRouteLocator.this, route.getId(), properties))
                    .bind();
            // @formatter:on
    
            return factory.applyAsync(config);
        }
    
    }
    View Code

      可以看到这个对象内部就是根据predicate.getName(), 然后从spring 容器获取对应的断言工厂,然后构造相对应的断言。解析Filter 也是同样的原理。 内置的一些断言工厂和过滤器工厂如下:

    断言工厂predicates:

     过滤器工厂gatewayFilterFactories:(30个)

    result = {HashMap@8599}  size = 30
     "SetPath" -> {SetPathGatewayFilterFactory@8711} "[SetPathGatewayFilterFactory@1805782b configClass = SetPathGatewayFilterFactory.Config]"
     "RequestHeaderToRequestUri" -> {RequestHeaderToRequestUriGatewayFilterFactory@8713} "[RequestHeaderToRequestUriGatewayFilterFactory@68bd197d configClass = AbstractGatewayFilterFactory.NameConfig]"
     "RequestHeaderSize" -> {RequestHeaderSizeGatewayFilterFactory@8715} "[RequestHeaderSizeGatewayFilterFactory@17e6209b configClass = RequestHeaderSizeGatewayFilterFactory.Config]"
     "CircuitBreaker" -> {SpringCloudCircuitBreakerHystrixFilterFactory@8717} "[SpringCloudCircuitBreakerHystrixFilterFactory@795cab8d configClass = SpringCloudCircuitBreakerFilterFactory.Config]"
     "RemoveRequestHeader" -> {RemoveRequestHeaderGatewayFilterFactory@8719} "[RemoveRequestHeaderGatewayFilterFactory@7f85ae76 configClass = AbstractGatewayFilterFactory.NameConfig]"
     "RemoveRequestParameter" -> {RemoveRequestParameterGatewayFilterFactory@8721} "[RemoveRequestParameterGatewayFilterFactory@13229d28 configClass = AbstractGatewayFilterFactory.NameConfig]"
     "ModifyRequestBody" -> {ModifyRequestBodyGatewayFilterFactory@8723} "[ModifyRequestBodyGatewayFilterFactory@75755b31 configClass = ModifyRequestBodyGatewayFilterFactory.Config]"
     "AddRequestParameter" -> {AddRequestParameterGatewayFilterFactory@8725} "[AddRequestParameterGatewayFilterFactory@5fed4a4e configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]"
     "RewriteLocationResponseHeader" -> {RewriteLocationResponseHeaderGatewayFilterFactory@8727} "[RewriteLocationResponseHeaderGatewayFilterFactory@62256510 configClass = RewriteLocationResponseHeaderGatewayFilterFactory.Config]"
     "MapRequestHeader" -> {MapRequestHeaderGatewayFilterFactory@8729} "[MapRequestHeaderGatewayFilterFactory@57843cd8 configClass = MapRequestHeaderGatewayFilterFactory.Config]"
     "DedupeResponseHeader" -> {DedupeResponseHeaderGatewayFilterFactory@8731} "[DedupeResponseHeaderGatewayFilterFactory@3e1cbbb configClass = DedupeResponseHeaderGatewayFilterFactory.Config]"
     "RequestRateLimiter" -> {RequestRateLimiterGatewayFilterFactory@8733} "[RequestRateLimiterGatewayFilterFactory@13f06661 configClass = RequestRateLimiterGatewayFilterFactory.Config]"
     "PreserveHostHeader" -> {PreserveHostHeaderGatewayFilterFactory@8735} "[PreserveHostHeaderGatewayFilterFactory@1ec6c80d configClass = Object]"
     "RewritePath" -> {RewritePathGatewayFilterFactory@8737} "[RewritePathGatewayFilterFactory@41549c77 configClass = RewritePathGatewayFilterFactory.Config]"
     "SetStatus" -> {SetStatusGatewayFilterFactory@8739} "[SetStatusGatewayFilterFactory@2d4d6215 configClass = SetStatusGatewayFilterFactory.Config]"
     "SetRequestHeader" -> {SetRequestHeaderGatewayFilterFactory@8741} "[SetRequestHeaderGatewayFilterFactory@6d04446d configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]"
     "PrefixPath" -> {PrefixPathGatewayFilterFactory@8743} "[PrefixPathGatewayFilterFactory@41b5bfd9 configClass = PrefixPathGatewayFilterFactory.Config]"
     "SaveSession" -> {SaveSessionGatewayFilterFactory@8745} "[SaveSessionGatewayFilterFactory@1a4477a5 configClass = Object]"
     "StripPrefix" -> {StripPrefixGatewayFilterFactory@8747} "[StripPrefixGatewayFilterFactory@21f9e5b7 configClass = StripPrefixGatewayFilterFactory.Config]"
     "ModifyResponseBody" -> {ModifyResponseBodyGatewayFilterFactory@8749} "[ModifyResponseBodyGatewayFilterFactory@36eb5eb3 configClass = ModifyResponseBodyGatewayFilterFactory.Config]"
     "RequestSize" -> {RequestSizeGatewayFilterFactory@8751} "[RequestSizeGatewayFilterFactory@2ea693b5 configClass = RequestSizeGatewayFilterFactory.RequestSizeConfig]"
     "RedirectTo" -> {RedirectToGatewayFilterFactory@8753} "[RedirectToGatewayFilterFactory@13f7747d configClass = RedirectToGatewayFilterFactory.Config]"
     "SetResponseHeader" -> {SetResponseHeaderGatewayFilterFactory@8755} "[SetResponseHeaderGatewayFilterFactory@6f0b16b7 configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]"
     "SecureHeaders" -> {SecureHeadersGatewayFilterFactory@8757} "[SecureHeadersGatewayFilterFactory@8d6d624 configClass = Object]"
     "AddResponseHeader" -> {AddResponseHeaderGatewayFilterFactory@8759} "[AddResponseHeaderGatewayFilterFactory@271a0679 configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]"
     "FallbackHeaders" -> {FallbackHeadersGatewayFilterFactory@8761} "[FallbackHeadersGatewayFilterFactory@792a232a configClass = FallbackHeadersGatewayFilterFactory.Config]"
     "Retry" -> {RetryGatewayFilterFactory@8763} "[RetryGatewayFilterFactory@461892a8 configClass = RetryGatewayFilterFactory.RetryConfig]"
     "AddRequestHeader" -> {AddRequestHeaderGatewayFilterFactory@8765} "[AddRequestHeaderGatewayFilterFactory@2adf0c5f configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]"
     "RemoveResponseHeader" -> {RemoveResponseHeaderGatewayFilterFactory@8767} "[RemoveResponseHeaderGatewayFilterFactory@3b0ca9e1 configClass = AbstractGatewayFilterFactory.NameConfig]"
     "RewriteResponseHeader" -> {RewriteResponseHeaderGatewayFilterFactory@8769} "[RewriteResponseHeaderGatewayFilterFactory@5cfcef5d configClass = RewriteResponseHeaderGatewayFilterFactory.Config]"

      org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory 路径断言工厂如下:(可以看到核心是返回一个GatewayPredicate 断言对象,内部的test 方法会发挥重要作用)

    /*
     * Copyright 2013-2019 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      https://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.cloud.gateway.handler.predicate;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.Optional;
    import java.util.function.Predicate;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    import org.springframework.core.style.ToStringCreator;
    import org.springframework.http.server.PathContainer;
    import org.springframework.util.CollectionUtils;
    import org.springframework.validation.annotation.Validated;
    import org.springframework.web.server.ServerWebExchange;
    import org.springframework.web.util.pattern.PathPattern;
    import org.springframework.web.util.pattern.PathPattern.PathMatchInfo;
    import org.springframework.web.util.pattern.PathPatternParser;
    
    import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.putUriTemplateVariables;
    import static org.springframework.http.server.PathContainer.parsePath;
    
    /**
     * @author Spencer Gibb
     */
    public class PathRoutePredicateFactory
            extends AbstractRoutePredicateFactory<PathRoutePredicateFactory.Config> {
    
        private static final Log log = LogFactory.getLog(RoutePredicateFactory.class);
    
        private static final String MATCH_OPTIONAL_TRAILING_SEPARATOR_KEY = "matchOptionalTrailingSeparator";
    
        private PathPatternParser pathPatternParser = new PathPatternParser();
    
        public PathRoutePredicateFactory() {
            super(Config.class);
        }
    
        private static void traceMatch(String prefix, Object desired, Object actual,
                boolean match) {
            if (log.isTraceEnabled()) {
                String message = String.format("%s \"%s\" %s against value \"%s\"", prefix,
                        desired, match ? "matches" : "does not match", actual);
                log.trace(message);
            }
        }
    
        public void setPathPatternParser(PathPatternParser pathPatternParser) {
            this.pathPatternParser = pathPatternParser;
        }
    
        @Override
        public List<String> shortcutFieldOrder() {
            return Arrays.asList("patterns", MATCH_OPTIONAL_TRAILING_SEPARATOR_KEY);
        }
    
        @Override
        public ShortcutType shortcutType() {
            return ShortcutType.GATHER_LIST_TAIL_FLAG;
        }
    
        @Override
        public Predicate<ServerWebExchange> apply(Config config) {
            final ArrayList<PathPattern> pathPatterns = new ArrayList<>();
            synchronized (this.pathPatternParser) {
                pathPatternParser.setMatchOptionalTrailingSeparator(
                        config.isMatchOptionalTrailingSeparator());
                config.getPatterns().forEach(pattern -> {
                    PathPattern pathPattern = this.pathPatternParser.parse(pattern);
                    pathPatterns.add(pathPattern);
                });
            }
            return new GatewayPredicate() {
                @Override
                public boolean test(ServerWebExchange exchange) {
                    PathContainer path = parsePath(
                            exchange.getRequest().getURI().getRawPath());
    
                    Optional<PathPattern> optionalPathPattern = pathPatterns.stream()
                            .filter(pattern -> pattern.matches(path)).findFirst();
    
                    if (optionalPathPattern.isPresent()) {
                        PathPattern pathPattern = optionalPathPattern.get();
                        traceMatch("Pattern", pathPattern.getPatternString(), path, true);
                        PathMatchInfo pathMatchInfo = pathPattern.matchAndExtract(path);
                        putUriTemplateVariables(exchange, pathMatchInfo.getUriVariables());
                        return true;
                    }
                    else {
                        traceMatch("Pattern", config.getPatterns(), path, false);
                        return false;
                    }
                }
    
                @Override
                public String toString() {
                    return String.format("Paths: %s, match trailing slash: %b",
                            config.getPatterns(), config.isMatchOptionalTrailingSeparator());
                }
            };
        }
    
        @Validated
        public static class Config {
    
            private List<String> patterns = new ArrayList<>();
    
            private boolean matchOptionalTrailingSeparator = true;
    
            @Deprecated
            public String getPattern() {
                if (!CollectionUtils.isEmpty(this.patterns)) {
                    return patterns.get(0);
                }
                return null;
            }
    
            @Deprecated
            public Config setPattern(String pattern) {
                this.patterns = new ArrayList<>();
                this.patterns.add(pattern);
                return this;
            }
    
            public List<String> getPatterns() {
                return patterns;
            }
    
            public Config setPatterns(List<String> patterns) {
                this.patterns = patterns;
                return this;
            }
    
            public boolean isMatchOptionalTrailingSeparator() {
                return matchOptionalTrailingSeparator;
            }
    
            public Config setMatchOptionalTrailingSeparator(
                    boolean matchOptionalTrailingSeparator) {
                this.matchOptionalTrailingSeparator = matchOptionalTrailingSeparator;
                return this;
            }
    
            @Override
            public String toString() {
                return new ToStringCreator(this).append("patterns", patterns)
                        .append("matchOptionalTrailingSeparator",
                                matchOptionalTrailingSeparator)
                        .toString();
            }
    
        }
    
    }
    View Code

     6》 FilteringWebHandler 过滤器执行处理器,内部包含全局过滤器。同时也是gateway 获取到handler之后进行处理的入口。

    7》 RoutePredicateHandlerMapping, 类似于SpringMVC 内部的org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping(解析SpringMVC 的请求); 只不过这个HandlerMapping 是解析路由请求, 继承关系如下:

     3. 服务调用过程

    1. 一个简单的URL为例子

    $ curl --header 'X-Request-Id: 2' --header 'X-Request-Id2: 3' http://localhost:9527/pay/getServerPort
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
    100    52    0    52    0     0   2588      0 --:--:-- --:--:-- --:--:--  2888{"success":true,"code":"200","msg":"","data":"8081"}

    2. 查看其调用过程如下:

    1. 程序入口 org.springframework.web.reactive.DispatcherHandler, 源码如下:

    package org.springframework.web.reactive;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.Map;
    
    import reactor.core.publisher.Flux;
    import reactor.core.publisher.Mono;
    
    import org.springframework.beans.factory.BeanFactoryUtils;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.core.annotation.AnnotationAwareOrderComparator;
    import org.springframework.http.HttpStatus;
    import org.springframework.lang.Nullable;
    import org.springframework.web.server.ResponseStatusException;
    import org.springframework.web.server.ServerWebExchange;
    import org.springframework.web.server.WebHandler;
    import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
    
    /**
     * Central dispatcher for HTTP request handlers/controllers. Dispatches to
     * registered handlers for processing a request, providing convenient mapping
     * facilities.
     *
     * <p>{@code DispatcherHandler} discovers the delegate components it needs from
     * Spring configuration. It detects the following in the application context:
     * <ul>
     * <li>{@link HandlerMapping} -- map requests to handler objects
     * <li>{@link HandlerAdapter} -- for using any handler interface
     * <li>{@link HandlerResultHandler} -- process handler return values
     * </ul>
     *
     * <p>{@code DispatcherHandler} is also designed to be a Spring bean itself and
     * implements {@link ApplicationContextAware} for access to the context it runs
     * in. If {@code DispatcherHandler} is declared with the bean name "webHandler"
     * it is discovered by {@link WebHttpHandlerBuilder#applicationContext} which
     * creates a processing chain together with {@code WebFilter},
     * {@code WebExceptionHandler} and others.
     *
     * <p>A {@code DispatcherHandler} bean declaration is included in
     * {@link org.springframework.web.reactive.config.EnableWebFlux @EnableWebFlux}
     * configuration.
     *
     * @author Rossen Stoyanchev
     * @author Sebastien Deleuze
     * @author Juergen Hoeller
     * @since 5.0
     * @see WebHttpHandlerBuilder#applicationContext(ApplicationContext)
     */
    public class DispatcherHandler implements WebHandler, ApplicationContextAware {
    
        @Nullable
        private List<HandlerMapping> handlerMappings;
    
        @Nullable
        private List<HandlerAdapter> handlerAdapters;
    
        @Nullable
        private List<HandlerResultHandler> resultHandlers;
    
    
        /**
         * Create a new {@code DispatcherHandler} which needs to be configured with
         * an {@link ApplicationContext} through {@link #setApplicationContext}.
         */
        public DispatcherHandler() {
        }
    
        /**
         * Create a new {@code DispatcherHandler} for the given {@link ApplicationContext}.
         * @param applicationContext the application context to find the handler beans in
         */
        public DispatcherHandler(ApplicationContext applicationContext) {
            initStrategies(applicationContext);
        }
    
    
        /**
         * Return all {@link HandlerMapping} beans detected by type in the
         * {@link #setApplicationContext injected context} and also
         * {@link AnnotationAwareOrderComparator#sort(List) sorted}.
         * <p><strong>Note:</strong> This method may return {@code null} if invoked
         * prior to {@link #setApplicationContext(ApplicationContext)}.
         * @return immutable list with the configured mappings or {@code null}
         */
        @Nullable
        public final List<HandlerMapping> getHandlerMappings() {
            return this.handlerMappings;
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) {
            initStrategies(applicationContext);
        }
    
    
        protected void initStrategies(ApplicationContext context) {
            Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
                    context, HandlerMapping.class, true, false);
    
            ArrayList<HandlerMapping> mappings = new ArrayList<>(mappingBeans.values());
            AnnotationAwareOrderComparator.sort(mappings);
            this.handlerMappings = Collections.unmodifiableList(mappings);
    
            Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
                    context, HandlerAdapter.class, true, false);
    
            this.handlerAdapters = new ArrayList<>(adapterBeans.values());
            AnnotationAwareOrderComparator.sort(this.handlerAdapters);
    
            Map<String, HandlerResultHandler> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
                    context, HandlerResultHandler.class, true, false);
    
            this.resultHandlers = new ArrayList<>(beans.values());
            AnnotationAwareOrderComparator.sort(this.resultHandlers);
        }
    
    
        @Override
        public Mono<Void> handle(ServerWebExchange exchange) {
            if (this.handlerMappings == null) {
                return createNotFoundError();
            }
            return Flux.fromIterable(this.handlerMappings)
                    .concatMap(mapping -> mapping.getHandler(exchange))
                    .next()
                    .switchIfEmpty(createNotFoundError())
                    .flatMap(handler -> invokeHandler(exchange, handler))
                    .flatMap(result -> handleResult(exchange, result));
        }
    
        private <R> Mono<R> createNotFoundError() {
            return Mono.defer(() -> {
                Exception ex = new ResponseStatusException(HttpStatus.NOT_FOUND, "No matching handler");
                return Mono.error(ex);
            });
        }
    
        private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {
            if (this.handlerAdapters != null) {
                for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
                    if (handlerAdapter.supports(handler)) {
                        return handlerAdapter.handle(exchange, handler);
                    }
                }
            }
            return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
        }
    
        private Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
            return getResultHandler(result).handleResult(exchange, result)
                    .checkpoint("Handler " + result.getHandler() + " [DispatcherHandler]")
                    .onErrorResume(ex ->
                            result.applyExceptionHandler(ex).flatMap(exResult -> {
                                String text = "Exception handler " + exResult.getHandler() +
                                        ", error=\"" + ex.getMessage() + "\" [DispatcherHandler]";
                                return getResultHandler(exResult).handleResult(exchange, exResult).checkpoint(text);
                            }));
        }
    
        private HandlerResultHandler getResultHandler(HandlerResult handlerResult) {
            if (this.resultHandlers != null) {
                for (HandlerResultHandler resultHandler : this.resultHandlers) {
                    if (resultHandler.supports(handlerResult)) {
                        return resultHandler;
                    }
                }
            }
            throw new IllegalStateException("No HandlerResultHandler for " + handlerResult.getReturnValue());
        }
    
    }
    View Code

    入口是: org.springframework.web.reactive.DispatcherHandler#handle:

    调用链如下:(可以看到也是从netty 相关调用到该类的)

       这个方法也比较清晰, 主要就是遍历handlerMappings 获取到Handler, 然后调用handler, 并且处理结果。

    2. 遍历handlerMappings获取handler

    handlerMappings 如下, 可以看到有4个,有就是遍历这四个获取handler, 找到就继续后面的流程。 对于SpringMVC 走的是 RequestMappinHandlerMapping 找到org.springframework.web.method.HandlerMethod

     对于Webflux 路由类型,会到: org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping#getHandlerInternal 寻找handler

        protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
            if (this.managementPortType == RoutePredicateHandlerMapping.ManagementPortType.DIFFERENT && this.managementPort != null && exchange.getRequest().getURI().getPort() == this.managementPort) {
                return Mono.empty();
            } else {
                exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_HANDLER_MAPPER_ATTR, this.getSimpleName());
                return this.lookupRoute(exchange).flatMap((r) -> {
                    exchange.getAttributes().remove(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR);
                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug("Mapping [" + this.getExchangeDesc(exchange) + "] to " + r);
                    }
    
                    exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR, r);
                    return Mono.just(this.webHandler);
                }).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
                    exchange.getAttributes().remove(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR);
                    if (this.logger.isTraceEnabled()) {
                        this.logger.trace("No RouteDefinition found for [" + this.getExchangeDesc(exchange) + "]");
                    }
    
                })));
            }
        }

    1》 org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping#lookupRoute 根据路径寻找满足条件的路由

        protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
            return this.routeLocator.getRoutes().concatMap((route) -> {
                return Mono.just(route).filterWhen((r) -> {
                    exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
                    return (Publisher)r.getPredicate().apply(exchange);
                }).doOnError((e) -> {
                    this.logger.error("Error applying predicate for route: " + route.getId(), e);
                }).onErrorResume((e) -> {
                    return Mono.empty();
                });
            }).next().map((route) -> {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Route matched: " + route.getId());
                }
    
                this.validateRoute(route, exchange);
                return route;
            });
        }

    这里就是调用org.springframework.cloud.gateway.route.CachingRouteLocator#routes 获取到所有的Route 对象, 然后调用getPredicate().apply(exchange) 判断是否满足断言的要求。

    org.springframework.cloud.gateway.handler.AsyncPredicate.AndAsyncPredicate#apply :

            public Publisher<Boolean> apply(T t) {
                return Flux.zip((Publisher)this.left.apply(t), (Publisher)this.right.apply(t)).map((tuple) -> {
                    return (Boolean)tuple.getT1() && (Boolean)tuple.getT2();
                });
            }

      可以看到如果是多个断言,就进行逻辑与进行判断。比如会先到: org.springframework.cloud.gateway.handler.predicate.GatewayPredicate#test

                public boolean test(ServerWebExchange exchange) {
                    PathContainer path = PathContainer.parsePath(exchange.getRequest().getURI().getRawPath());
                    Optional<PathPattern> optionalPathPattern = pathPatterns.stream().filter((pattern) -> {
                        return pattern.matches(path);
                    }).findFirst();
                    if (optionalPathPattern.isPresent()) {
                        PathPattern pathPattern = (PathPattern)optionalPathPattern.get();
                        PathRoutePredicateFactory.traceMatch("Pattern", pathPattern.getPatternString(), path, true);
                        PathMatchInfo pathMatchInfo = pathPattern.matchAndExtract(path);
                        ServerWebExchangeUtils.putUriTemplateVariables(exchange, pathMatchInfo.getUriVariables());
                        return true;
                    } else {
                        PathRoutePredicateFactory.traceMatch("Pattern", config.getPatterns(), path, false);
                        return false;
                    }
                }

    2》 flatMap 内部核心逻辑:

    exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR, r); 代码将Route 对象放到了org.springframework.web.server.adapter.DefaultServerWebExchange#attributes 内部的Map中用于后续业务代码使用(org.springframework.web.server.adapter.DefaultServerWebExchange#attributes 这个属性是一个内部Map, 用于当前上下文共享和传递一些信息)。 然后返回的webHandler 就是 自动配置注入的org.springframework.cloud.gateway.handler.FilteringWebHandler 对象。

    3. 调用org.springframework.web.reactive.DispatcherHandler#invokeHandler 开始处理逻辑

        private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {
            if (this.handlerAdapters != null) {
                for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
                    if (handlerAdapter.supports(handler)) {
                        return handlerAdapter.handle(exchange, handler);
                    }
                }
            }
            return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
        }

      和springmvc 机制一样,交给HandlerAdapter处理器适配器, 如果支持这样的handler就交给处理器适配器进行调用。 处理器适配器有三个:

     1》 最后匹配到: org.springframework.web.reactive.result.SimpleHandlerAdapter#supports

        public boolean supports(Object handler) {
            return WebHandler.class.isAssignableFrom(handler.getClass());
        }

    2》 然后进行调用:org.springframework.web.reactive.result.SimpleHandlerAdapter#handle

        public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
            WebHandler webHandler = (WebHandler)handler;
            Mono<Void> mono = webHandler.handle(exchange);
            return mono.then(Mono.empty());
        }

    3》 继续调用到: org.springframework.cloud.gateway.handler.FilteringWebHandler#handle

        @Override
        public Mono<Void> handle(ServerWebExchange exchange) {
            Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
            List<GatewayFilter> gatewayFilters = route.getFilters();
    
            List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
            combined.addAll(gatewayFilters);
            // TODO: needed or cached?
            AnnotationAwareOrderComparator.sort(combined);
    
            if (logger.isDebugEnabled()) {
                logger.debug("Sorted gatewayFilterFactories: " + combined);
            }
    
            return new DefaultGatewayFilterChain(combined).filter(exchange);
        }

      这里可以看到其核心逻辑是:首先从上面的缓存map 获取到路由Route 对象; 然后获取到过滤器; 然后获取到全局过滤器; 两个过滤器合并, 合并之后排序完创建链条进行过滤器链的调用。

      这里合并后的过滤器链如下:

     4》 org.springframework.cloud.gateway.handler.FilteringWebHandler.DefaultGatewayFilterChain 过滤器链:(可以看到是一个链条模式的调用)

        private static class GatewayFilterAdapter implements GatewayFilter {
            private final GlobalFilter delegate;
    
            GatewayFilterAdapter(GlobalFilter delegate) {
                this.delegate = delegate;
            }
    
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                return this.delegate.filter(exchange, chain);
            }
    
            public String toString() {
                StringBuilder sb = new StringBuilder("GatewayFilterAdapter{");
                sb.append("delegate=").append(this.delegate);
                sb.append('}');
                return sb.toString();
            }
        }
    
        private static class DefaultGatewayFilterChain implements GatewayFilterChain {
            private final int index;
            private final List<GatewayFilter> filters;
    
            DefaultGatewayFilterChain(List<GatewayFilter> filters) {
                this.filters = filters;
                this.index = 0;
            }
    
            private DefaultGatewayFilterChain(FilteringWebHandler.DefaultGatewayFilterChain parent, int index) {
                this.filters = parent.getFilters();
                this.index = index;
            }
    
            public List<GatewayFilter> getFilters() {
                return this.filters;
            }
    
            public Mono<Void> filter(ServerWebExchange exchange) {
                return Mono.defer(() -> {
                    if (this.index < this.filters.size()) {
                        GatewayFilter filter = (GatewayFilter)this.filters.get(this.index);
                        FilteringWebHandler.DefaultGatewayFilterChain chain = new FilteringWebHandler.DefaultGatewayFilterChain(this, this.index + 1);
                        return filter.filter(exchange, chain);
                    } else {
                        return Mono.empty();
                    }
                });
            }
        }

    这里介绍几个重要的过滤器的作用:

    (1) org.springframework.cloud.gateway.filter.NettyWriteResponseFilter#filter: 可以看到核心作用是在链条执行完发送结果

        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            return chain.filter(exchange).doOnError((throwable) -> {
                this.cleanup(exchange);
            }).then(Mono.defer(() -> {
                Connection connection = (Connection)exchange.getAttribute(ServerWebExchangeUtils.CLIENT_RESPONSE_CONN_ATTR);
                if (connection == null) {
                    return Mono.empty();
                } else {
                    if (log.isTraceEnabled()) {
                        log.trace("NettyWriteResponseFilter start inbound: " + connection.channel().id().asShortText() + ", outbound: " + exchange.getLogPrefix());
                    }
    
                    ServerHttpResponse response = exchange.getResponse();
                    NettyDataBufferFactory factory = (NettyDataBufferFactory)response.bufferFactory();
                    ByteBufFlux var10000 = connection.inbound().receive().retain();
                    factory.getClass();
                    Flux<NettyDataBuffer> body = var10000.map(factory::wrap);
                    MediaType contentType = null;
    
                    try {
                        contentType = response.getHeaders().getContentType();
                    } catch (Exception var8) {
                        if (log.isTraceEnabled()) {
                            log.trace("invalid media type", var8);
                        }
                    }
    
                    return this.isStreamingMediaType(contentType) ? response.writeAndFlushWith(body.map(Flux::just)) : response.writeWith(body);
                }
            })).doOnCancel(() -> {
                this.cleanup(exchange);
            });
        }

    (2) org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter#filter 

        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            Route route = (Route)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
            if (route == null) {
                return chain.filter(exchange);
            } else {
                log.trace("RouteToRequestUrlFilter start");
                URI uri = exchange.getRequest().getURI();
                boolean encoded = ServerWebExchangeUtils.containsEncodedParts(uri);
                URI routeUri = route.getUri();
                if (hasAnotherScheme(routeUri)) {
                    exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR, routeUri.getScheme());
                    routeUri = URI.create(routeUri.getSchemeSpecificPart());
                }
    
                if ("lb".equalsIgnoreCase(routeUri.getScheme()) && routeUri.getHost() == null) {
                    throw new IllegalStateException("Invalid host: " + routeUri.toString());
                } else {
                    URI mergedUrl = UriComponentsBuilder.fromUri(uri).scheme(routeUri.getScheme()).host(routeUri.getHost()).port(routeUri.getPort()).build(encoded).toUri();
                    exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, mergedUrl);
                    return chain.filter(exchange);
                }
            }

      替换请求的url, 最终放到org.springframework.web.server.adapter.DefaultServerWebExchange#attributes 内部替换后的url 为: lb://cloud-payment-service/pay/getServerPort

    (3) org.springframework.cloud.gateway.filter.LoadBalancerClientFilter#filter 

        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            URI url = (URI)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
            String schemePrefix = (String)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR);
            if (url != null && ("lb".equals(url.getScheme()) || "lb".equals(schemePrefix))) {
                ServerWebExchangeUtils.addOriginalRequestUrl(exchange, url);
                if (log.isTraceEnabled()) {
                    log.trace("LoadBalancerClientFilter url before: " + url);
                }
    
                ServiceInstance instance = this.choose(exchange);
                if (instance == null) {
                    throw NotFoundException.create(this.properties.isUse404(), "Unable to find instance for " + url.getHost());
                } else {
                    URI uri = exchange.getRequest().getURI();
                    String overrideScheme = instance.isSecure() ? "https" : "http";
                    if (schemePrefix != null) {
                        overrideScheme = url.getScheme();
                    }
    
                    URI requestUrl = this.loadBalancer.reconstructURI(new DelegatingServiceInstance(instance, overrideScheme), uri);
                    if (log.isTraceEnabled()) {
                        log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
                    }
    
                    exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, requestUrl);
                    return chain.filter(exchange);
                }
            } else {
                return chain.filter(exchange);
            }
        }
    
        protected ServiceInstance choose(ServerWebExchange exchange) {
            return this.loadBalancer.choose(((URI)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR)).getHost());
        }

      这里也就是判断当前上下文的请求路径, 如果以lb 开头就开始调用loadBalancer(这里默认是ribbon 相关组件), 然后替换后生成最终的url 如下: http://127.0.0.1:8081/pay/getServerPort, 然后存入到exchange.getAttributes() 缓存map 中。

    (4) org.springframework.cloud.gateway.filter.NettyRoutingFilter#filter 这里也就是进行发送数据请求, 并且将netty 通道信息记录在connection 对象中存入exchange.getAttribute() 属性中用于后续获取结果等操作

    /*
     * Copyright 2013-2019 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      https://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.cloud.gateway.filter;
    
    import java.net.URI;
    import java.time.Duration;
    import java.util.List;
    
    import io.netty.channel.ChannelOption;
    import io.netty.handler.codec.http.DefaultHttpHeaders;
    import io.netty.handler.codec.http.HttpMethod;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import reactor.core.publisher.Flux;
    import reactor.core.publisher.Mono;
    import reactor.netty.http.client.HttpClient;
    import reactor.netty.http.client.HttpClientResponse;
    
    import org.springframework.beans.factory.ObjectProvider;
    import org.springframework.cloud.gateway.config.HttpClientProperties;
    import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter;
    import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter.Type;
    import org.springframework.cloud.gateway.route.Route;
    import org.springframework.cloud.gateway.support.TimeoutException;
    import org.springframework.core.Ordered;
    import org.springframework.core.io.buffer.NettyDataBuffer;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.server.reactive.AbstractServerHttpResponse;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
    import org.springframework.util.StringUtils;
    import org.springframework.web.server.ResponseStatusException;
    import org.springframework.web.server.ServerWebExchange;
    
    import static org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter.filterRequest;
    import static org.springframework.cloud.gateway.support.RouteMetadataUtils.CONNECT_TIMEOUT_ATTR;
    import static org.springframework.cloud.gateway.support.RouteMetadataUtils.RESPONSE_TIMEOUT_ATTR;
    import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR;
    import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.CLIENT_RESPONSE_CONN_ATTR;
    import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.CLIENT_RESPONSE_HEADER_NAMES;
    import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
    import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
    import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR;
    import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.PRESERVE_HOST_HEADER_ATTRIBUTE;
    import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.isAlreadyRouted;
    import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.setAlreadyRouted;
    
    /**
     * @author Spencer Gibb
     * @author Biju Kunjummen
     */
    public class NettyRoutingFilter implements GlobalFilter, Ordered {
    
        private static final Log log = LogFactory.getLog(NettyRoutingFilter.class);
    
        private final HttpClient httpClient;
    
        private final ObjectProvider<List<HttpHeadersFilter>> headersFiltersProvider;
    
        private final HttpClientProperties properties;
    
        // do not use this headersFilters directly, use getHeadersFilters() instead.
        private volatile List<HttpHeadersFilter> headersFilters;
    
        public NettyRoutingFilter(HttpClient httpClient,
                ObjectProvider<List<HttpHeadersFilter>> headersFiltersProvider,
                HttpClientProperties properties) {
            this.httpClient = httpClient;
            this.headersFiltersProvider = headersFiltersProvider;
            this.properties = properties;
        }
    
        public List<HttpHeadersFilter> getHeadersFilters() {
            if (headersFilters == null) {
                headersFilters = headersFiltersProvider.getIfAvailable();
            }
            return headersFilters;
        }
    
        @Override
        public int getOrder() {
            return Ordered.LOWEST_PRECEDENCE;
        }
    
        @Override
        @SuppressWarnings("Duplicates")
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
    
            String scheme = requestUrl.getScheme();
            if (isAlreadyRouted(exchange)
                    || (!"http".equals(scheme) && !"https".equals(scheme))) {
                return chain.filter(exchange);
            }
            setAlreadyRouted(exchange);
    
            ServerHttpRequest request = exchange.getRequest();
    
            final HttpMethod method = HttpMethod.valueOf(request.getMethodValue());
            final String url = requestUrl.toASCIIString();
    
            HttpHeaders filtered = filterRequest(getHeadersFilters(), exchange);
    
            final DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders();
            filtered.forEach(httpHeaders::set);
    
            boolean preserveHost = exchange
                    .getAttributeOrDefault(PRESERVE_HOST_HEADER_ATTRIBUTE, false);
            Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
    
            Flux<HttpClientResponse> responseFlux = httpClientWithTimeoutFrom(route)
                    .headers(headers -> {
                        headers.add(httpHeaders);
                        // Will either be set below, or later by Netty
                        headers.remove(HttpHeaders.HOST);
                        if (preserveHost) {
                            String host = request.getHeaders().getFirst(HttpHeaders.HOST);
                            headers.add(HttpHeaders.HOST, host);
                        }
                    }).request(method).uri(url).send((req, nettyOutbound) -> {
                        if (log.isTraceEnabled()) {
                            nettyOutbound
                                    .withConnection(connection -> log.trace("outbound route: "
                                            + connection.channel().id().asShortText()
                                            + ", inbound: " + exchange.getLogPrefix()));
                        }
                        return nettyOutbound.send(request.getBody()
                                .map(dataBuffer -> ((NettyDataBuffer) dataBuffer)
                                        .getNativeBuffer()));
                    }).responseConnection((res, connection) -> {
    
                        // Defer committing the response until all route filters have run
                        // Put client response as ServerWebExchange attribute and write
                        // response later NettyWriteResponseFilter
                        exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);
                        exchange.getAttributes().put(CLIENT_RESPONSE_CONN_ATTR, connection);
    
                        ServerHttpResponse response = exchange.getResponse();
                        // put headers and status so filters can modify the response
                        HttpHeaders headers = new HttpHeaders();
    
                        res.responseHeaders().forEach(
                                entry -> headers.add(entry.getKey(), entry.getValue()));
    
                        String contentTypeValue = headers.getFirst(HttpHeaders.CONTENT_TYPE);
                        if (StringUtils.hasLength(contentTypeValue)) {
                            exchange.getAttributes().put(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR,
                                    contentTypeValue);
                        }
    
                        setResponseStatus(res, response);
    
                        // make sure headers filters run after setting status so it is
                        // available in response
                        HttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter(
                                getHeadersFilters(), headers, exchange, Type.RESPONSE);
    
                        if (!filteredResponseHeaders
                                .containsKey(HttpHeaders.TRANSFER_ENCODING)
                                && filteredResponseHeaders
                                        .containsKey(HttpHeaders.CONTENT_LENGTH)) {
                            // It is not valid to have both the transfer-encoding header and
                            // the content-length header.
                            // Remove the transfer-encoding header in the response if the
                            // content-length header is present.
                            response.getHeaders().remove(HttpHeaders.TRANSFER_ENCODING);
                        }
    
                        exchange.getAttributes().put(CLIENT_RESPONSE_HEADER_NAMES,
                                filteredResponseHeaders.keySet());
    
                        response.getHeaders().putAll(filteredResponseHeaders);
    
                        return Mono.just(res);
                    });
    
            Duration responseTimeout = getResponseTimeout(route);
            if (responseTimeout != null) {
                responseFlux = responseFlux
                        .timeout(responseTimeout, Mono.error(new TimeoutException(
                                "Response took longer than timeout: " + responseTimeout)))
                        .onErrorMap(TimeoutException.class,
                                th -> new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT,
                                        th.getMessage(), th));
            }
    
            return responseFlux.then(chain.filter(exchange));
        }
    
        private void setResponseStatus(HttpClientResponse clientResponse,
                ServerHttpResponse response) {
            HttpStatus status = HttpStatus.resolve(clientResponse.status().code());
            if (status != null) {
                response.setStatusCode(status);
            }
            else {
                while (response instanceof ServerHttpResponseDecorator) {
                    response = ((ServerHttpResponseDecorator) response).getDelegate();
                }
                if (response instanceof AbstractServerHttpResponse) {
                    ((AbstractServerHttpResponse) response)
                            .setStatusCodeValue(clientResponse.status().code());
                }
                else {
                    // TODO: log warning here, not throw error?
                    throw new IllegalStateException("Unable to set status code "
                            + clientResponse.status().code() + " on response of type "
                            + response.getClass().getName());
                }
            }
        }
    
        private HttpClient httpClientWithTimeoutFrom(Route route) {
            Integer connectTimeout = (Integer) route.getMetadata().get(CONNECT_TIMEOUT_ATTR);
            if (connectTimeout != null) {
                return this.httpClient.tcpConfiguration((tcpClient) -> tcpClient
                        .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeout));
            }
            return httpClient;
        }
    
        private Duration getResponseTimeout(Route route) {
            Number responseTimeout = (Number) route.getMetadata().get(RESPONSE_TIMEOUT_ATTR);
            return responseTimeout != null ? Duration.ofMillis(responseTimeout.longValue())
                    : properties.getResponseTimeout();
        }
    
    }
    View Code

    filter方法的headers如下:

     内部的httpClient 如下: 

    connection 对象如下:

     (5) org.springframework.cloud.gateway.filter.ForwardRoutingFilter#filter

        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            URI requestUrl = (URI)exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
            String scheme = requestUrl.getScheme();
            if (!ServerWebExchangeUtils.isAlreadyRouted(exchange) && "forward".equals(scheme)) {
                if (log.isTraceEnabled()) {
                    log.trace("Forwarding to URI: " + requestUrl);
                }
    
                return this.getDispatcherHandler().handle(exchange);
            } else {
                return chain.filter(exchange);
            }
        }

      处理forward 转发请求的。

      上述链条执行完成之后则请求回到org.springframework.cloud.gateway.filter.NettyWriteResponseFilter#filter 执行回传结果的操作。也就是写到org.springframework.web.server.ServerWebExchange#getResponse 回传给请求的客户端。

    总结:我理解整体的思路是:

    1》 客户端通过netty 和 gateway 建立连接, 封装一个org.springframework.web.server.ServerWebExchange 对象用于整个请求链的上下文环境共享

    /*
     * Copyright 2002-2019 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      https://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.web.server;
    
    import java.security.Principal;
    import java.time.Instant;
    import java.util.Map;
    import java.util.function.Consumer;
    import java.util.function.Function;
    
    import reactor.core.publisher.Mono;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.i18n.LocaleContext;
    import org.springframework.http.codec.multipart.Part;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.lang.Nullable;
    import org.springframework.util.Assert;
    import org.springframework.util.MultiValueMap;
    
    /**
     * Contract for an HTTP request-response interaction. Provides access to the HTTP
     * request and response and also exposes additional server-side processing
     * related properties and features such as request attributes.
     *
     * @author Rossen Stoyanchev
     * @since 5.0
     */
    public interface ServerWebExchange {
    
        /**
         * Name of {@link #getAttributes() attribute} whose value can be used to
         * correlate log messages for this exchange. Use {@link #getLogPrefix()} to
         * obtain a consistently formatted prefix based on this attribute.
         * @since 5.1
         * @see #getLogPrefix()
         */
        String LOG_ID_ATTRIBUTE = ServerWebExchange.class.getName() + ".LOG_ID";
    
    
        /**
         * Return the current HTTP request.
         */
        ServerHttpRequest getRequest();
    
        /**
         * Return the current HTTP response.
         */
        ServerHttpResponse getResponse();
    
        /**
         * Return a mutable map of request attributes for the current exchange.
         */
        Map<String, Object> getAttributes();
    
        /**
         * Return the request attribute value if present.
         * @param name the attribute name
         * @param <T> the attribute type
         * @return the attribute value
         */
        @SuppressWarnings("unchecked")
        @Nullable
        default <T> T getAttribute(String name) {
            return (T) getAttributes().get(name);
        }
    
        /**
         * Return the request attribute value or if not present raise an
         * {@link IllegalArgumentException}.
         * @param name the attribute name
         * @param <T> the attribute type
         * @return the attribute value
         */
        @SuppressWarnings("unchecked")
        default <T> T getRequiredAttribute(String name) {
            T value = getAttribute(name);
            Assert.notNull(value, () -> "Required attribute '" + name + "' is missing");
            return value;
        }
    
        /**
         * Return the request attribute value, or a default, fallback value.
         * @param name the attribute name
         * @param defaultValue a default value to return instead
         * @param <T> the attribute type
         * @return the attribute value
         */
        @SuppressWarnings("unchecked")
        default <T> T getAttributeOrDefault(String name, T defaultValue) {
            return (T) getAttributes().getOrDefault(name, defaultValue);
        }
    
        /**
         * Return the web session for the current request. Always guaranteed  to
         * return an instance either matching to the session id requested by the
         * client, or with a new session id either because the client did not
         * specify one or because the underlying session had expired. Use of this
         * method does not automatically create a session. See {@link WebSession}
         * for more details.
         */
        Mono<WebSession> getSession();
    
        /**
         * Return the authenticated user for the request, if any.
         */
        <T extends Principal> Mono<T> getPrincipal();
    
        /**
         * Return the form data from the body of the request if the Content-Type is
         * {@code "application/x-www-form-urlencoded"} or an empty map otherwise.
         * <p><strong>Note:</strong> calling this method causes the request body to
         * be read and parsed in full and the resulting {@code MultiValueMap} is
         * cached so that this method is safe to call more than once.
         */
        Mono<MultiValueMap<String, String>> getFormData();
    
        /**
         * Return the parts of a multipart request if the Content-Type is
         * {@code "multipart/form-data"} or an empty map otherwise.
         * <p><strong>Note:</strong> calling this method causes the request body to
         * be read and parsed in full and the resulting {@code MultiValueMap} is
         * cached so that this method is safe to call more than once.
         * <p><strong>Note:</strong>the {@linkplain Part#content() contents} of each
         * part is not cached, and can only be read once.
         */
        Mono<MultiValueMap<String, Part>> getMultipartData();
    
        /**
         * Return the {@link LocaleContext} using the configured
         * {@link org.springframework.web.server.i18n.LocaleContextResolver}.
         */
        LocaleContext getLocaleContext();
    
        /**
         * Return the {@link ApplicationContext} associated with the web application,
         * if it was initialized with one via
         * {@link org.springframework.web.server.adapter.WebHttpHandlerBuilder#applicationContext(ApplicationContext)}.
         * @since 5.0.3
         * @see org.springframework.web.server.adapter.WebHttpHandlerBuilder#applicationContext(ApplicationContext)
         */
        @Nullable
        ApplicationContext getApplicationContext();
    
        /**
         * Returns {@code true} if the one of the {@code checkNotModified} methods
         * in this contract were used and they returned true.
         */
        boolean isNotModified();
    
        /**
         * An overloaded variant of {@link #checkNotModified(String, Instant)} with
         * a last-modified timestamp only.
         * @param lastModified the last-modified time
         * @return whether the request qualifies as not modified
         */
        boolean checkNotModified(Instant lastModified);
    
        /**
         * An overloaded variant of {@link #checkNotModified(String, Instant)} with
         * an {@code ETag} (entity tag) value only.
         * @param etag the entity tag for the underlying resource.
         * @return true if the request does not require further processing.
         */
        boolean checkNotModified(String etag);
    
        /**
         * Check whether the requested resource has been modified given the supplied
         * {@code ETag} (entity tag) and last-modified timestamp as determined by
         * the application. Also transparently prepares the response, setting HTTP
         * status, and adding "ETag" and "Last-Modified" headers when applicable.
         * This method works with conditional GET/HEAD requests as well as with
         * conditional POST/PUT/DELETE requests.
         * <p><strong>Note:</strong> The HTTP specification recommends setting both
         * ETag and Last-Modified values, but you can also use
         * {@code #checkNotModified(String)} or
         * {@link #checkNotModified(Instant)}.
         * @param etag the entity tag that the application determined for the
         * underlying resource. This parameter will be padded with quotes (")
         * if necessary.
         * @param lastModified the last-modified timestamp that the application
         * determined for the underlying resource
         * @return true if the request does not require further processing.
         */
        boolean checkNotModified(@Nullable String etag, Instant lastModified);
    
        /**
         * Transform the given url according to the registered transformation function(s).
         * By default, this method returns the given {@code url}, though additional
         * transformation functions can by registered with {@link #addUrlTransformer}
         * @param url the URL to transform
         * @return the transformed URL
         */
        String transformUrl(String url);
    
        /**
         * Register an additional URL transformation function for use with {@link #transformUrl}.
         * The given function can be used to insert an id for authentication, a nonce for CSRF
         * protection, etc.
         * <p>Note that the given function is applied after any previously registered functions.
         * @param transformer a URL transformation function to add
         */
        void addUrlTransformer(Function<String, String> transformer);
    
        /**
         * Return a log message prefix to use to correlate messages for this exchange.
         * The prefix is based on the value of the attribute {@link #LOG_ID_ATTRIBUTE}
         * along with some extra formatting so that the prefix can be conveniently
         * prepended with no further formatting no separators required.
         * @return the log message prefix or an empty String if the
         * {@link #LOG_ID_ATTRIBUTE} is not set.
         * @since 5.1
         */
        String getLogPrefix();
    
        /**
         * Return a builder to mutate properties of this exchange by wrapping it
         * with {@link ServerWebExchangeDecorator} and returning either mutated
         * values or delegating back to this instance.
         */
        default Builder mutate() {
            return new DefaultServerWebExchangeBuilder(this);
        }
    
    
        /**
         * Builder for mutating an existing {@link ServerWebExchange}.
         * Removes the need
         */
        interface Builder {
    
            /**
             * Configure a consumer to modify the current request using a builder.
             * <p>Effectively this:
             * <pre>
             * exchange.mutate().request(builder-> builder.method(HttpMethod.PUT));
             *
             * // vs...
             *
             * ServerHttpRequest request = exchange.getRequest().mutate()
             *     .method(HttpMethod.PUT)
             *     .build();
             *
             * exchange.mutate().request(request);
             * </pre>
             * @see ServerHttpRequest#mutate()
             */
            Builder request(Consumer<ServerHttpRequest.Builder> requestBuilderConsumer);
    
            /**
             * Set the request to use especially when there is a need to override
             * {@link ServerHttpRequest} methods. To simply mutate request properties
             * see {@link #request(Consumer)} instead.
             * @see org.springframework.http.server.reactive.ServerHttpRequestDecorator
             */
            Builder request(ServerHttpRequest request);
    
            /**
             * Set the response to use.
             * @see org.springframework.http.server.reactive.ServerHttpResponseDecorator
             */
            Builder response(ServerHttpResponse response);
    
            /**
             * Set the {@code Mono<Principal>} to return for this exchange.
             */
            Builder principal(Mono<Principal> principalMono);
    
            /**
             * Build a {@link ServerWebExchange} decorator with the mutated properties.
             */
            ServerWebExchange build();
        }
    
    }
    View Code

    2》 到达org.springframework.web.reactive.DispatcherHandler#handle 方法开始处理: 找到满足条件的Route 对象(经过一系列断言匹配); 然后找到该Route的filter和全局filter, 组合后开始过滤器链条的执行

    3》 进行过滤器链条的执行, 核心的包括:

      请求路径替换的

      lb 负载均衡的过滤器

      Netty 请求数据(使用负载均衡过滤器处理后的url 请求数据,并将响应通道channel封装起来存入ServerWebExchange.getAttributes() 属性中)

      Netty 响应数据处理器,从上面的channel 中获取到响应数据, 处理后通过ServerWebExchange#getResponse 再写回去数据。

      所以起重要作用的是其内部的一系列过滤器链, 以链条的形式完成整个请求响应。org.springframework.web.server.ServerWebExchange 也是一个重要的类,用于在整个请求链中作为上下文传递一些参数。

    exchange.getAttribute

    【当你用心写完每一篇博客之后,你会发现它比你用代码实现功能更有成就感!】
  • 相关阅读:
    Access restriction on class due to restriction on required library rt.jar?
    Why “no projects found to import”?
    MySQL
    您对无法重新创建的表进行了更改或者启用了“阻止保存要求重新创建表的更改”选项
    INTJINTJ——内向+直觉+思维+判
    豆瓣网案例分析报告
    如何使用Git
    如何在不到六个月的时间内成为一个开发者
    关于网站编程Alex
    string
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/15590490.html
Copyright © 2011-2022 走看看