简介: 最近通过深刻学习Spring Cloud Gateway发现这个框架的架构设计非常简单、无效,很多组件的设计都十分值得学习,本文就Spring Cloud Gateway做一个简略的介绍,以及针对一次申请Spring Cloud Gateway的解决流程做一个较为具体的剖析。

作者 | 寻筝
起源 | 阿里技术公众号

一 前言

最近通过深刻学习Spring Cloud Gateway发现这个框架的架构设计非常简单、无效,很多组件的设计都十分值得学习,本文就Spring Cloud Gateway做一个简略的介绍,以及针对一次申请Spring Cloud Gateway的解决流程做一个较为具体的剖析。

二 简介

Spring Cloud Gateway 即Spring官网推出的一款API网关,该框架蕴含了Spring5、SpringBoot2、Project Reactor,其中底层通信框架用的netty。Spring Cloud Gateway在推出之初的时候,Netflix公司曾经推出了相似性能的API网关框架ZUUL,但ZUUL有一个毛病是通信形式是阻塞的,尽管起初降级到了非阻塞式的ZUUL2,然而因为Spring Cloud Gateway曾经推出一段时间,同时本身也面临材料少、维护性较差的因素没有被广泛应用。

1 要害术语

在应用Spring Cloud Gateway的时候须要了解三个模块,即

Route:

即一套路由规定,是集URI、predicate、filter等属性的一个元数据类。

Predicate:

这是Java8函数式编程的一个办法,这里能够看做是满足什么条件的时候,route规定进行失效。

Filter:

filter能够认为是Spring Cloud Gateway最外围的模块,熔断、平安、逻辑执行、网络调用都是filter来实现的,其中又细分为gateway filter和global filter,区别在于是具体一个route规定失效还是所有route规定都失效。

能够先上一段代码来看看:

 @RequestMapping("/paramTest")  public Object paramTest(@RequestParam Map<String,Object> param) {      return param.get("name");  }  @Bean  public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {return builder.routes()      .route("path_route", r ->                   r.path("/get")                  .filters(f -> f.addRequestParameter("name", "value"))                  .uri("forward:///paramTest"))      .build();  }
  • route办法代表的就是一个路由规定;
  • path办法代表的就是一个predicate,背地的事实是PathRoutePredicateFactory,在这段代码的含意即当门路蕴含/get的时候,以后规定失效。
  • filters办法的意思即给以后路由规定增加一个减少申请参数的filter,每次申请都对参数里增加 name:value 的键值对;
  • uri 办法的含意即最终路由到哪里去,这里的forward前缀会将申请交给spring mvc的DispatcherHandler进行路由,进行本机的逻辑调用,除了forward以外还能够应用http、https前缀进行http调用,lb前缀能够在配置注册核心后进行rpc调用。

上图是Spring Cloud Gateway官网文档给出的一个工作原理图,Spring Cloud Gateway 接管到申请后进行路由规定的匹配,而后交给web handler 进行解决,web handler 会执行一系列的filter逻辑。

三 流程剖析

1 承受申请

Spring Cloud Gateway的底层框架是netty,承受申请的要害类是ReactorHttpHandlerAdapter,做的事件很简略,就是将netty的申请、响应转为http的申请、响应并交给一个http handler执行前面的逻辑,下图为该类的源码仅保留外围逻辑。

@Overridepublic Mono< Void> apply(HttpServerRequest request, HttpServerResponse response) {    NettyDataBufferFactory bufferFactory = new NettyDataBufferFactory(response.alloc());    ServerHttpRequest adaptedRequest;    ServerHttpResponse adaptedResponse;//转换申请    try {        adaptedRequest = new ReactorServerHttpRequest(request, bufferFactory);        adaptedResponse = new ReactorServerHttpResponse(response, bufferFactory);    }    catch (URISyntaxException ex) {        if (logger.isWarnEnabled()) {        ...    }    ...    return this.httpHandler.handle(adaptedRequest, adaptedResponse)            .doOnError(ex -> logger.warn("Handling completed with error: " + ex.getMessage()))            .doOnSuccess(aVoid -> logger.debug("Handling completed with success"));}

2 WEB过滤器链

http handler做的事件第一是将request 和 response转为一个exchange,这个exchange十分外围,是各个filter之间参数流转的载体,该类蕴含request、response、attributes(扩大字段),接着做的事件就是web filter链的执行,其中的逻辑次要是监控。

其中WebfilterChainParoxy 又会引出新的一条filter链,次要是平安、日志、认证相干的逻辑,由此可见Spring Cloud Gateway的过滤器设计是层层嵌套,扩展性很强。

3 寻找路由规定

外围类是RoutePredicateHandlerMapping,逻辑也非常简单,就是把所有的route规定的predicate遍历一遍看哪个predicate可能命中,外围代码是:

return this.routeLocator.getRoutes()      .filter(route -> {         ...         return route.getPredicate().test(exchange);      })

因为我这里用的是path进行过滤,所以背地的逻辑是PathRoutePredicateFactory来实现的,除了PathRoutePredicateFactory还有很多predicate规定。

这些路由规定都能从官网文档上找到影子。

4 外围过滤器链执行

找到路由规定后下一步就是执行了,这里的外围类是FilteringWebHandler,其中的源码为:

做的事件很简略:

  • 获取route级别的过滤器
  • 获取全局过滤器
  • 两种过滤器放在一起并依据order进行排序
  • 执行过滤器链

因为我的配置里蕴含了一个增加申请参数的逻辑,所以红线箭头处就是我配置的gateway filter名为 AddRequestParameterGatewayFilterFactory,其余全是Gloabl Filter,这些过滤器的性能次要是url解析,申请转发,响应回写等逻辑,因为咱们这里用的是forward schema,所以申请转发会由ForwardRoutingFilter进行执行。

5 申请转发

ForwardRoutingFilter做的事件也很简略,间接复用了spring mvc的能力,将申请提交给dispatcherHandler进行解决,dispatcherHandler会依据path前缀找到须要指标处理器执行逻辑。

@Overridepublic Mono< Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {   URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);   String scheme = requestUrl.getScheme();   if (isAlreadyRouted(exchange) || !"forward".equals(scheme)) {      return chain.filter(exchange);   }   setAlreadyRouted(exchange);   //TODO: translate url?   if (log.isTraceEnabled()) {      log.trace("Forwarding to URI: "+requestUrl);   }   return this.dispatcherHandler.handle(exchange);}

6 响应回写

响应回写的外围类是NettyWriteResponseFilter,然而大家能够留神到执行器链中NettyWriteResponseFilter的排序是在最后面的,按情理这种响应解决的类应该是在靠后才对,这里的设计比拟奇妙。大家能够看到chain.filter(exchange).then(),意思就是执行到我的时候间接跳过下一个,等前面的过滤器都执行完后才执行这段逻辑,这种行为管制的办法值得学习。

@Overridepublic Mono< Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {   // NOTICE: nothing in "pre" filter stage as CLIENT_RESPONSE_ATTR is not added   // until the WebHandler is run   return chain.filter(exchange).then(Mono.defer(() -> {      HttpClientResponse clientResponse = exchange.getAttribute(CLIENT_RESPONSE_ATTR);      if (clientResponse == null) {         return Mono.empty();      }      log.trace("NettyWriteResponseFilter start");      ServerHttpResponse response = exchange.getResponse();      NettyDataBufferFactory factory = (NettyDataBufferFactory) response.bufferFactory();      //TODO: what if it's not netty      final Flux< NettyDataBuffer> body = clientResponse.receive()            .retain() //TODO: needed?            .map(factory::wrap);      MediaType contentType = response.getHeaders().getContentType();      return (isStreamingMediaType(contentType) ?            response.writeAndFlushWith(body.map(Flux::just)) : response.writeWith(body));   }));}

四 总结

整体读完Spring Cloud Gateway申请流程代码后,有几点感触:

  • 过滤器是Spring Cloud Gateway最外围的设计,甚至于能够夸大说Spring Cloud Gateway是一个过滤器链执行框架而不是一个API网关,因为API网关理论的申请转发、申请响应回写都是在过滤器中做的,这些是Spring Cloud Gateway感知不到的逻辑。
  • Spring Cloud Gateway路由规定获取的模块具备优化的空间,因为是循环遍历进行获取的,如果每个route规定较多,predicate规定较简单,就能够思考用map进行优化了,当日route规定,predicate规定也不会很简单,兼顾到代码的可读性,以后形式也没有什么问题。
  • 作为API网关框架,内置了十分多的过滤器,如果有过滤器的卸载性能可能会更好,用户可用依据理论状况卸载不必要的性能,背地缩小的逻辑开销,在调用量极大的API网关场景,收益也会很可观。

原文链接
本文为阿里云原创内容,未经容许不得转载。