-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
API网关异步化改造技术选型 #34
Comments
请问大神最后用了什么方案来做网关 |
请问spring gateway 有http 1.0下,性能是不是很差? |
@mingletxt spring-cloud/spring-cloud-gateway#301 这里有个测试可以参考下 |
@mingletxt 我最后采取的是spring 4.x + Spring boot 1.5.x的方案,基于servlet3.1的异步,因为我们内部很多插件还不支持spring boot2.x,后续会迁移到spring webflux的方案 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
背景
目前的网关是基于
Spring Boot 1.5.x
和Tomcat 8.5.x
构建,采用多线程阻塞模型,也就是说每个请求都会占用一个独立的线程资源,而线程在JVM中是一个相对比较重的资源。当应用是CPU密集型的或者说依赖的远程服务都正常工作时,这种模型能够很好的满足需求,但一旦后端服务出现了延迟,比如慢查询、FullGC、依赖的第三方接口出问题等情况,线程池很容易被打满,使得整个集群服务出现问题。典型的IO密集型的应用也会有类似的问题,比如网关有很多HTTP请求、RPC远程调用等,当并发量比较大的时候,线程都阻塞在IO等待上,造成线程资源的浪费。这种模型的优势比较明显:
但劣势也很明显:
而异步化的方式则完全不同,通常情况下一个CPU核启动一个线程即可处理所有的请求、响应。一个请求的生命周期不再固定于一个线程,而是会分成不同的阶段交由不同的线程池处理,系统的资源能够得到更充分的利用。而且因为线程不再被某一个连接独占,一个连接所占用的系统资源也会低得多,只是一个文件描述符加上几个监听器,而在阻塞模型中,每条连接都会独占一个线程,是一个非常重的资源。对于上游服务的延迟情况,能够得到很大的缓解,因为在阻塞模型中,慢请求会独占一个线程资源,而异步化之后,因为单条连接诶所占用的资源变的非常低,因此系统可以同时处理大量的请求。
因此考虑对网关进行异步化改造,解决当前遇到的超时、延迟等问题。
技术选型
Zuul 2
Zuul 2基于Netty和RxJava实现,采用了异步非阻塞模型,本质上其实就是队列+事件驱动。在zuul 1中一个请求的完整生命周期都是在一个线程中完成的,但在zuul 2中,请求首先会经过netty server,接着会运行前置拦截器,然后通过netty客户端将请求转发给后端的服务,最后运行后置拦截器并返回响应。但是和zuul 1不同,这里的拦截器同时支持异步和同步两种模式,对于一些比较快的操作,可以直接使用同步拦截器。
异步拦截器示例:
这里返回的是一个
Observable
,这是RxJava
中的概念,和Java8的CompletableFuture
有点像,对于方法调用者来说拿到的都是一个Observable
,而内部的实现方式可以是同步,也可以是异步,但是调用者不用关心这个东西,无论实现怎么改,方法的签名是不用变的,始终返回的都是一个Observable
。Zuul 2是一个不错的选择,但是spring官方已经不打算集成zuul 2了,加上Netflix也打算把技术栈尽可能的迁移到Spring,hystrix和Eureka也都进入维护状态,不再开发新特性,zuul未来也有可能是同样的命运。
基于Servlet3.1的异步
Servlet3.1引入了非阻塞式编程模型,支持请求的异步处理。
Spring 4.x+
也增加了对非阻塞式IO的支持,例如下面的代码示例(SpringMVC5 + Tomcat 8.5+):虽然说Servlet3.1提供了对异步的支持,但是其编程模型本质上还是同步的:
Filter
,Servlet
, 或者有一些方法仍然是阻塞的,比如getParameter
,getPart
等,解析请求体、写会响应本质上还是同步的,但一般来说性能损耗也不算大,网关的耗时基本上都在业务方的IO调用上。Spring 5 Reactive
对于异步编程模型的选择,Spring5中引入了两种方式,一种是构建于Servlet 3.1之上的
SpringMVC
,另一种是构建于Netty之上的Spring WebFlux
。Spring WebFlux
不同于Spring MVC
,是一个专门为异步设计的响应式框架,完全非阻塞,支持响应式编程模型,可以运行在 Netty, Undertow, 和 Servlet 3.1+容器中。不同于SpringMVC,WebFlux的请求体、响应都支持响应式类型,可以异步的接受、写入响应,是一个完全异步化的框架。
另外
Spring WebFlux
也提供了一个响应式、非阻塞的HTTP客户端:WebClient
. 其内部支持多种实现,默认是Reactor Netty
,也支持Jetty reactive HttpClient,当然也可以自己通过ClientHttpConnector
扩展。Spring Cloud Gateway
Spring Cloud Gateway是由spring官方基于Spring5.0、Spring Boot2.0、Project Reactor等技术开发的网关,目的是代替原先版本中的Spring Cloud Netfilx Zuul,目前Netfilx已经开源了Zuul2.0,但Spring没有考虑集成,而是推出了自己开发的Spring Cloud GateWay。该项目提供了一个构建在Spring生态系统之上的API网关。
特性:
Spring Cloud DiscoveryClient
自研
另外也可以参考Zuul2、Spring Cloud Gateway等,基于Netty、Vertx或者spring4.x提供的基于Servlet 3.1的异步机制自研,但自研成本会很高,需要从零开始开发。
对比
问题
需要特别注意的一些问题:
总结
网关的异步化改造相对还是比较必要的,作为所有流量的入口,性能、稳定性是非常重要的一环,另外由于网关接入了内部所有的API,因此在大促时需要进行比较完善的压测,评估网关的容量,并进行扩容,但如果内部的业务比较复杂,网关接入了非常多的API,这种中心化的方案就会导致很难对网关进行比较准确的容量评估,后面可以考虑基于Service Mesh的思想,对网关进行去中心化改造,将网关的核心逻辑,比如鉴权、限流、协议转换、计费、监控、告警等都抽到sidecar中。
参考链接
The text was updated successfully, but these errors were encountered: