共计 5263 个字符,预计需要花费 14 分钟才能阅读完成。
简介:最近通过深刻学习 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 执行前面的逻辑,下图为该类的源码仅保留外围逻辑。
@Override
public 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 前缀找到须要指标处理器执行逻辑。
@Override
public 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(),意思就是执行到我的时候间接跳过下一个,等前面的过滤器都执行完后才执行这段逻辑,这种行为管制的办法值得学习。
@Override
public 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 网关场景,收益也会很可观。
原文链接
本文为阿里云原创内容,未经容许不得转载。