APP下载

Dubbo的序列化协议扩展及其RPC协议Thrift的优化

2019-05-16翟成彤董海峰

智能计算机与应用 2019年2期
关键词:序列化跨平台服务提供者

翟成彤,董海峰

(西安石油大学 计算机学院,西安710065)

1 概述

Dubbo是一个专注于提供高性能和透明RPC远程服务调用的解决方案,同时也是阿里巴巴内部SOA服务治理的核心框架,每天为2000+服务提供超过30亿次调用并且常常广泛见于移动互联网+的应用中[1]。

随着技术日新月异的发展,出现了许多在序列化和跨平台服务调用方面优秀的新技术。如果能够把这些优秀的技术整合到Dubbo架构中,那么就可以提升Dubbo的序列化性能和跨平台调用的能力。

在远程调用方面,使用更高效的序列化协议Protostuff编码和解码,以提高传输数据的性能和减少系统调用响应的时间。此外,在互联网公司可能会使用不同的语言去开发软件,同时为了提高代码的复用性和异构系统之间的互相调用,开源社区团队基于Dubbo扩展了REST协议,用来在异构系统之间进行通讯。但是,该类模式却是通过(HTTP+JSON/XML)文本的方式传输数据,性能并不是很好[2]。如果使用跨语言的Thrift二进制协议,可以极大地提高在异构系统之间调用的性能。

2 背景

2.1 Protostuff and Hessian2 对比

Dubbo默认的序列化协议Hessian2相比基于Protobuf的 Protostuff协议更慢[3],因此,Protostuff协议可以被添加到Dubbo的默认RPC序列化协议栈中,用来更加高效地提升Dubbo RPC传输性能。

2.2 Dubbo中的Thrift协议和原生的Thrift协议比较

Dubbo协议栈默认有一种Thrift协议的实现,但却不是原生的Thrift协议,因为有一个协议头被添加到了Thrift协议,破坏了原生Thrift协议,使得解析协议比原生协议更为繁复与耗时。如果实践中需要考虑用到跨语言通信,最好的方式是使用原生的Thrift协议,因其具有更好的跨平台特性。故而,本文拟尝试去重构Dubbo RPC协议栈中的Thrift协议以便支持原生的Thrift协议,给框架带来更高的性能和跨平台特性。

2.3 Dubbo系统调用基本模型

Dubbo系统间调用如图1所示。由图1可知,该模型设计中的重点功能阐释详见如下。

(1)服务提供者在启动时,即在注册中心注册。

(2)服务消费者启动,则向注册中心订阅服务。

(3)注册中心为消费者返回服务提供者列表,如果提供者发生了改变,注册中心就会基于长连接再将改变的数据推送到消费者。

(4)服务消费者从提供者列表中,使用负载均衡算法选择一个提供者加以调用,如果调用失败,然后选择另外一个。

图1 Dubbo系统间调用Fig.1 Dubbo call between systems

3 扩展设计与实现

Dubbo使用微内核和插件化的结构,这样的设计表现出扩展性强的特点。Dubbo的可扩展性原则是基于 JDK 的 SPI(service provider interface)[4],SPI是通过自定义@SPI注解和扩展点加载类ExtensionLoader来实现的。

ExtensionLoader通过解析指定目录/METAINF/services/下的接口扩展文件加载被标记为@SPI接口的实现类。

因此,有2个步骤来扩展注释@SPI注解的接口。分别是:实现接口;在工程目录/META-INF/services/path下,创建一个文件名为接口全路径名的文本文件。

首先,在github上下载dubbox源代码,然后在本地idea编辑器打开由maven构建的工程结构。本节主要研究了dubbo-common模块和dubbo-rpc模块的基础。在所有扩展被实现后,整个dubbox项目编译为dubbo.jar包。对此可做探讨分述如下。

3.1 扩展protostuff序列化协议

3.1.1 Protosutff协议实现扩展

研究可知,在dubbo-common模块中进行扩展包支持的序列化,就需要建立一个包来扩展protostuff协议,在这个包中需要实现Dubbo架构的3个核心接口的序列化,即:com.alibaba.dubbo.serialize.Serialization;com.alibaba.dubbo.common.serialize.ObjectInput; com.alibaba.dubbo.common.serialize.ObjectOutput; 扩展序列化协议名称定义为 protostuff,并支持 protostuff1.0.8。 在这个包中,设有5个类,也就是:ProtostuffObjectInput、ProtostuffObjectOutput、 ProtostuffSerialization、 Serialization Util、ProtostuffFactory。 其中 ProtostuffObjectInput实现ObjectInput接口,处理输入流的反序列化;ProtostuffObjectOutput实现 ObjectOuput接口,处理输出流的序列化;ProtostuffSerialization实现Serialization接口,定义序列化名称、序列化编号,并且实现序列化和反序列化函数。而SerializationUtil,ProtostuffFactory这2个类则是设计调用 protostuff 1.0.8.jar来处理函数的序列化和反序列化。 接口和实现类的关系如图2所示。序列化数据的实现过程如图3所示。反序列化数据实现过程如图4所示。

在此基础上,项目的根目录/resours/METAINF/dubbo/com.alibaba.dubbo.common.serialize.Serialization就需要添加一行,内容表述如下:fileprotostuff= com.alibaba.dubbo.common.serialize.support.protostuff.ProtostuffSerialization

当获取到序列化接口的实现时,通过SPI机制,可以加载扩展的实现类。

图2 序列化接口和实现类关系Fig.2 Serialize interfaces and relationships of implementation class

图3 输出流数据序列化类的实现Fig.3 The implementation of output stream data serialization class

图4 输入流反序列化类的实现Fig.4 The implementation of input stream deserialization class

3.1.2 Protostuff配置和使用

Dubbo在传输层实现数据的序列化和反序列化,也就是编码和解码。根据Dubbo的数据包结构特征,可以知道,当数据流到达传输层时可以读取在服务提供者的目录下xml形式的protostuff序列化协议配置,例如:<dubbo:protocol name=”dubbo” Serialization=”protostuff” />, 然 后 就 会 加 载 序 列 化 实 现ProtostuffSerialization。当使用 ProtostuffSerialization序列化和反序列化数据时,服务提供者将会对数据进行编码和解码。

服务提供者编码数据并提供给消费者的核心处理过程如图5所示。服务的提供者从服务消费者接收到请求数据并解码的过程如图6所示。

图5 数据编码过程Fig.5 Data encoding process

图6 数据解码过程Fig.6 Data decoding process

3.2 RPC的Thrift协议扩展

3.2.1 优化 Thrift协议的实现

研究中,需要在dubbox项目目录下创建名字为dubbo-rpc-thriftx的模块,这个名字为了和以前dubbo-rpc-thrift作区分同时可以被兼容,符合设计模式开闭原则[5]。 这里使用原生 thrift0.10.0.jar作为支持,在此基础上原生协议可以使用,命名为thriftX。在新近启用的模块目录下创建新的目录com /alibaba /dubbo /rpc /protocol/thriftx,用来存储协议扩展类的实现,其中之一需要实现RPC通讯协议的核心—com.alibaba.dubbo.rpc.Protocol。 在这个 thriftx包中还需要实现 thriftXprotocol和ThriftUtils这2个类。其中,ThriftUtils是用来生产网络通讯所需对象的工程,thriftXprotocol是Protocol接口的实现,可用于暴露远程服务和引用远程服务,具体的设计细节是ThriftUtils工具类调用thrift1.10.jar包中的方法实现。

研究可得,通过实现thriftXprotocol类发布服务的过程,如图7所示。通过实现thriftXprotocol类引用服务的过程,如图8所示。

图7 ThriftX协议暴露服务过程Fig.7 ThritfX protocol exposure service process

图8 ThriftX协议引用服务的过程 Fig.8 ThriftX protocol referencing service process

接下来, 在 src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol路径下文本文件中加入 ThriftX = com.alibaba.dubbo.rpc.protocol.thriftx.ThriftXProtocol。 当获得 RPC 通信协议接口的实现时,可以通过SPI机制将其加载到扩展实现类中。

3.2.2 ThriftX 协议的配置和使用

首先,定义一个service.thrift文件,再编译生成接口文件。然后拷贝文件到服务提供和服务消费者工程。

在服务提供者中,接口的方法需要被实现并且接口需要被暴露,服务暴露的过程可分述如下。

(1)在服务启动时,spring容器根据配置的xml文件[6], 可将 <dubbo:servier protocol= ” thriftX”interface = ” com.alibaba.dubbo.demo.DemoService$Iface” />, 解析到 ServiceConfig, 而后通过ServiceConfig获取到格式如下格式的 URL:Registry://registry-addr/com.alibaba.dubbo.registry.RegistryService? Export= URL.encode( ” thriftX://service-addr/com.alibaba.dubbo.demo.DemoService$ Iface? Version=1.0.0”)。

(2)通过 URL 中的”registry://”约定协议头的识别,则将会调用RegistryProtocol类中的export()方法,接着将读取URL中的参数注册到注册中心。服务提供者的 URL 格式如:”thriftX://serviceaddr/com.alibaba.dubbo.demo.DemoService $ Iface?Version = 1.0.0”。 通过调用 URL 中的”thrift://”协议头,会调用export()方法打开服务端口。thriftX暴露服务的过程如图7所示。

进一步研究可知,客户端服务调用过程可以分为2步,对此可阐述如下。

(1)客户端服务使用dubbo的方式调用XML文件中定义的服务引用的接口,服务引用过程的研究内容如下。

① 在XML配置文件中配置<dubbo:reference id=”DemoService” interface= ”com.alibaba.dubbo.demo.DemoService $Iface”/> ,根据 Spring 约束,XML标签会被ReferenceConfig对象解析过后,通过此对象获取url。

② 通过 URL”registry://”协议头识别,服务将会调用RegistryProtocol类中的refer()方法,使得后续通过配置的参数查询提供者url,例如:thriftX://service-addr/com.alibaba.dubbo.demo.DemoService$ Iface? Version = 1.0.0.。 在 解 析 thrifx://时,thriftXProtocol中的doRefer()方法会被调用,如图8所示。

(2)客户端可能是一种没有使用dubbo的状态,更甚至不是由Java语言编写的,这就需要根据特定的语言接口文件的需要,客户端使用不同定义的接口thrift文件。通过接口文件可以得到一个Client对象,客户端对象包含stub的所有接口函数,而用户代码可以通过Client对象来调用thrift文件的接口函数,事实上,Client调用的是接口函数的本地stub接口。接口函数的stub将调用请求发送到服务提供者,同时服务提供者根据调用的函数名和函数参数调用实际的实现函数来进行特定的操作。Thrift服务提供者在指令处理流程后,会将相应函数的返回值发送到调用客户端对象。Thrift的客户端对象将函数的返回值传递给用户的调用函数以完成调用过程。

4 性能测试

4.1 测试环境

本文测试在同一台机器部署了zookeeper,也就是服务提供者和服务消费者的笔记本电脑上。这台机器的配置是,Thinkpad-E450,memory 4 G,CPU intel i5 win7 操作系统和 jdk1.7.0_07。

4.2 测试数据(单线程)

(1)定义包含通用数据类型的Stu对象,例如:

public class Stu implements Serializable{

public String name;

public Integer age;

public double account;

public Date birth;

public Boolean high;

public long height;

//get set method

(2)测试数据类型

简单的对象Stu

复杂对象(list<stu>包含100个stu对象)

1 K字符串

10 K字符串

1 M字符串(代表较大数据)

(3)测试设计。在测试不同协议的性能时,研究中并未考虑服务器处理特定服务的耗时操作,因此用于测试的服务提供者处理逻辑旨在返回客户端请求的数据。另外,单个请求的响应时间相对较小,不容易衡量。因此,这个测试使用循环调用相同的接口,然后得到平均的响应时间。

4.3 测试结果及结果分析

Dubbo不同的通信协议和序列化方案相结合的结果见表1。而对应于表1中使用测试方案的数字参见表2。表2中的结果是测试不同数据类型。所有测试结果时间单位为ms。

表1可以被切分成2部分。第一部分是前五个数字 a、b、c、d、e。 通信协议是 dubbo,但是序列化是不同的方案,与表2中的测试结果相比,可以从5种不同的序列化中分析得出利弊。另一部分是后面的3个字符f,g,h被设计为跨平台通信,通过3种协议比较表2中的测试结果,由此可以推得结论,在跨平台通信协议中是最好的。

表1 传输协议和序列化模式Tab.1 Transport protocol and serialization mode

在本文中,平均响应时间是大于线上时间的[7]。不同的测试环境会导致测试结果出现一定的偏差,但在相同的环境中,根据测试结果的相对数据变化得出相应的结论。

通过比较表 2中的 5组数据 a、b、c、d、e。 与测试结果中的默认序列化协议hessian2相比,Dubbox扩展序列化协议kyro和fst的性能未见显著提高。本次研究扩展的protostuff有突出的性能表现,针对此点,重点做了一些测试,结果都相差无几,其运行机理原因还需要重新分析。比较表2中的f,g,h可知,f,g明显具有更大的响应耗时。f组使用的是HTTP+JSON的REST协议,过程中运用了文本序列化和应用层HTTP作为传输协议。理论上讲,f组性能是最差的,和测试结果一致。g组是Dubbo内置的Thrift协议,虽然修改了原生的Thrift协议,但其结果性能表现却并不理想。通过这3组测试结果,可以看到扩展的ThriftX在跨平台通信和平均响应时间方面更好,并且同时也显示了Thrift原始生态协议的优势。

表2 不同类型数据测试结果Tab.2 Different types of data test results

5 结束语

在本文中,dubbo默认协议的分布式体系结构是可扩展的,添加了一个新的序列化协议。测试并考查了新的序列化协议protostuff及其原始序列化协议的运行性能。通过测试结果,将protostuff与其它序列化协议进行比较分析可知,其在小数据传输方面具有一定的优势。另外,对于跨平台通信优化扩展的ThriftX协议,通过与dubbox的REST协议和dubbo原始Thrift协议的比较,ThriftX性能具有明显的优势。

但是,本次研究也仍然存在一定不足,测试结果在平均响应时间上一般来说偏大,究其原因可能在于测试环境,未来也需要有针对地加以改进解决。而考虑到本次测试中硬件资源有限,并未能真正涉足有关多线程测试的研究。而且除了扩展协议的稳定性外,在其它方面也还需要进行后续设计上的优化与完善。

猜你喜欢

序列化跨平台服务提供者
跨层级网络、跨架构、跨平台的数据共享交换关键技术研究与系统建设
一款游戏怎么挣到全平台的钱?
潜力双跨平台:进阶:谁将跨入下一个“十大”?
网络服务提供者的侵权责任研究
浅析网络服务提供者因网络用户侵权的侵权责任
初中生写作序列化实践与思考
分层次序列化训练增强考场写作的增分因素
对“失序”的习作教学之思考和把脉
坚持以读促写 注重思维训练
基于C++语言的跨平台软件开发的设计