分别使用dubbo协议和triple协议,按照官方文档搭建Demo。
两个流程对比下来发现,dubbo协议搭建起来比较简单直接,定义好接口,实现类,然后启动provider和consumer就完事了。而triple协议则需要先定义proto文件
然后增加maven编译插件,在maven编译时生成对应的接口类Greeter,POJO对象HelloReply、HelloRequest以及一些其他的辅助类,用于后续定义接口实现时继承使用。
生成好之后,需要导出的接口的实现需要继承自生成的DubboxxxTriple类
之所以要使用生成的类,是为了完成Java与proto的互相转换。
后续的重点分析点在于
- triple协议相比于dubbo协议多了哪些特性和优势
- 使用这两种协议发起rpc请求的流程和接受到rpc请求的处理有哪些流程上的不同
目前已知:tripple协议兼容http2.0和grpc,更适用于云原生;可以跨语言、跨端跨平台进行调用;可以流式处理数据,适用于直播等大数据包场景;可以使用protobuf进行编解码;
Dubbo3 triple流式编程示例
https://developer.aliyun.com/article/1097681
流式编程的应用场景:
在一些大文件传输、直播等应用场景中,consumer 或 provider 需要跟对端进行大量数据的传输,由于这些情况下的数据量是非常大的,因此是没有办法可以在一个RPC 的数据包中进行传输,因此对于这些数据包我们需要对数据包进行分片之后,通过多次 RPC 调用进行传输,如果我们对这些已经拆分了的 RPC 数据包进行并行传输,那么到对端后相关的数据包是无序的,需要对接收到的数据进行排序拼接,相关的逻辑会非常复杂。但如果我们对拆分了的 RPC 数据包进行串行传输,那么对应的网络传输 RTT 与数据处理的时延会是非常大的。
为了解决以上的问题,并且为了大量数据的传输以流水线方式在 consumer 与provider 之间传输,因此 Streaming RPC 的模型应运而生。
通过 Triple 协议的 Streaming RPC 方式,会在 consumer 跟 provider 之间建立多条用户态的长连接,Stream。同一个 TCP 连接之上能同时存在多个 Stream,其中每条 Stream 都有 StreamId 进行标识,对于一条 Stream 上的数据包会以顺序方式读写。
我理解流式调用不再是传统的消费者和提供者的短暂的、单向通信,而是消费者和提供者之间建立的持久的、双向通信
triple多语言兼容原理:proto作为媒介,定义接口和参数。
triple兼容grpc原理:header里面包含所有grpc的header
此外,dubbo 协议在线程模型上比较丰富,而triple则比较单一。详见
https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/advanced-features-and-usage/performance/threading-model/provider/
文档中可见,dubbo对消费者的线程模型进行了一次优化,主要是降低IO线程的开销,将IO线程做的一些事情交给业务线程。
服务端的线程模型则比较多,有五种。其实我比较关心的点在于,dubbo这5种线程模型,AllDispatcher、DirectDispatcher、MessageOnlyDispatcher、ExecutionDispatcher、ConnectionOrderedDispatcher,他们有何选型上的考量。据我观察,其实完成一次rpc通信主要的消耗有channel的sent、received、connected、disconnected、caught,以及数据的编解码。这五种线程模型其实是把这些任务以不同的方式分散到IO和业务线程。如下图为All Dispatcher 。
那么,在面对不同场景时,如何对线程模型进行配置呢?这些线程模型分别适用于怎么样的场景呢?
https://zhuanlan.zhihu.com/p/157354148
这篇文章里我认为有句话说的很好,
如果处理逻辑较为简单,并且不会发起新的I/O请求,那么直接在I/O线程上处理会更快,因为这样减少了线程池调度与上下文切换的开销,毕竟线程切换还是有一定成本的。如果逻辑较为复杂,或者需要发起网络通信,比如查询数据库,则I/O线程必须派发请求到新的线程池进行处理,否则I/O线程会被阻塞,导致处理IO请求效率降低。
也就是说,如果本次请求是一次快速的操作,那么大可以在IO线程中进行,因为进行上下文切换反而会更加耗时。但如果是一次耗时的操作,那么就推荐交给业务线程来处理,防止IO线程被占满。
怎么去判断本次是不是快速地呢?我认为有两个方面,一个是是否进行了多次网络通信或者磁盘IO,另一个是 接收/响应的数据量是否过大导致编解码过于耗时。