简介: 最近通过深刻学习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网关场景,收益也会很可观。
原文链接
本文为阿里云原创内容,未经容许不得转载。