zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Javascript多线程引擎(一)
    Windows下Git使用入门
    Linux创建新用户,给予FTP操作权限
    mysql数据库设置远程连接权限
    Linux下修改mysql的root密码后数据库消失怎么处理
    php mysql 存储 IOS Emoji表情失败和乱码问题
    RDS for MySQL 如何定位本地 IP
    Geohash距离估算
    GeoHash核心原理解析
    阿里云配置免费DVSSL证书(For Apache)
  • 原文地址:https://www.cnblogs.com/davidwang456/p/10402768.html
Copyright © 2011-2022 走看看