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

原文地址:https://www.cnblogs.com/davidwang456/p/10402768.html