再谈SpringCloud Gateway源码

再谈SpringCloud Gateway源码

前言:

之前有阅读过一次SpringCloud Gateway的源码,不过那时更多的是浮于表面,走了一遍流程。直到现在工作中真的遇到了基于SpringCloud Gateway的业务开发,才发现源码中很多机制还是不熟悉,于时又重新学习了一遍源码,并做此记录

前置知识:

  1. Reactive Streams&Reactor Core
  2. Spring WebFlux
  3. 浅谈Spring Cloud Gateway源码

一、整体请求流程

SpringCloud Gateway是基于Spring WebFlux完成请求的分发,整合SpringCloud Gateway后请求分发流程如下图:


二、前置对象准备

上面的流程更多的是描述当一个请求进来以后的分发流程,但是涉及到的一些对象都是在项目启动阶段完成的实例化

1、实例化HandlerMapping

在SpringCloud Gateway中会进行自动装配,自动创建一个RoutePredicateHandlerMapping,用于后续处理请求。当然我们也可以自定义,只要顺序在他的前面就可以(这也是常见自定义HandlerMapping的操作)

2、实例化Route

该部分逻辑涉及实例化对象较多

在HandlerMapping中,会涉及当前请求url与Route中定义的请求url匹配的逻辑,匹配命中则代表是网关请求,如果无法匹配命中则继续使用后续的handlerMapping判定,如当前项目上的创建的controller的接口的HandlerMapping。

1)RouteDefinitionLocator :实例化CompositeRouteDefinitionLocator

CompositeRouteDefinitionLocator可以看做是RouteDefinitionLocator的包装器,用于遍历访问所有RouteDefinitionLocator中定义的RouteDefinition。

在使用侧,我们只需要按照RouteDefinitionLocator中RouteDefinition的标准定义对象,即可完成自定义的添加

2)RouteLocator :实例化RouteDefinitionRouteLocator

这里会借助GatewayProperties、GatewayFilterFactory、RoutePredicateFactory,以及上一步创建的CompositeRouteDefinitionLocator(内部包含所有的RouteDefinition)来实例化该对象。

请注意,由于@Primary注解的存在,Gateway中使用的RouteLocator其实是下面的CachingRouteLocator,不过在CachingRouteLocator中,只是对所有的Route进行了缓存

值得一提的是,这里的CompositeRouteLocator和前面的CompositeRouteDefinitionLocator的作用都是一致的,都是完成对所RouteLocator的包装,尽管示例代码中只有一个,但在真实的业务场景中,我们同样可以进行自定义扩展

注意:记住前面的RouteDefinitionLocator和RouteLocator,都是被Composite包装过一次,并且逻辑都是封装遍历。明白这个对于后续业务代码的执行流程至关重要。

3)Route :实例化Route

回顾最整体的请求流程,在获取HandlerMapping的时候,会调用到getHandlerInternal方法(org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping#getHandlerInternal。再往后走会调用到lookupRoute方法(org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping#lookupRoute)

即下图示例位置:

根据前面的解释,调用getRoutes的逻辑会遍历所有的RouteLocator ,然后在RouteLocator内部又会遍历调用所有的RouteDefinitionLocator 。此时就到了最关键的convertToRoute方法。该方法就会构建所有的断路器、拦截器(实例化Bean的时候有传递对应的Factory,在此处完成构建)

最终lookupRoute的返回值是Mono<Route>对象,即会返回匹配到的第一个Route,该对象就是我们预期命中的Route

3、实例化WebHandler

在getHandlerInternal方法(org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping#getHandlerInternal)中

,在命中Route路由后,只是将Route放置在了上下文中,此处真正返回的是在该HandlerMapping中定义的WebHandler对象

WebHandler对象同样是在自动装配类中实例化的,并且附带过滤器配置。

于是,我们可以得到结论,在最后传递到HandlerAdapter中执行handler请求的Object对象就是Gateway中定义的FilteringWebHandler(WebHandler)

最终在该WebHandler中,完成对应的执行逻辑。查看代码可以得出结论,这里的Filter主要分为两部分,一部分是从Route对象上获取的Filter(GatewayFilter),还有一部分是全局Filter(GlobalFilter),这部分在该WebHandler实例化的就传递进来了。将所有的Filter串联在一起,以一个责任链的形式完成业务逻辑


三、实践业务扩展点

1、定义扩展Route对象

我们可以自定义一个RouteDefinitionLocator类型的Bean,只要在调用该类的getRouteDefinitions方法时,返回我们自定义的Route,具体定义方式有很多,比如使用JSON,只需要最终解析出来的逻辑满足RouteDefinition规范即可

2、Filter能做什么

更多请参考官网文档

常见GlobalFilter:

  1. AdaptCachedBodyGlobalFilter:缓存
  2. NettyWriteResponseFilter:response写入
  3. ForwardPathFilter:转发请求
  4. ReactiveLoadBalancerClientFilter:负载均衡
  5. LoadBalancerServiceInstanceCookieFilter:根据cookie负载均衡
  6. NettyRoutingFilter:利用netty发起请求

常见GatewayFilter:

  1. AddRequestHeaderGatewayFilter:添加请求头
  2. PrefixPathGatewayFilter:切除path前缀
  3. RewritePathGatewayFilter:重写path
  4. RetryGatewayFilter:请求重试
  5. RemoveRequestHeaderGatewayFilter:移除请求头信息
  6. RequestRateLimiterGatewayFilter:限流

业务系统中常见的使用:

  1. 认证授权
  2. 基础参数校验
  3. 消息结构体的转化(XML转JSON)
  4. 业务参数级别的限流(尽管自带限流,但是功能过于简单,无法满足企业级系统的能力)
  5. 配置参数完成请求参数和返回结果的二次修改
  6. RPC的泛化调用
  7. 业务指标的监控埋点
  8. ...

3、定义扩展Filter对象

如果我们希望这个Filter不用配置全局生效,就继承GlobalFilter;如果是希望在RouteDefinition中定义了才生效,则继承GatewayFilter。

补充:

  1. 针对GatewayFilter,我们要定义的有两部分,一个是Filter的定义AbstractGatewayFilterFactory类(Filter的Factory),需要实现apply方法用于实例化相应的GatewayFilter对象(可使用OrderedGatewayFilter对象进行包装);另一个是Filter的实现类,用于完成具体的业务逻辑。
  2. 执行流程为,在Route实例化的时候(convertToRoute),会根据Route中配置的Filter列表名字过滤(不修改则使用默认规则)出后所有的GatewayFilterFactory,然后调用GatewayFilterFactory的apply方法,此时就会回调到我们创建的GatewayFilter类的apply方法,此时只需要在apply方法中调用我们真正的业务Filter即可(尽管这里是Factory,但是可以参考适配器模式理解),示例代码如下:
java 复制代码
@Component
public class RequestHandlerGatewayFilterFactory extends AbstractGatewayFilterFactory<RequestHandlerGatewayFilterConfig> {
    @Autowired
    RequestHandlerGatewayFilter filter;

    public RequestHandlerGatewayFilterFactory() {
        // 指定配置文件对象类型
        super(RequestHandlerGatewayFilterConfig.class);
    }

    @Override
    public GatewayFilter apply(RequestHandlerGatewayFilterConfig config) {
        // 设置filter顺序
        int order = Optional.ofNullable(config)
                .map(BaseFilterConfig::getOrder)
                .orElse(RouteOrderConstants.REQUEST_HANDLER_ORDER);

        return new OrderedGatewayFilter((exchange, chain) -> {
            // 配置文件内容传递
            exchange.getAttributes().put(GatewayConstants.CACHE_REQUEST_REQUEST_HANDLER_CONFIG, config);
            return filter.apply(exchange, chain);
        }, order);
    }
}

4、定义父类Filter简化请求参数处理

GatewayFilter的filter方法参数为ServerWebExchange对象,这并不方便我们在业务上对请求参数处理。我们可以编写一个抽象父类,用于屏蔽掉请求参数获取这部分,示例代码可参考:

java 复制代码
public class CacheOutputMessage implements ReactiveHttpOutputMessage {

    private final DataBufferFactory bufferFactory;

    private final HttpHeaders httpHeaders;

    private Flux<DataBuffer> body = Flux.error(new IllegalStateException("error"));

    public CacheOutputMessage(ServerWebExchange exchange, HttpHeaders httpHeaders) {
        this.bufferFactory = exchange.getResponse().bufferFactory();
        this.httpHeaders = httpHeaders;
    }

    @Override
    public void beforeCommit(Supplier<? extends Mono<Void>> action) {
    }

    @Override
    public boolean isCommitted() {
        return false;
    }

    @Override
    public HttpHeaders getHeaders() {
        return this.httpHeaders;
    }

    @Override
    public DataBufferFactory bufferFactory() {
        return this.bufferFactory;
    }
    
    
    public Flux<DataBuffer> getBody() {
        return this.body;
    }

    @Override
    public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
        this.body = Flux.from(body);
        return Mono.empty();
    }

    @Override
    public Mono<Void> writeAndFlushWith(
            Publisher<? extends Publisher<? extends DataBuffer>> body) {
        return writeWith(Flux.from(body)
                .flatMap(p -> p));
    }

    @Override
    public Mono<Void> setComplete() {
        return writeWith(Flux.empty());
    }
}
java 复制代码
public class AbstractGatewayFilter {

    /**
     * 入口:FilterFactory的回调方法
     */
    public Mono<Void> apply(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 如果已经完成请求,不再执行后续操作
        if (ServerWebExchangeUtils.isAlreadyRouted(exchange)) {
            return chain.filter(exchange);
        }

        // 1、修改请求体
        ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
        Mono<String> afterModifiedBody = serverRequest.bodyToMono(String.class)
                .defaultIfEmpty("")
                .flatMap(body -> {
                    BiFunction<ServerWebExchange, Mono<String>, Mono<String>> modifyBodyFun =
                            (funExchange, funBody) ->
                                    // 修改请求内容
                                    funBody.map(b -> getModifiedBody(b, exchange));
                    return modifyBodyFun.apply(exchange, Mono.just(body));
                });

        // 2、重新构造请求对象(含有请求头修改)
        return prepareOutputMessage(exchange, afterModifiedBody)
                .flatMap(outputMessage -> chain.filter(
                        exchange.mutate().request(
                                decorate(exchange, outputMessage.getHeaders(), outputMessage)
                        ).build()
                ));
    }

    private Mono<CacheOutputMessage> prepareOutputMessage(ServerWebExchange exchange, Mono<String> modifiedBody) {
        HttpHeaders headers = copyHeaders(exchange.getRequest().getHeaders());
        BodyInserter<Mono<String>, ReactiveHttpOutputMessage> bodyInserter
                = BodyInserters.fromPublisher(modifiedBody, String.class);

        CacheOutputMessage outputMessage = new CacheOutputMessage(exchange, headers);
        return bodyInserter.insert(outputMessage, new BodyInserterContext())
                .thenReturn(outputMessage);
    }

    private ServerHttpRequestDecorator decorate(ServerWebExchange exchange,
                                                HttpHeaders headers,
                                                CacheOutputMessage outputMessage) {
        return new ServerHttpRequestDecorator(exchange.getRequest()) {
            @Override
            public HttpHeaders getHeaders() {
                return copyHeaders(getModifiedHeaders(exchange, super.getHeaders()));
            }

            @Override
            public Flux<DataBuffer> getBody() {
                return outputMessage.getBody();
            }
        };
    }

    /**
     * 复制一份新的请求头
     */
    private HttpHeaders copyHeaders(HttpHeaders headers) {
        HttpHeaders newHeaders = new HttpHeaders();
        if (headers == null) {
            return newHeaders;
        }
        Map<String, List<String>> map = new HashMap<>();
        headers.forEach((key, dataList) ->
                map.put(key,
                        CollectionUtils.isEmpty(dataList) ? dataList : new ArrayList<>(dataList))
        );
        newHeaders.putAll(map);
        return newHeaders;
    }
    
    /**
     * 子类可重写此方法,用于修改请求头
     */
    protected HttpHeaders getModifiedHeaders(ServerWebExchange exchange, HttpHeaders headers) {
        return headers;
    }

    /**
     * 子类可重写此方法,用于修改请求内容
     */
    protected StringtModifiedBody(String body, ServerWebExchange exchange) {
        return body;
    }
}
相关推荐
okok__TXF8 分钟前
Mybatis源码分析
java·后端·mybatis
白云如幻27 分钟前
【Java】Hibernate的一级缓存
java·hibernate
eternal__day1 小时前
Spring Boot 快速入手
java·spring boot·后端·spring·java-ee·maven
爱的叹息1 小时前
Spring Boot中事务状态(TransactionStatus)的核心信息及常见应用场景
java·spring boot·后端
杉之2 小时前
Java中的不可变集合
java·笔记·学习
潘多编程2 小时前
Gradle实战指南:从入门到进阶,与Maven的深度对比
java·maven
故城、2 小时前
MQ中的RabbitMQ
java·mq
橘猫云计算机设计2 小时前
基于JavaWeb的二手图书交易系统(源码+lw+部署文档+讲解),源码可白嫖!
java·开发语言·前端·毕业设计·php
猿java2 小时前
程序员,你使用过灰度发布吗?
java·分布式·后端