• 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

  • 相关阅读:
    Memento模式
    CSS实现半透明div层的方法
    JS解析json数据(如何将json字符串转化为数组)
    并发容器Map之二:ConcurrentNavigableMap
    JSinArray检查数组中是否存在某个值
    Servlet3.0之七:@WebFilter申明该类为过滤器
    Spring源码阅览——BeanFactory体系结构
    使用 Selenium RC 测试 web 应用程序
    函数式编程
    9 个 Java 处理 Exception 的最佳实践
  • 原文地址:https://www.cnblogs.com/davidwang456/p/10402768.html
Copyright © 2020-2023  润新知