• api网关揭秘--spring cloud gateway源码解析


    要想了解spring cloud gateway的源码,要熟悉spring webflux,我的上篇文章介绍了spring webflux。

    1.gateway 和zuul对比

    I am the author of spring cloud gateway. Zuul is built on servlet 2.5 (works with 3.x), using blocking APIs. It doesn't support any long lived connections, like websockets.
    
    Gateway is built on Spring Framework 5, Project Reactor and Spring Boot 2 using non-blocking APIs. Websockets are supported and it's a much better developer experience since it's tightly integrated with Spring.

    简单的来说:

      1.zuul是基于servlet 2.5,兼容servlet3.0,使用的是阻塞API,不支持长连接如websocket

      2.Gateway基于spring5,Reactor和Spring boot2使用了非阻塞API,支持websocket,和spring完美集成,对开发者友好。

      另外

      3.zuul2支持非阻塞API,但没有和spring cloud集成,且已经停止维护,不在考虑之列。

    2.架构图

    3.自动配置解析core spring.factories

    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=
    org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration,
    org.springframework.cloud.gateway.config.GatewayAutoConfiguration,
    org.springframework.cloud.gateway.config.GatewayLoadBalancerClientAutoConfiguration,
    org.springframework.cloud.gateway.config.GatewayNoLoadBalancerClientAutoConfiguration,
    org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration,
    org.springframework.cloud.gateway.config.GatewayRedisAutoConfiguration,
    org.springframework.cloud.gateway.discovery.GatewayDiscoveryClientAutoConfiguration
    org.springframework.boot.env.EnvironmentPostProcessor=
    org.springframework.cloud.gateway.config.GatewayEnvironmentPostProcessor

    3.1 GatewayClassPathWarningAutoConfiguration检查前端控制器

        @Configuration
        @ConditionalOnClass(name = "org.springframework.web.servlet.DispatcherServlet")
        protected static class SpringMvcFoundOnClasspathConfiguration {
    
            public SpringMvcFoundOnClasspathConfiguration() {
                log.warn(BORDER
                        + "Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway at this time. "
                        + "Please remove spring-boot-starter-web dependency." + BORDER);
            }
    
        }
    
        @Configuration
        @ConditionalOnMissingClass("org.springframework.web.reactive.DispatcherHandler")
        protected static class WebfluxMissingFromClasspathConfiguration {
    
            public WebfluxMissingFromClasspathConfiguration() {
                log.warn(BORDER + "Spring Webflux is missing from the classpath, "
                        + "which is required for Spring Cloud Gateway at this time. "
                        + "Please add spring-boot-starter-webflux dependency." + BORDER);
            }
    
        }

    1.若存在springmvc的前端控制器org.springframework.web.servlet.DispatcherServlet,则报警,原因是使用非阻塞的API

    2.若不存在spring webflux的前端控制器org.springframework.web.reactive.DispatcherHandler,则报警,原因是需要使用非阻塞的API

    3.2 网关自动配置GatewayAutoConfiguration

    3.2.1 RoutePredicateHandlerMapping

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

    RoutePredicateHandlerMapping继承自AbstractHandlerMapping,在此步进行路径查找

        @Override
        protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
            // don't handle requests on the management port if set
            if (managmentPort != null
                    && exchange.getRequest().getURI().getPort() == managmentPort.intValue()) {
                return Mono.empty();
            }
            exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getSimpleName());
    
            return lookupRoute(exchange)
                    // .log("route-predicate-handler-mapping", Level.FINER) //name this
                    .flatMap((Function<Route, Mono<?>>) r -> {
                        exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
                        if (logger.isDebugEnabled()) {
                            logger.debug(
                                    "Mapping [" + getExchangeDesc(exchange) + "] to " + r);
                        }
    
                        exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r); //2
                        return Mono.just(webHandler);
                    }).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
                        exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
                        if (logger.isTraceEnabled()) {
                            logger.trace("No RouteDefinition found for ["
                                    + getExchangeDesc(exchange) + "]");
                        }
                    })));
        }
    
    protected Mono<Route> lookupRoute(ServerWebExchange exchange) {//1
            return this.routeLocator.getRoutes()
                    // individually filter routes so that filterWhen error delaying is not a
                    // problem
                    .concatMap(route -> Mono.just(route).filterWhen(r -> {
                        // add the current route we are testing
                        exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
                        return r.getPredicate().apply(exchange);
                    })
                            // instead of immediately stopping main flux due to error, log and
                            // swallow it
                            .doOnError(e -> logger.error(
                                    "Error applying predicate for route: " + route.getId(),
                                    e))
                            .onErrorResume(e -> Mono.empty()))
                    // .defaultIfEmpty() put a static Route not found
                    // or .switchIfEmpty()
                    // .switchIfEmpty(Mono.<Route>empty().log("noroute"))
                    .next()
                    // TODO: error handling
                    .map(route -> {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Route matched: " + route.getId());
                        }
                        validateRoute(route, exchange);
                        return route;
                    });
    
            /*
             * TODO: trace logging if (logger.isTraceEnabled()) {
             * logger.trace("RouteDefinition did not match: " + routeDefinition.getId()); }
             */
        }

     1.通过RouteLocator返回Route

     关于Route的定义

        private Route(String id, URI uri, int order,
                AsyncPredicate<ServerWebExchange> predicate,
                List<GatewayFilter> gatewayFilters) {
            this.id = id;
            this.uri = uri;
            this.order = order;
            this.predicate = predicate;
            this.gatewayFilters = gatewayFilters;
        }

    可以看出url和gatewayFilter进行了绑定

     2.将Route放入ServerWebExchange的属性中。

    3.2.2 FilteringWebHandler

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

    参考文献:

    【1】https://stackoverflow.com/questions/47092048/how-spring-cloud-gateway-is-different-from-zuul

    【2】https://blog.csdn.net/xuejike/article/details/81290695

  • 相关阅读:
    Go语言之函数基础
    Go语言之内置函数与包函数
    【LeetCode】1668.最大重复子字符串(三)
    [Python]编码规范性(四)——注释(属性、格式)
    【LeetCode】1929. 数组串联(2)
    [Python]编码规范性(三)——注释(类、接口、函数)
    [Python]编码规范性(五)——命名(包和模块、类、函数)
    [Python]序列为空的判定
    【LeetCode】27. 移除元素
    [Python]编码规范性(一)——排版(缩进、语句、空格)
  • 原文地址:https://www.cnblogs.com/davidwang456/p/10402768.html
Copyright © 2020-2023  润新知