携程酒店RSocket实践

634 阅读7分钟
原文链接: mp.weixin.qq.com

作者简介

刘诚,携程酒店研发性能架构师。2014年加入携程,致力于通过架构的演进,控制企业硬件成本。

一、初识RSocket

在QCon2019北京大会上第一次得知RSocket。印象深刻的是Netifi公司通过他们研发的RSocket帮助企业实现微服务,在40,000RPS的场景下,Istio需要每月3495美金,而Netifi每月只要388美金,同时性能提升10倍,这无疑对任何企业都是极具吸引力的。

Netiffi的创始人在会上也谈到,使用Netiffi的Broker,得益于RSocket协议,无需独立部署监控、服务发现、健康检查、负载均衡等等中间件。如果是跨云部署,例如谷歌云与亚马逊云之间,或者亚马逊云与企业本地数据中心,都只要通过Netiffi的Broker即可无缝沟通,无需处理复杂的适配问题。

回来查询了不算丰富的资料后发现,Istio的技术专家发文称RSocket Broker的service mesh比Istio有将近10倍的速度提升。考虑到Istio专家的观点还有一定说服力的,那么RSocket真的有那么厉害?

二、RSocket生产实践

我们决定到生产上面去实践RSocket,看看性能到底如何。现在已经支持RSocket的service框架有Spring Flux:

 

Dubbo3.0 snapshot

压测对比的是Dubbo2.7。Dubbo2.7的样例代码如下:

Dubbo3的样例代码如下:

SpringFlux的样例代码如下:

分别压测QPS200,400和800的情况,结果显示CPU、内存和响应时间基本一致。是不是压测方式有问题,为什么性能一点也没有提高?

三、RSocket的定义

在解答上面的问题之前,我们先来看看RSocket到底是什么?

官方定义:RSocket是基于reactive stream flow control的双向的、多路的、基于消息的、二进制通讯协议。它提供了4种交互模式:

  • request - response:一个请求,一个响应。现在的Restful服务既是如此;

  • fire-and-forget:对于那些不关心结果的请求,直接返回;

  • request – stream:一个请求,多次结果返回;

  • channel:服务器可以发多个请求给客户端,客户端也可以发多个结果给服务器;

几个特点:

  • 可取消:请求和响应都可取消,能够高效的清理系统资源;

  • 可中断后继续:如果被调用方卡住了,请求方可断开后,过一会再过来重试;

  • 可租赁:响应者可根据自己的实际情况来控制调用方的频率,其实就是响应式编程中的背压的实现;

上面只是定义了RSocket协议,在具体的实现上面是非常灵活的。

进一步查询资料后发现,现有JAVA的RSocket实现一般都是基于TCP长连接。熟悉Dubbo的朋友,立刻就会想到Dubbo不就是基于TCP长连接进行服务调用的么。是的,但是不同之处在于RSocket是一系列的协议规范,原先的Dubbo虽然也是基于TCP长连接实现的,但是并没有完全按照RSocket的规范来进行实现。

更加确切的来说,那个时候应该还没有RSocket。这个也就帮助我们理解为什么Dubbo3开始接入RSocket,以及阿里为什么也是RSocket的拥护者之一。至此就能理解为什么性能没提高了,在我们实践的场景中,只是把原来基于HTTP的请求方式变成了基于TCP的实现。就生产结果而言,并没有性能大幅提升,更别提10倍的提升。

那RSocket只有TCP长连接的优势?

四、RSocket协议的业务开发优势

作为一名一线业务开发者,可能更关心的是使用RSocket协议写业务代码时的优劣势。就我个人而言,感觉还是很棒的。例如下面这个传入参数为Mono,返回也为Mono类型的接口定义方式。

熟悉响应式编程的同学应该知道Mono是Pivotal Reactor Core中的一种类型。是一种特殊的发布者,最多只发布一次。

如果应用本身就是以非堵塞的方式写的,那这里就可以直接使用reactor core的所有API。当然也有同学会说,即使不返回Mono,例如Dubbo2.7中的返回CompletableFuture,我们只要自己内部转换一下即可。然后应用里面照样可以使用reactor core或者rxjava等响应式编程的框架。

的确如此,但是如果是Flux呢?可以多次、不断地往流里面写入结果的呢?CompletableFuture还能支持么?显然不行。因为flux本身就定义为最多可以发布n次。

那为什么要用flux?

flux是响应式编程中的一种常用对象,其实就是request -> stream一种实现。一个请求发起后,结果可以分批写回。好处是什么?例如:A服务调用B服务,B服务调用C和D服务,但是D服务很慢,如果是request -> response模式,那必须要等到C和D完成后,才能返回结果给A。如果是Request -> stream模式,则可以先把C的结果返回给A,然后等D的结果拿到了,在返回给A。这样就可以高效的利用系统资源,减少等待。

熟悉Dubbo的同学可能会说,request -> stream这种模式也不是RSocket独有的吧,例如Dubbo就可以使用下面的方式来实现:

看完上面的代码,然后我们可以思考一下如何用上面提供的API去实现下面的功能。你就会发现flux的这种Reactive Functional Programming的编程方式大大降低了编程的难度与代码量,提升了代码的可读性:

但是这还不是全部的好处,下面我们来看看RSocket的另外一种使用场景。

五、RSocket的展望

响应式编程中有一个比较有名的功能叫背压。例如:当上游服务调用下游服务,而下游服务来不及处理的时候,可以选择性的限制上游服务的调用。

而我们日常在刷手机的时候,经常会由于手机卡顿,无论是APP导致的还是网络导致的,重复点击或者刷新页面的情况。而HTTP本身是无状态的,所以只要有请求,无论是有效的还是无效的,服务器都会进行处理直到完成。

但是如果有背压,那我们就可以一定程度上减少APP的无效和重复的请求。

例如:用户查看订单列表,如果一下子过来10个请求,其实只要返回最后一个即可,前面9个都可以忽略。如果实现了,服务器就会减少流量,对硬件成本的控制有着非常积极的作用。InfoQ的文章中就提到:Facebook的工程师现在就是这样实现APP与服务器之间的通讯。

之所以称之为展望,因为这个也是我们下一个实践的目标。

参考资料

   


大数据与人工智能沙龙

8月24日上海

火热报名中

↓↓↓

【推荐阅读】