聊聊spring cloud gateway的GlobalFilter

本文主要研究一下spring cloud gateway的GlobalFilter

GatewayAutoConfiguration

spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java

@Configuration
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
@EnableConfigurationProperties
@AutoConfigureBefore(HttpHandlerAutoConfiguration.class)
@AutoConfigureAfter({GatewayLoadBalancerClientAutoConfiguration.class, GatewayClassPathWarningAutoConfiguration.class})
@ConditionalOnClass(DispatcherHandler.class)
public class GatewayAutoConfiguration {
    //......
    @Bean
    public FilteringWebHandler filteringWebHandler(List<GlobalFilter> globalFilters) {
        return new FilteringWebHandler(globalFilters);
    }

    @Bean
    public RoutePredicateHandlerMapping routePredicateHandlerMapping(FilteringWebHandler webHandler,
                                                                       RouteLocator routeLocator) {
        return new RoutePredicateHandlerMapping(webHandler, routeLocator);
    }
    //......
}
  • 这里将globalFilters(NettyWriteResponseFilter、ForwardPathFilter、RouteToRequestUrlFilter、LoadBalancerClientFilter、AdaptCachedBodyGlobalFilter、WebsocketRoutingFilter、NettyRoutingFilter、ForwardRoutingFilter)作为构造器参数创建了FilteringWebHandler
  • 而依据FilteringWebHandler和RouteLocator创建了RoutePredicateHandlerMapping,这里的RouteLocator是CachingRouteLocator

FilteringWebHandler

spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/handler/FilteringWebHandler.java

/**
 * WebHandler that delegates to a chain of {@link GlobalFilter} instances and
 * {@link GatewayFilterFactory} instances then to the target {@link WebHandler}.
 *
 * @author Rossen Stoyanchev
 * @author Spencer Gibb
 * @since 0.1
 */
public class FilteringWebHandler implements WebHandler {
    protected static final Log logger = LogFactory.getLog(FilteringWebHandler.class);

    private final List<GatewayFilter> globalFilters;

    public FilteringWebHandler(List<GlobalFilter> globalFilters) {
        this.globalFilters = loadFilters(globalFilters);
    }

    private static List<GatewayFilter> loadFilters(List<GlobalFilter> filters) {
        return filters.stream()
                .map(filter -> {
                    GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter);
                    if (filter instanceof Ordered) {
                        int order = ((Ordered) filter).getOrder();
                        return new OrderedGatewayFilter(gatewayFilter, order);
                    }
                    return gatewayFilter;
                }).collect(Collectors.toList());
    }

    /* TODO: relocate @EventListener(RefreshRoutesEvent.class)
    void handleRefresh() {
        this.combinedFiltersForRoute.clear();
    }*/

    @Override
    public Mono<Void> handle(ServerWebExchange exchange) {
        Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
        List<GatewayFilter> gatewayFilters = route.getFilters();

        List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
        combined.addAll(gatewayFilters);
        //TODO: needed or cached?
        AnnotationAwareOrderComparator.sort(combined);

        logger.debug("Sorted gatewayFilterFactories: "+ combined);

        return new DefaultGatewayFilterChain(combined).filter(exchange);
    }
    //......
}
  • 这里在构造器里头调用loadFilters方法把List 转换为List
  • 之后的handle方法,把选定的route的gatewayFilters与转换后的gatewayFilters合并,然后重新排序
  • 之后使用合并后的gatewayFilters创建DefaultGatewayFilterChain,挨个filter下去

GatewayFilterAdapter

    private static class GatewayFilterAdapter implements GatewayFilter {

        private final GlobalFilter delegate;

        public GatewayFilterAdapter(GlobalFilter delegate) {
            this.delegate = delegate;
        }

        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            return this.delegate.filter(exchange, chain);
        }

        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("GatewayFilterAdapter{");
            sb.append("delegate=").append(delegate);
            sb.append('}');
            return sb.toString();
        }
    }

这里将GlobalFilter适配为GatewayFilter,最后调用filter方法

DefaultGatewayFilterChain

    private static class DefaultGatewayFilterChain implements GatewayFilterChain {

        private final int index;
        private final List<GatewayFilter> filters;

        public DefaultGatewayFilterChain(List<GatewayFilter> filters) {
            this.filters = filters;
            this.index = 0;
        }

        private DefaultGatewayFilterChain(DefaultGatewayFilterChain parent, int index) {
            this.filters = parent.getFilters();
            this.index = index;
        }

        public List<GatewayFilter> getFilters() {
            return filters;
        }

        @Override
        public Mono<Void> filter(ServerWebExchange exchange) {
            return Mono.defer(() -> {
                if (this.index < filters.size()) {
                    GatewayFilter filter = filters.get(this.index);
                    DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this, this.index + 1);
                    return filter.filter(exchange, chain);
                } else {
                    return Mono.empty(); // complete
                }
            });
        }
    }

这里使用了责任链模式,里头filter方法,挨个遍历执行,传入的chain包含了当前的index,用于控制跳出责任链

RoutePredicateHandlerMapping

spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMapping.java

public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {

    private final FilteringWebHandler webHandler;
    private final RouteLocator routeLocator;

    public RoutePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator) {
        this.webHandler = webHandler;
        this.routeLocator = routeLocator;

        setOrder(1);
    }

    @Override
    protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
        exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getClass().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);
                    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) + "]");
                    }
                })));
    }

    @Override
    protected CorsConfiguration getCorsConfiguration(Object handler, ServerWebExchange exchange) {
        //TODO: support cors configuration via global properties and
        // properties on a route see gh-229
        // see RequestMappingHandlerMapping.initCorsConfiguration()
        // also see https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/test/java/org/springframework/web/cors/reactive/CorsWebFilterTests.java
        return super.getCorsConfiguration(handler, exchange);
    }
    //......
}

这个RoutePredicateHandlerMapping主要是实现了父类的getHandlerInternal

DispatcherHandler

spring-webflux-5.0.6.RELEASE-sources.jar!/org/springframework/web/reactive/DispatcherHandler.java

/**
 * Central dispatcher for HTTP request handlers/controllers. Dispatches to
 * registered handlers for processing a request, providing convenient mapping
 * facilities.
 *
 * <p>{@code DispatcherHandler} discovers the delegate components it needs from
 * Spring configuration. It detects the following in the application context:
 * <ul>
 * <li>{@link HandlerMapping} -- map requests to handler objects
 * <li>{@link HandlerAdapter} -- for using any handler interface
 * <li>{@link HandlerResultHandler} -- process handler return values
 * </ul>
 *
 * <p>{@code DispatcherHandler} is also designed to be a Spring bean itself and
 * implements {@link ApplicationContextAware} for access to the context it runs
 * in. If {@code DispatcherHandler} is declared with the bean name "webHandler"
 * it is discovered by {@link WebHttpHandlerBuilder#applicationContext} which
 * creates a processing chain together with {@code WebFilter},
 * {@code WebExceptionHandler} and others.
 *
 * <p>A {@code DispatcherHandler} bean declaration is included in
 * {@link org.springframework.web.reactive.config.EnableWebFlux @EnableWebFlux}
 * configuration.
 *
 * @author Rossen Stoyanchev
 * @author Sebastien Deleuze
 * @author Juergen Hoeller
 * @since 5.0
 * @see WebHttpHandlerBuilder#applicationContext(ApplicationContext)
 */
public class DispatcherHandler implements WebHandler, ApplicationContextAware {

    @SuppressWarnings("ThrowableInstanceNeverThrown")
    private static final Exception HANDLER_NOT_FOUND_EXCEPTION =
            new ResponseStatusException(HttpStatus.NOT_FOUND, "No matching handler");

    private static final Log logger = LogFactory.getLog(DispatcherHandler.class);

    @Nullable
    private List<HandlerMapping> handlerMappings;

    @Nullable
    private List<HandlerAdapter> handlerAdapters;

    @Nullable
    private List<HandlerResultHandler> resultHandlers;

    //......

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        initStrategies(applicationContext);
    }

    protected void initStrategies(ApplicationContext context) {
        Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
                context, HandlerMapping.class, true, false);

        ArrayList<HandlerMapping> mappings = new ArrayList<>(mappingBeans.values());
        AnnotationAwareOrderComparator.sort(mappings);
        this.handlerMappings = Collections.unmodifiableList(mappings);

        Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
                context, HandlerAdapter.class, true, false);

        this.handlerAdapters = new ArrayList<>(adapterBeans.values());
        AnnotationAwareOrderComparator.sort(this.handlerAdapters);

        Map<String, HandlerResultHandler> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
                context, HandlerResultHandler.class, true, false);

        this.resultHandlers = new ArrayList<>(beans.values());
        AnnotationAwareOrderComparator.sort(this.resultHandlers);
    }

    @Override
    public Mono<Void> handle(ServerWebExchange exchange) {
        if (logger.isDebugEnabled()) {
            ServerHttpRequest request = exchange.getRequest();
            logger.debug("Processing " + request.getMethodValue() + " request for [" + request.getURI() + "]");
        }
        if (this.handlerMappings == null) {
            return Mono.error(HANDLER_NOT_FOUND_EXCEPTION);
        }
        return Flux.fromIterable(this.handlerMappings)
                .concatMap(mapping -> mapping.getHandler(exchange))
                .next()
                .switchIfEmpty(Mono.error(HANDLER_NOT_FOUND_EXCEPTION))
                .flatMap(handler -> invokeHandler(exchange, handler))
                .flatMap(result -> handleResult(exchange, result));
    }
    //......
}
  • 这里setApplicationContext的时候调用了initStrategies方法
  • 使用BeanFactoryUtils.beansOfTypeIncludingAncestors获取了容器中注册的HandlerMapping
  • RoutePredicateHandlerMapping注册到了容器中,这里会被获取到
  • handlerMapping用于根据exchange来获取handler
  • 这了使用的是concatMap,如果返回的是Mono.empty()则不会被concat,然后next取第一个,即按handlerMappings排序之后的第一个来

handlerMappings

这里按优先级从高到底有如下几个:

  • WebFluxEndpointHandlerMapping(order=-100)
  • ControllerEndpointHandlerMapping(order=-100)
  • RouterFunctionMapping(order=-1)
  • RequestMappingHandlerMapping(order=0)
  • RoutePredicateHandlerMapping(order=1)
  • SimpleUrlHandlerMapping(Ordered.LOWEST_PRECEDENCE)

小结

spring cloud gateway的GlobalFilter在FilteringWebHandler被适配为GatewayFilter,然后与route级别的gatewayFilters进行合并,作用在当前route上面。RoutePredicateHandlerMapping会被DispatcherHandler识别,按order优先级排序,依次根据mapping来获取该exchange的handler,找到不是Mono.empty()的第一个,然后进行invokeHandler以及handleResult。

因此可以理解为GlobalFilter就是全局的GatewayFilter,作用在所有route上面。而GatewayFilter是route级别的。

doc

  • 113. Global Filters
  • 聊聊spring cloud gateway的RouteLocator

原文发布于微信公众号 - 码匠的流水账(geek_luandun)

原文发表时间:2018-06-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java编程技术

Spring中RequestScope作用域Bean原理

可知上面时序图完成了对RequestScope对象定义的修改创建了代理bean,具体修改内容是修改了beanClass为ScopedProxyFactoryBe...

34420
来自专栏叁金大数据

C#调用C++ Dll

现在项目基本都是旁边C++的哥们做好dll扔给我,然后我调用。好久之前晚上down了一份c#调用c++dll的方法,出处早已经遗忘。闲来无事,放上来好了。原作者...

44720
来自专栏码匠的流水账

聊聊storm的window trigger

storm-core-1.2.2-sources.jar!/org/apache/storm/trident/windowing/WindowTridentPr...

10840
来自专栏码匠的流水账

聊聊storm的window trigger

storm-core-1.2.2-sources.jar!/org/apache/storm/trident/windowing/WindowTridentPr...

13500
来自专栏码匠的流水账

聊聊NettyConnector的start及shutdown

reactor-netty-0.7.6.RELEASE-sources.jar!/reactor/ipc/netty/NettyConnector.java

13910
来自专栏Android相关

CoordiantorLayout与Behavior

CoordinatorLayout继承自FrameLayout,并且实现了NestedScrollingParent2接口用于接收嵌套滑动的事件。并且内部定义了...

10320
来自专栏码匠的流水账

聊聊storm的IEventLogger

storm-2.0.0/storm-client/src/jvm/org/apache/storm/metric/IEventLogger.java

14930
来自专栏微信公众号:Java团长

一个经典例子让你彻彻底底理解Java回调机制

以前不理解什么叫回调,天天听人家说加一个回调方法啥的,心里想我草,什么叫回调方法啊?然后自己就在网上找啊找啊找,找了很多也不是很明白,现在知道了,所谓回调:就是...

15340
来自专栏张善友的专栏

如何结合IbatisNet的LIST遍历实现模糊查询

我仿照Java的Spring+Ibatis+Struct用Castle+IBatisNet+Asp.net的开发框架的DAO的基类:BaseSqlMapDao内...

24790
来自专栏码匠的流水账

reactor-netty中HttpClient对TcpClient的封装

本文主要研究一下reactor-netty中HttpClient对TcpClien的封装

43510

扫码关注云+社区

领取腾讯云代金券