TARS为SpringCloud提供高性能的RPC能力

传统HTTP存在的瓶颈

Spring Cloud 是一个优秀的开源微服务解决方案,一般采用 HTTP + json 的 REST 接口对外提供服务,简洁易用部署方便,不少公司也基于 Spring Cloud 做为基础架构去构建自身的微服务架构。可是随着业务规模和用户规模的增加,传统基于的 HTTP 的服务会逐步暴露出一些问题。前端

首先是性能的问题,随着用户请求量的增加和业务逻辑复杂度的提高,咱们会发现微服务的单机性能会成为系统瓶颈。git

其次是稳定性问题,当一个服务节点A须要依赖于后端的几个服务的时候,咱们会发现当其中一个被依赖的服务发生卡顿,极可能会致使前端的服务节点A产生毛刺甚至没法继续提供服务,并且当问题节点没有可以被及时屏蔽或者恢复的时候,还有可能会致使整个系统雪崩。github

TARS如何为SpringCloud提供高性能解决方案

TARS 是腾讯从2008年到今天一直在使用的后台逻辑层的统一应用框架,上述问题在 TARS 框架的发展过程当中已经获得了比较好的解决。如今,TARS 经过插件集成到 Spring Cloud 体系中,但愿经过输出 TARS 的 RPC 能力针对某些对性能和稳定性要求更高应用的场景提供一种新的解决方案,而且提供了基于 Spring Boot 的开发方式,符合Spring Cloud开发者的使用习惯,能够仅使用较小的开发成本在整个Spring Cloud体系中引入TARS的RPC能力。编程

将 TARS 结合到 Spring Cloud 中使用,经过 TARS 提供的长链接异步调用和二进制协议能够明显的提高 RPC 调用性能。长链接经过链接复用减小总体的链接数量减小了资源消耗,同时经过二进制协议提高了编解码效率提高了总体的 RPC 性能。json

一. 解决SpringCloud传统HTTP网络链接使用率不高的问题后端

问题: 因为HTTP协议自己是无状态的,因此发起一次请求的时候必须等待上一个请求响应才能再次使用这个链接,就算是采用流水线模式一个链接上的请求也会被以前发起的请求所阻塞,若是要提升并发能力则必需要创建大量的链接,而链接的创建、维持和销毁都会消耗系统资源。缓存

解决: 由于HTTP协议的特性,HTTP的回包是依赖于请求的前后顺序的,必需要按照顺序处理完一个请求再处理下一个请求,若是但愿并行的处理请求则只能经过创建新的连接从而产生建连的时间开销以及维护链接须要的CPU和内存资源。网络

而TARS的协议设计是TARS的私有协议,每一个请求会带有一个请求id,经过同一个连接来发送多个请求能够经过id来匹配返回从而避免了线程阻塞,从而下降了硬件资源消耗。 输入图片说明架构

经过上图能够看到,TARS能够在同一个链接上不断的写入请求和接收响应,而客户端经过请求Id来关联每个请求和对应的响应,从而能够复用链接,避免了资源的浪费,一般状况下一个客户端和一个服务端之间仅使用数个链接就能够知足传输的要求。并发

二. 解决SpringCloud传统HTTP通信协议性能低下的问题

问题: HTTP + json 自己是一种可读性很高的文本协议,所以实际传输的数据包会比二进制协议要大很多,并且文本协议在数据的序列化反序列化效率上相比二进制数据的效率要低不少,因此 HTTP 协议自己的性能就不高。

解决: TARS的数据传输采用的是TARS协议进行编解码,TARS协议是一种二进制协议,相较于常见的JSON等文本协议,二进制协议主要有两个方面的优点:

  1. 编解码效率 二进制协议的编解码是按二进制位直接进行编解码的,减小了对不肯定的字符串解析的过程,直接从对应的二进制位读取数据,效率相比解析文本协议有很是大的提高。

  2. 网络包大小 由于全部的数据都是采用二进制存储,数据按位存储减小了对空间的浪费,使得数据序列化后能减小对空间的占用。

TARS协议采用.tars文件定义接口和数据接口,经过提供的工具能够将数据和接口定义翻译成各类语言的代码实现。

接口的共享只须要提供接口的定义文件,使用者经过定义文件直接生成客户端接口代码便可。这样减小了双方的沟通成本,避免了须要写大量的接口定义文档以及解析JSON所需的对象。

三. 解决SpringCloud传统 HTTP服务基于同步线程模型的性能问题

问题: 传统的 HTTP 服务可能是基于同步的线程模型,因为 HTTP 协议自己无状态,因此在协议层面就不支持异步,因此当咱们在客户端发起一次 HTTP 调用时主调线程必须挂起等待被调响应请求,这个时候主调线程的资源则被浪费了,由于线程资源是有限的,大量线程被挂起等待白白浪费了主调方的运算资源。

解决: 相比于使用HTTP协议的常规方案,TARS首先提供的特性就是异步长链接的RPC调用方式:

发起一个异步调用以后,当前线程并不会被阻塞而是继续执行,当收到服务端响应以后在回调线程池中经过回掉函数来执行结果的处理。这样全部的处理线程都一直处于工做的状态中,而不会挂起致使线程资源的浪费。总体上提高了服务的处理能力。

TARS的异步能力主要是经过两个部分的异步来实现的,首先是网络首发包的异步,TARS的网络层实现采用了Reactor模型,经过nio提供的事件IO实现基于事件的异步网络IO。第二是线程模型的异步,咱们从线程模型上来看TARS如何是作到异步调用的: 输入图片说明

TARS的主要经过上图的过程来完成异步调用,首先主调线程发起异步调用,主调线程将请求内容加入网络线程池的发送队列中,以后该线程继续执行。网络线程池使用Reactor模型实现,经过nio提供的Selecter实现事件IO,因此全部网络线程均是事件驱动的异步IO,当监听到对应链接的写事件后将请求发送,等待监听到读事件后读取响应并交给回调线程处理响应。这样全部的线程都避免了IO阻塞达到了更高的利用效率。

四. 解决SpringCloud服务端基于同步线程模型的稳定性问题

问题: 微服务的服务端基于同步的线程模型面临的最大的隐患就是线程的IO等待,好比说一个基于同步的线程模型的微服务,依赖后面的3个接口,微服务自己的线程数是50个,那么当后面依赖的3个接口中有一个延时飚高,只须要有50个对问题接口的调用,就足够把整个微服务的进程挂起,由于当前已经没有线程可以对外提供服务了(固然能够设置超时,可是设置超时治标不治本)。同时,因为微服务线程对问题接口的IO等待,会致使微服务的队列中堆积大量的等待时间过长(可能已经超时)的请求,当问题接口恢复后,服务端会消耗资源去处理大量的过时的请求(请求超时,客户端再也不等待)致使问题进一步恶化,严重的甚至会致使系统雪崩。

解决: TARS提供了纯异步化编程,和服务端过载保护的能力,在服务端保证收到大量的请求也可以保证服务的正常处理效率,其次由于主调方采用长链接和异步调用,避免了大量新建链接和阻塞带来的资源浪费从而提高了服务的总体稳定性。

此外,若是服务端收到过量的请求每每会致使服务端的线程竞争,让服务端的处理能力低于正常的处理水平,在TARS则经过队列来进行过载保护。咱们来看TARS的线程模型: 输入图片说明

在网络线程收到请求后,TARS会将请求先加入请求队列,工做线程从请求队列中获取请求进行处理,若是短期内大量请求到达只会被缓存到请求队列中并不会影响工做线程池的处理能力。若是工做线程池从队列中取到请求发现其已经超时则会直接丢弃请求避免处理无效的请求。

经过上面TARS的解决方案,看看实际的使用场景

场景一.同步调用,性能提高一倍标题

一般能够简单能够简单的改造服务,将原本的HTTP接口改成使用TARS,咱们对比一下在同步调用场景下TARS调用和HTTP调用的性能差别,这里规定了服务端线程数为100个线程,服务端的处理都为简单的echo服务。咱们对比一下在同一台机器上不一样RPC方式、不一样的客户端线程数以及不一样数据包大小的TPS数据:

输入图片说明

能够看出,由于采用了链接复用和二进制的协议,总体的调用效率相比使用HTTP有了很是明显的提高,并且是仅仅在简单优化了一下调用方式的状况下,对业务处理逻辑并无影响。

场景二.异步调用,避免阻塞,提高性能

假如咱们在Spring Cloud中存在这样一个调用关系,A服务须要调用B服务,而B服务须要依赖处理耗时远大因而B服务的C服务。好比在一般的业务场景中,若是API接口须要调用一个订单生成的服务,而订单生成服务只须要生成订单ID计算量相对较小,可是他还须要依赖一个订单写入服务,应为涉及到库存修改、订单写入须要一系列的事务处理,总体耗时远远大于订单ID的生成,因此订单服务大量的线程资源浪费在了等待订单写入服务上。在这种状况下可使用TARS改造订单服务和写入服务,从而使用异步调用写入服务来提高资源利用率,采用TARS提供的异步RPC能力来进行跟深度的改造:

输入图片说明

一般状况下若是使用同步调用,由于B须要等待C服务的响应,须要花上自身处理耗时的数倍来进行等待C服务返回结果,线程被阻塞浪费线程资源。这样的状况咱们能够保持A服务不变,提供REST接口,而B服务采用异步调用来进行改造。以下图所示:

输入图片说明

咱们经过简单的代码来模拟上述过程,加入C服务的逻辑时收到请求后Sleep 10s后返回结果,C服务有10个处理线程,最开始B和C之间采用同步调用,要达到最大的并发效率B服务必须也提供10个线程才可以达到最大的并发效率 TPS 为1。此时咱们采用异步调用改造B服务:

输入图片说明

此时仅需核数 + 1个线程便可达到最大的处理效率。在一般的业务使用中,若是全部IO均用异步实现,那么只使用核数+1个线程便能达到较高的处理效率,从而避免了同步IO带来的资源浪费。

对上述状况进行测试,咱们规定C服务默认采用100个线程,服务的处理过程为Sleep 10s,用以模拟一个耗时比较高的资源服务。B服务为一个依赖资源服务C的普通服务,即收到C的结果即返回,在测试中B服务分别采用同步和异步的方式调用C服务,经过调整线程数记录B服务在不一样线程数的状况下能提供的最大吞吐:

输入图片说明

由于C服务能提供的最大TPS为10,能够看出使用TARS的异步调用由于避免了阻塞,仅使用较少的线程数即可以达到对资源服务C的充分利用,从而避免了对资源的浪费。

在以上改造中,对外的HTTP接口并不须要改动,能够仅在内部须要提高RPC性能和用到异步调用的地方进行改造便可,能够平滑的按服务逐步升级。并且由于均采用Spring boot实现,只须要修改接口,其他全部业务代码仍是使用Spring注入便可。

写在最后

咱们经过插件实现了TARS对Eureka服务发现的支持,提供了Spring boot starter包和相关的注解,可以经过符合Spring Cloud开发者习惯的开发方式快速开发服务。经过对服务发现和开发方式对Spring Cloud集成可以让开发者以较小的代价快速的在整个Spring Cloud的环境中快速引入TARS的RPC能力。

欢迎访问TARS项目的Github地址:https://github.com/Tencent/Tars

试用最新的TARS on SpringCloud !

相关文章
相关标签/搜索