• SpringCloud Alibaba Gateway实践与原理学习


    目录

    一、实践

    1. 项目环境搭建
    2. 配置测试路由、负载均衡
    3. 测试配置GlobalFilter拦截器模拟登录验证
    4. 配置CROS跨域
    5. 测试配置限流

    二、原理分析

    1. 请求路由流程
    2. 核心类分析
      • DispatcherHandler
      • RouteLocator
      • RouteDefinition
      • RoutePredicateFactory
      • GlobalFilter
      • GatewayFilterFactory
      • ReactorLoadBalancer
      • FilteringWebHandler
    一、实践
    1、项目环境搭建

    网关微服务模块版本控制

     <version>0.0.1version>
        <name>gateway-demoname>
        <description>使用gateway的demodescription>
    
        <properties>
            <java.version>11java.version>
            <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
            <spring-boot.version>2.4.4spring-boot.version>
            <spring-cloud.version>2020.0.5spring-cloud.version>
        properties>
    
    
    	...
    	...
    
     <dependencyManagement>
            <dependencies>
                
                <dependency>
                    <groupId>org.springframework.cloudgroupId>
                    <artifactId>spring-cloud-dependenciesartifactId>
                    <version>${spring-cloud.version}version>
                    <type>pomtype>
                    <scope>importscope>
                dependency>
                
    
                <dependency>
                    <groupId>org.springframework.bootgroupId>
                    <artifactId>spring-boot-dependenciesartifactId>
                    <version>${spring-boot.version}version>
                    <type>pomtype>
                    <scope>importscope>
                dependency>
            dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    新建gateway的maven项目引入依赖

    
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-starter-gatewayartifactId>
            dependency>
    
    
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-starter-loadbalancerartifactId>
            dependency>
    
    
    
            <dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
                <version>2021.1version>
            dependency>
    
    
            <dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
                <version>2021.1version>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    最后新建provider-demo的web项目、启动nacos-server,启动provideo-demo、gateway-demo项目,查看nacos可视化界面,保证正确注册。

    # nacos服务发现注册
    spring:
      cloud:
        nacos:
          discovery:
            server-addr: 127.0.0.1:8848
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    2、配置测试路由、负载均衡

    首先测试localhost:80/test/hello能否正确访问provider-demo接口,然后在gateway模块使用yml文件配置路由,测试localhost:8080/test/hello能否访问provider-demo模块接口,然后启动不同端口的2个以上provider-demo的实例,在测试负载均衡访问localhost:8080/loadbalance/hello

    spring:
      cloud:
        nacos:
          discovery:
            server-addr: 127.0.0.1:8848
    
        gateway:
          routes:
            - id : test-router1
              uri: http://localhost:80   # provider-demo的 的路由地址
              predicates:
                - Path=/test/**
            - id: provider-dynamic-loadbalance-router  # 路由的id,没有固定规则但要求唯一,建议配合服务名
              # uri: http://localhost:8001   # 匹配后提供服务的路由地址
              uri: lb://provider-demo   # 需要注意的是uri的协议为lb,表示启用Gateway的负载均衡功能。lb://serviceName是spring cloud gateway在微服务中自动为我们创建的负载均衡uri。
              predicates:
                - Path=/loadbalance/**         # 断言,路径相匹配的路由
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    测试配置类配置路由

    package henu.soft.scl.router;
    
    /**
     * @author sichaolong
     * @date 2022/8/19 15:30
     */
    
    import org.springframework.cloud.gateway.route.RouteLocator;
    import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
    import org.springframework.cloud.gateway.support.ipresolver.RemoteAddressResolver;
    import org.springframework.cloud.gateway.support.ipresolver.XForwardedRemoteAddressResolver;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * 配置路由有两种方式:1、通过配置文件。2、通过配置类
     * 这里通过配置类配置路由
     */
    /**
     * 配置路由有两种方式:1、通过配置文件。2、通过配置类(测试test_router2、test_router3暂时不能使用)
     * 这里通过配置类配置路由
     */
    @Configuration
    public class MyRouter {
        @Bean
        public RouteLocator customerRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
    
            return routeLocatorBuilder.routes()
                    .route("test_router2", r ->
                            r.path("/test2").uri("http://www.baidu.com"))
                    .route("test_router3", r ->
                            r.path("/test3").uri("http://localhost:80/test/hello"))
                    .route(
                            // 第一个参数是路由的唯一身份
                            "path_route_lb",
                            // 第二个参数是个lambda实现,
                            // 设置了配套条件是按照请求路径匹配,以及转发地址,
                            // 注意lb://表示这是个服务名,要从
                            r -> r.path("/lbtest/**").uri("lb://provider-demo")
                    )
    //                .route("test1", r -> {
    //                    return r.host("*.somehost.org").and().path("/somepath")
    //                            .filters(f -> f.addRequestHeader("header1", "header-value-1"))
    //                            .uri("http://someuri")
    //                            .metadata(RESPONSE_TIMEOUT_ATTR, 200)
    //                            .metadata(CONNECT_TIMEOUT_ATTR, 200);
    //                })
                    .build();
    
    
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    注意:测试gateway的负载均衡需要额外引入loadbalance依赖!!!

    3、测试配置GlobalFilter拦截器模拟登录验证

    定义MyLoginGlobalFilter类实现GlobalFilter, Ordered,重写相应方法

    package henu.soft.scl.filter;
    
    import com.alibaba.cloud.commons.lang.StringUtils;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.Ordered;
    import org.springframework.http.HttpStatus;
    import org.springframework.stereotype.Component;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Mono;
    
    import java.util.Date;
    import java.util.List;
    
    /**
     * @author sichaolong
     * @date 2022/8/19 17:00
     */
    
    /**
     * name : MyLoginGlobalFilter.java
     * creator : sichaolong
     * date : 2022/8/19 17:01
     * descript : gateway做登录验证
    **/
    
    @Component
    @Slf4j
    public class MyLoginGlobalFilter implements GlobalFilter, Ordered {
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    
            log.info("come in MyLogGatewayFilter: " + new Date());
    //        String name = exchange.getRequest().getQueryParams().getFirst("name");
            String token = exchange.getRequest().getHeaders().getFirst("token");
    
            log.info("token is :{}",token );
    //        if (StringUtils.isBlank(token)) {
    //            log.info("用户未登录!");
    //            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
    //            return exchange.getResponse().setComplete();
    //        }
            // 继续下一个filter
            return chain.filter(exchange);
        }
    
        @Override
        public int getOrder() {
            return 0;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    4、配置CROS跨域
    package henu.soft.scl.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.cors.CorsConfiguration;
    import org.springframework.web.cors.reactive.CorsWebFilter;
    import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
    
    /**
     * @author sichaolong
     * @date 2022/8/19 14:51
     */
    @Configuration
    public class MyCrosConfig {
    
        @Bean
        public CorsWebFilter CorsWebFilter(){
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    
            CorsConfiguration corsConfiguration = new CorsConfiguration();
    
            // 这里配置跨域
            corsConfiguration.addAllowedHeader("*");
            corsConfiguration.addAllowedMethod("*");
            corsConfiguration.addAllowedOrigin("*");
            corsConfiguration.setAllowCredentials(true);
    
            source.registerCorsConfiguration("/**",corsConfiguration);
    
            return new CorsWebFilter(source);
        }
    
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    5、测试配置限流

    借助gateway内置的限流API来限流,参考https://juejin.cn/post/6844903742731452424

    在Spring Cloud Gateway中,有Filter过滤器,因此可以在“pre”类型的Filter中自行实现上述三种过滤器。但是限流作为网关最基本的功能,Spring Cloud Gateway官方就提供了RequestRateLimiterGatewayFilterFactory这个类,适用Redis和lua脚本实现了令牌桶的方式。具体实现逻辑在RequestRateLimiterGatewayFilterFactory类中,lua脚本在如下图所示的文件夹中:

    请添加图片描述

    
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-starter-gatewayartifactId>
            dependency>
    
    
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-starter-loadbalancerartifactId>
            dependency>
    
    
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-data-redis-reactiveartifactId>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    编写限流配置,然后测试,频发刷新网页,发现报错429,查看redis

    spring:
      cloud:
        nacos:
          discovery:
            server-addr: 127.0.0.1:8848
        gateway:
          routes:
            - id : test-router1
              uri: http://localhost:80   # provider-demo的 的路由地址
              predicates:
                - Path=/test/**
              # gateway限流
              filters:
                - name: RequestRateLimiter
                  args:
                    key-resolver: '#{@myRequestRateLimit}'
                    redis-rate-limiter.replenishRate: 1
                    redis-rate-limiter.burstCapacity: 3
            - id: provider-dynamic-loadbalance-router  # 路由的id,没有固定规则但要求唯一,建议配合服务名
              # uri: http://localhost:8001   # 匹配后提供服务的路由地址
              uri: lb://provider-demo   # 需要注意的是uri的协议为lb,表示启用Gateway的负载均衡功能。lb://serviceName是spring cloud gateway在微服务中自动为我们创建的负载均衡uri。
              predicates:
                - Path=/loadbalance/**         # 断言,路径相匹配的路由
    
      # gateway借助内置API和redis实现令牌桶限流
      redis:
        host: 127.0.0.1
        port: 6379
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    package henu.soft.scl.ratelimit;
    
    import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Mono;
    
    /**
     * @author sichaolong
     * @date 2022/8/26 20:27
     */
    
    /**
     * name : MyRequestRateLimit.java
     * creator : sichaolong
     * date : 2022/8/26 20:28
     * descript : gateway限流,根据指定的request参数限流
    **/
    
    @Configuration
    public class MyRequestRateLimit implements KeyResolver {
        @Override
        public Mono<String> resolve(ServerWebExchange exchange) {
            // 根据请求url限流
            // return Mono.just(exchange.getRequest().getURI().getPath());
    
            // 根据远程客户端地址限流
            return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    请添加图片描述

    二、原理分析
    1、请求路由流程
    1. 进来的请求由 DispatcherHandler处理。
    2. DispatcherHandler 遍历所有的HandlerMapping调用其handler方法,其中 RoutePredicateHandlerMapping 间接实现HandlerMapping 的handler方法。
    3. 在handler方法内部RoutePredicateHandlerMapping 依赖 RouteLocator 获取所有路由配置Flux并根据匹配条件打到请求匹配的路由Route。
    4. RoutePredicateHandlerMapping 把请求交给 FilteringWebHandler 处理。
    5. FilteringWebHandler 从请求匹配的路由获取对应的路由 Filter,并和全局 Filter 合并构造 GatewayFilterChain,请求最终由 GatewayFilterChain 里的 Filter 按顺序处理。
    2、核心类分析
    (1)DispatcherHandler

    DispatcherHandler是所有请求的调度器负责分发请求,成员属性包含ListListList
    请添加图片描述

    handler方法式请求的入口,接着遍历HandlerMapping的实现类,调用调用相应的getHandler方法

       public Mono<Void> handle(ServerWebExchange exchange) {
            if (this.handlerMappings == null) {
                return this.createNotFoundError();
            } else {
                return 
                    CorsUtils.isPreFlightRequest(exchange.getRequest()) ? // 是否是PreFlightRequest
                    this.handlePreFlight(exchange) // 是PreFlightRequest
                    : Flux.fromIterable(this.handlerMappings).concatMap((mapping) -> { return mapping.getHandler(exchange); // 不是PreFlightRequest,需要遍历HandlerMapping,调用getHandler()
                }).next().switchIfEmpty(this.createNotFoundError()).flatMap((handler) -> {
                    return this.invokeHandler(exchange, handler);
                }).flatMap((result) -> {
                    return this.handleResult(exchange, result);
                });
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    HandlerMapping的间接实现RoutePredicateHandlerMapping类,该类借助RouteLocator从配置中解析的Flux 匹配Route,然后借助FilteringWebHandler构造List过滤器链

    请添加图片描述

    Gateway Filter 处理请求和响应相关的核心代码在 FilteringWebHandler 类。也就是请求最终由 GatewayFilterChain 里的 Filter 按顺序处理。

    (2)RouteLocator

    Gateway 通过接口 RouteLocator 接口来获取路由配置,RouteLocator 有不同的实现,对应了不同的定义路由的方式。

    • 通过自定义配置类配置路由:将RouteLocator放到容器,形参注入 RouteLocatorBuilder ,然后调用形参的builder()方法返回包含path、url、filter等的RouteLocator
    • 通过自定义yaml配置文件配置路由:RouteLocator 另一种常用的实现是 RouteDefinitionRouteLocator 。这种实现依赖于 RouteDefinitionLocator 来提供 RouteDefinition ,再由 RouteDefinition 构造路由。

    请添加图片描述

    多个 RouteLocator 可以同时使用,这些实例提供的路由最终会由 CompositeRouteLocator 整合,再由 CachingRouteLocator 缓存。他们之间的关系如图。也就是最终的routeLocator会保存在CachingRouteLocator中,当

    请添加图片描述

    (3)RouteDefinition

    RouteDefinitionRoute 的区别在于 RouteDefinition 是一种便于序列化的对象。它定义了一个路由应该包含哪些匹配条件和过滤器,以及这些匹配条件和过滤器使用的参数。在使用时 Gateway 会根据 RouteDefinition 对象提供的定义构造出 Route 对象。

    下面的GatewayProperties、PropertiesRouteDefinitionLocator是yml配置路由解析到RouteDefinition的核心实现,然后RouteLocator在根据RouteDefinition构造路由Route。

    请添加图片描述

    另外Gateway 在构造 Route 对象时会根据 RouteDefinition 配置的匹配条件名和过滤器名找到对应的 Factory 获取对应的匹配条件和过滤器实例链。

    img

    (4)RoutePredicateFactory

    Spring Cloud Gateway 本身已经提供了很多常用的 Predicate 实现,常用的匹配规则都可以直接配置使用,比如 Host、Header、请求参数、请求方法等。需要自定义匹配规则时可以实现接口 RoutePredicateFactory

    (5)GlobalFilter

    过滤器包含了 Gateway 处理请求和响应的核心逻辑。一些功能比较基础通用的过滤器 Spring Cloud Gateway 项目本身已经实现提供了,可以直接配置使用。Spring Cloud Gateway 过滤器分为两类,全局 Filter 和路由 Filter。

    全局 Filter 顾名思义就是会自动对所有的路由都生效的过滤器,有些功能比如全局日志、监控等就比较适合设计成全局过滤器。这类过滤器对应的是 GlobalFilter 接口。需要自定义的全局过滤器时只要实现这个接口并注册成 Bean 即可。

    如练习gateway做登录权限认证就是自定义filter实现了GlobalFilter

    package henu.soft.scl.filter;
    
    import com.alibaba.cloud.commons.lang.StringUtils;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.Ordered;
    import org.springframework.http.HttpStatus;
    import org.springframework.stereotype.Component;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Mono;
    
    import java.util.Date;
    import java.util.List;
    
    /**
     * @author sichaolong
     * @date 2022/8/19 17:00
     */
    
    /**
     * name : MyLoginGlobalFilter.java
     * creator : sichaolong
     * date : 2022/8/19 17:01
     * descript : gateway做登录验证
    **/
    
    @Component
    @Slf4j
    public class MyLoginGlobalFilter implements GlobalFilter, Ordered {
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    
            log.info("come in MyLogGatewayFilter: " + new Date());
    //        String name = exchange.getRequest().getQueryParams().getFirst("name");
            String token = exchange.getRequest().getHeaders().getFirst("token");
    
            log.info("token is :{}",token );
    //        if (StringUtils.isBlank(token)) {
    //            log.info("用户未登录!");
    //            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
    //            return exchange.getResponse().setComplete();
    //        }
            // 继续下一个filter
            return chain.filter(exchange);
        }
    
    
        @Override
        public int getOrder() {
            return 0;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    (6)GatewayFilterFactory

    路由 Filter 是针对某个路由进行配置的。有些功能需要根据具体路由的需求使用不同的参数配置,比如熔断、增删 header 等。这类功能就需要以路由 Filter 的形式实现。要自定义路由 Filter 需要实现接口 GatewayFilterFactory 。由于不同的路由在使用这类 Filter 时可能会使用不同的配置,所以这类 Filter 需要对应的 Factory。接口方法 apply 根据传入的配置再生成 Filter 实例。

    这两类 Filter 在处理请求之前会先放一起排序。Filter 的顺序可以通过添加 Spring 的 Order 注解或者通过实现 Ordered 接口声明。Gateway Filter 处理请求和响应相关的核心代码在 FilteringWebHandler 类。

    可以看到 FilteringWebHandler 会把所有的 GlobalFilter 实例加载进来并且使用 GatewayFilterAdapter 适配成 GatewayFilter 。在 handle 方法处理请求时,会把适配后的 GlobalFilter 以及路由本身的 GatewayFilter 全并在一个 List 里,然后按 Order 排序。排序完之后会构造一个 GatewayFilterChain ,由 GatewayFilterChainfilter 方法触发这些 Filter 的执行。(见下面FilterWebHandler类)

    (7)ReactorLoadBalancer

    gateway支持从注册中心查找指定的服务进行负载均衡,URI 以 lb:// 开头的路由会被 Gateway 自带的 ReactiveLoadBalancerClientFilter 处理,这个 Filter 会识别 URI 中的服务名,并为其创建一个 ReactorLoadBalancer,然后负载均衡策略从对应服务的实例中获取其中一个实例作为请求转发目标。

    img

    (8)FilteringWebHandler

    handler在此完成执行,调用过滤器链的filter方法。

    public class FilteringWebHandler implements WebHandler {
        // 封装好的的 GatewayFilter 集合
        private final List<GatewayFilter> globalFilters;
    	// 构造函数,传入List globalFilters
        public FilteringWebHandler(List<GlobalFilter> globalFilters) {
            this.globalFilters = loadFilters(globalFilters);
        }
    	// 使用内部类 GatewayFilterAdapter 完成  List globalFilters ---> List globalFilters;
        private static List<GatewayFilter> loadFilters(List<GlobalFilter> filters) {
            return (List)filters.stream().map((filter) -> {
                FilteringWebHandler.GatewayFilterAdapter gatewayFilter = new FilteringWebHandler.GatewayFilterAdapter(filter);
                if (filter instanceof Ordered) {
                    int order = ((Ordered)filter).getOrder();
                    return new OrderedGatewayFilter(gatewayFilter, order);
                } else {
                    return gatewayFilter;
                }
            }).collect(Collectors.toList());
        }
    	// 核心的处理请求方法
        public Mono<Void> handle(ServerWebExchange exchange) {
            // 先拿到Route
            Route route = (Route)exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
            // 该Route单独对应的过滤器
            List<GatewayFilter> gatewayFilters = route.getFilters();
            // 整合
            List<GatewayFilter> combined = new ArrayList(this.globalFilters);
            combined.addAll(gatewayFilters);
            AnnotationAwareOrderComparator.sort(combined);
            if (logger.isDebugEnabled()) {
                logger.debug("Sorted gatewayFilterFactories: " + combined);
            }
    		// 内部类 DefaultGatewayFilterChain 过滤器链遍历 执行 filter 方法
            return (new FilteringWebHandler.DefaultGatewayFilterChain(combined)).filter(exchange);
        }
    
        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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
  • 相关阅读:
    操作系统面试题汇总(不定期更新)
    一线大厂必问微服务系列——SpringCloudAlibaba整合Sleuth
    实现fashion_minst服装图像分类
    15薪|20k-40k 前端工程师
    AI算法工程师 | 03人工智能基础-Python科学计算和可视化(二)Matplotlib
    freemarker遍历list拼接成字符串(逗号分隔)
    Ansible 指定受控端使用Python的版本
    RPA的价值和优势有哪些?
    IO模型5-进阶-Netty模型
    Spring系列七:JDK 动态代理和 CGLIB 代理
  • 原文地址:https://blog.csdn.net/qq_24654501/article/details/126551445