近期,随着Eureka不再维护,Hystrix不再开发新功能,进入维护状态。以及最近中国开源出现一些大事,预测一下2019年未来Spring Cloud生态圈中的第二代组件的组合,仅代表个人看法。
Spring Cloud1.X
Spring Cloud自从推出之后,给大家的感觉就是Spring Cloud做它最擅长的事,也就是高度抽象和封装,强强联手整合最优东西为我所用,比如Netflix开源的Eureka,Hystrix,Ribbon等。而且提供多种技术选型,态度中立而选最优。上个月也就是2018年11月19号左右,Netflix的开源项目Hystrix宣布状态,不再开发新功能,处于维护状态。引发朋友圈的一些思考。
虽然Eureka,Hystrix等不再继续开发或维护,但是目前来说不影响使用,不管怎么说感谢开源,向Netflix公司的开源致敬。
随着Spring Cloud生态圈的发展与成长,Spring Cloud陆续推出了自己的一些组件,挑选主要组件说明如下表所示:
Spring-cloud-openfeign
基于Feign的升级服务之间调用的必备组件
spring-cloud-zuul
来源于Netflix Zuul目前还在继续维护,但是已经有自己的Spring Cloud Gateway,不久将来逐渐淘汰
spring-cloud-eureka
集成于Netflix Eureka,目前还在跟随Spring Cloud版本升级维护,最终也会被替代
spring-cloud-config
自研,功能不太强大,国内使用其它配置中心替代,比如携程的Apollo
全链路监控
(sleuth+zikpin或pinpont),sleuth自研,国内目前使用最多的是skywaling等上生产
spring-cloud-ribbon
来源于Netflix集成,ribbon目前还在跟随Spring Cloud版本维护中,目前孵化未来替代品spring-cloud-lb
Spring-cloud-hystrix
来源于Netflix集成,目前还在跟随Spring Cloud版本维护中目前已经孵化spring-cloud-r4j
Spring Cloud1.X和Spring clould2.X的组件组合汇总,如下表所示
在这里特意着重来写写关于springcloud的网关Zuul
关于zuul1.X
Zuul1设计比较简单,代码不多也比较容易读懂,它本质上就是一个同步Servlet,采用多线程阻塞模型。
同步Servlet使用thread per connection方式处理请求。简单讲,每来一个请求,Servlet容器要为该请求分配一个线程专门负责处理这个请求,直到响应返回客户端这个线程才会被释放返回容器线程池。如果后台服务调用比较耗时,那么这个线程就会被阻塞,阻塞期间线程资源被占用,不能干其它事情。我们知道Servlet容器线程池的大小是有限制的,当前端请求量大,而后台慢服务比较多时,很容易耗尽容器线程池内的线程,造成容器无法接受新的请求,Netflix为此还专门研发了Hystrix熔断组件来解决慢服务耗尽资源问题。
优势
同步阻塞模式的编程模型比较简单,整个请求->处理->响应的流程(call flow)都是在一个线程中处理的,这样开发调试比较方便易于理解,比如出了问题跟踪调试比较方便。另外,线程局部变量(ThreadLocal)机制在同步多线程模式下可以工作,有些监控产品,例如CAT调用链依赖于ThreadLocal,在同步多线程模式下,CAT埋点比较方便,调用链关系的展示也比较直观。
缺点
我们知道线程本身需要消耗CPU和内存资源,且多线程之间切换是有开销的(所谓的上下文切换Context Switch开销),线程越多,这种上下文切换的开销就越大,同步阻塞模式一般会启动很多的线程,必然引入线程切换开销。另外,同步阻塞模式下,容器线程池的数量一般是固定的,造成对连接数有一定限制,当后台服务慢,容器线程池易被耗尽,一旦耗尽容器会拒绝新的请求,这个时候容器线程其实并不忙,只是被后台服务调用IO阻塞,但是干不了其它事情。
总体上,同步阻塞模式比较适用于计算密集型(CPU bound)应用场景。对于IO密集型场景(IO bound),同步阻塞模式会白白消耗很多线程资源,它们都在等待IO的阻塞状态,没有做实质性工作。
关于zuul2.X
Zuul2的设计相对比较复杂,它采用了Netty实现异步非阻塞编程模型。
一般异步模式的本质都是使用队列Queue(或称总线Bus),你可以简单理解为前端有一个队列专门负责处理用户请求,后端有个队列专门负责处理后台服务调用,中间有个事件环线程(Event Loop Thread),它同时监听前后两个队列上的事件,有事件就触发回调函数处理事件。这种模式下需要的线程比较少,基本上每个CPU核上只需要一个事件环处理线程,前端的连接数可以很多,连接来了只需要进队列,不需要启动线程,事件环线程由事件触发,没有多线程阻塞问题。
优势
异步非阻塞模式启动的线程很少,基本上一个CPU core上只需启一个事件环处理线程,它使用的线程资源就很少,上下文切换(Context Switch)开销也少。非阻塞模式可以接受的连接数大大增加,可以简单理解为请求来了只需要进队列,这个队列的容量可以设得很大,只要不超时,队列中的请求都会被依次处理。
缺点
异步模式让编程模型变得复杂。一方面Zuul2本身的代码要比Zuul1复杂很多,Zuul1的代码比较容易看懂,Zuul2的代码看起来就比较费劲。另一方面异步模型没有一个明确清晰的请求->处理->响应执行流程(call flow),它的流程是通过事件触发的,请求处理的流程随时可能被切换断开,内部实现要通过一些关联id机制才能把整个执行流再串联起来,这就给开发调试运维引入了很多复杂性,比如你在IDE里头调试异步请求流就非常困难。另外ThreadLocal机制在这种异步模式下就不能简单工作,因为只有一个事件环线程,不是每个请求一个线程,也就没有线程局部的概念,所以对于CAT这种依赖于ThreadLocal才能工作的监控工具,调用链埋点就不好搞(实际可以工作但需要进行特殊处理)。
总体上,异步非阻塞模式比较适用于IO密集型(IO bound)场景,这种场景下系统大部分时间在处理IO,CPU计算比较轻,少量事件环线程就能处理。
Zuul1和Zuul2的性能比较
Netflix本身对网关使用异步非阻塞模式这件事情是非常谨慎的,它们进行了严格的性能测试,Netflix给出了一个比较模糊的数据,大致Zuul2的性能比Zuul1好20%左右,这里的性能主要指每节点每秒处理的请求数。为什么说模糊呢?因为这个数据受实际测试环境,流量场景模式等众多因素影响,你很难复现这个测试数据。即便这个20%的性能提升是确实的,其实这个性能提升也并不大,和异步引入的复杂性相比,这20%的提升是否值得是个问题。
最后,本文内容来自学习//p1-tt.byteimg.com/origin/pgc-image/e48dca82bd3e4c6ab90d7d2f926def3f.jpg" style="width: 650px;">
版权声明:CosMeDna所有作品(图文、音视频)均由用户自行上传分享,仅供网友学习交流。若您的权利被侵害,请联系删除!
本文链接://www.cosmedna.com/article/597281639.html