基于ZeroMQ&JSON的SOA网络传输中间件研究

2021-06-16 09:35黄江张李超王森林郭强强李萌
电子技术与软件工程 2021年7期
关键词:序列化局域网队列

黄江 张李超 王森林 郭强强 李萌

(华中科技大学 湖北省武汉市 430074)

随着互联网技术的发展和业务需求的扩张,分布式系统成为现代软件架构设计的热点。传统的单体架构、垂直架构虽然结构简单,但是不易扩展,能够承接的业务复杂度有限,因此分布式系统应运而生。分布式系统由一组通过计算机网络链接、自治的、配备了分布式系统软件的计算机组合而成。近年来,基于服务的架构(SOA)、基于微服务的架构的提出更是将分布式系统推向了高潮。

分布式系统最重要的就是进程(位于同一台或多台计算机)之间的通信。进程间的通信机制主要有:

(1)使用基于同步请求/响应的通信机制,如REST 风格的HTTP、gRPC;

(2)使用异步的基于消息的通信机制,如高级消息队列协议(AMQP)、流文本定向消息协议(STOMP)。[1]

消息队列经常被用来设计分布式系统的通信系统。陈瑶基于ActiveMQ 设计实现了一个数据传输框架[2];汪然基于ActiveMQ实现了消息中间件系统[3];孟承、王建基于RabbitMQ 实现了软件化的雷达通信中间件[4];袁小军基于ZeroMQ 设计实现了分布式的3D 打印控制系统[5];蒲凤平基于ZeroMQ 设计实现了列车检测系统[6];朱民新基于ZeroMQ 实现了分布式存储系统。

以下采用异步的消息队列作为进程间通信的基础,通过二次封装,构建两套轻量级的基于SOA 的分布式系统网络传输中间件。

1 ZeroMQ&JSON

1.1 ZeroMQ

ZeroMQ 是一个基于C++ 的轻量级开源消息队列,支持AMQP、异步、多线程。同类的

开源消息队列还有RabbitMQ,基于Erlang 语言,同样支持AMQP;ActiveMQ,基于java,只支持JVM 平台,跨平台性不好。根据Nicolas Estrada 等人[2]的研究测试,ZeroMQ 在性能和可伸缩性方面都要优于RabbitMQ,在负载增加时的退化阈值要高于后者,具有更好的可扩展性和更快的速度。

最新的ZeroMQ 提供了多种传输模式:客户端-服务器模式、广播-接收模式、发布-订阅模式、管道模式、独立对模式、原生模式、请求-响应模式。其中,客户端-服务器和广播-接受模式目前还不稳定;发布-订阅模式用于一对多的传输,数据从单一的发布者发送到多个订阅者;管道模式类似于linux 传统的管道传输;独立对模式用于两端的单一链接,常用于跨线程的通信;原生模式用于底层的tcp 连接通信;请求-响应模式可用的套接字类型更多,功能更丰富,既可以通过简单的REQ 和REP 套接字实现简单的两端之间的对话,也可以通过Router 和Dealer 套接字实现负载均衡和路由转发。

在实际开发中,ZeroMQ的API虽然简洁,但是由于相对较底层,因此各大开发语言C/C++、JAVA、PHP 等都对ZeroMQ 进行了封装,且都是开源的。笔者选择支持C++开发的Czmq 开源库,该库在gitub 中使用也是最多的。

1.2 JSON

信息的序列化和反序列化也是一个关键问题。序列化(Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。序列化常用的格式有二进制格式、XML、JSON。

图1:各JSON 库综合评价[10]

图2:类的主要成员

JSON 是当下很流行的数据格式,JSON 来源于javascrpit 的对象表示方法,根据Valeriu Manuel Ionescu 对于前面提到的三种序列化格式的性能对比,当数据用JSON 编码时,最终文件大小一般小于用XML 格式编码的文件。并且,JSON 比另外两者的可读性和简洁性更好,这也是它流行的原因。因此笔者选择用JSON 作为参数数据的传输格式。

C++语言中的JSON 开源库也有很多,根据github 中作者Milo Yip[4]对众多JSON 库综合考量解析时间、占用内存、序列化时间、代码长度、优化时间的测评(如图1 所示),选择腾讯公司的开源库RappidJson。

1.3 基于C++面向对象与继承的设计

C++面向对象的编程思想,将最原始的服务端与客户抽象出来作为两个对象,服务具有多样性而请求服务具有一般性,即不同的服务将实现不同的计算处理,不同的服务请求只是变更传输的参数或文件。因此,将服务端实现为一个虚类,通过继承来实现多样化的服务,重写(overwrite)服务的计算处理函数的一个子类即是一个具体的服务。

图3:传输的主要过程

图4:类的基本成员

2 局域网内传输

2.1 对象设计

局域网内传输由于数据不经过互联网,只在几台计算机内传输,结构相对比较简单。设计两个对象:Worker(工作站)、Client(客户端)。Worker 提供复杂的运算;Client 向Worker 发送带有参数或文件的任务,Worker 接收任务并完成。一个任务可以只是一次计算,也可以带有向下一个Worker 发送的任务,也就说,任务是具有递归性的。类的主要成员如图2 所示,主要传输过程如图3 所示。

2.2 对象UUID

为了识别系统中每一个对象的身份,区分同类型不同对象,根据套接字的特点,选择对象(Worker/Client)的IP 地址和端口号为参数由哈希函数生成unsignedint 型的UUID,在跨局域网传输中尤为重要,因为Router 套接字的路由是以连接的众多套接字的identity 选项为识别依据。

2.3 局域网内Worker的发现机制

为了解决Client 如何知道局域网某种Worker 存在且可用(不忙碌)的问题,Worker 通过广播的方式向每一台计算机发送自己的服务信息,包括服务名称和自己的IP 地址。由于ZeroMQ 的底层是TCP 协议,并且内置的广播-接收模式尚不稳定,因此选择底层的udp 套接字进行广播的发送和接受。

图5:传输的主要过程

图6:简单消息结构

图7:引入中间代理后的消息结构

Client 每次发送任务前会启用udp 接收局域网内所有可用的Worker服务信息,并将可用服务信息保留在本地服务信息缓冲区中,下次再次发起任务时将优先查询本地服务缓冲区。

2.4 大型文件传输

当Worker 与Client 在同一计算上时,文件的传输就没有必要,所以每当接收端发现要接收文件时,会将JSON 字符串参数发送方的IP 与自身IP 进行比较再决定是否传输文件。

由于ZeroMQ 消息缓存限制,大型文件的传输需要进行分块。由于Req/Rep 套接字一问一答的固定顺序,且消息的传输具有可靠性,文件的分块传输不必采用滑动窗口式的拥塞控制策略,每次向发送方回复”OK”以确定文件被准确接收,再次增强了消息传输的稳定性。

分块传输的文件会暂存在外存,每当一块文件到达接收端,接收端都会将文件存储在工作路径下的固定文件夹,文件接收完成后会返回文件路径及文件名,作下一步计算处理。对象销毁后将自动清除缓存文件,也可以调用cleanBuf 清除。

表1:不同大小文件传输时间

图8:容量因子控制传输

图9:远程彩色3D 打印

2.5 异步

如果一个任务包含大型文件,Client 发送请求后将会阻塞很长一段时间。为了实现Client 异步请求,实现了一个简单FIFO 队列,队列里存放的是Task(任务)对象。Client 发送请求时,不会直接产生动作,而是将Task 放入队列,然后立即返回,再次询问时可以获悉当前任务的执行状态。后台线程将轮询FIFO 队列,存在Task时将其发送到Worker,返回后将结果放到结果缓冲区以供查询。为了查询的效率,用哈希值生产函数将Task 生成唯一的ID 值,作为键值存入哈希表。

3 跨局域网传输

3.1 对象设计

跨局域网的传输需要跨越互联网,没有公网IP 的设备无法进行直接通信,因此,额外设计一个中间代理Manager,中间代理需要具有可供连接的公网IP。Manager 拥有一个Router 套接字来实现消息的路由。由于三方消息传输中很难保证一问一答的模式,Worker/Client 将套接字改为Dealer。除了Worker 的发现机制,其它方面几乎和局域网下一摸一样。类的主要成员如图4 所示,主要传输过程如图5 所示。

3.2 传输协议设计

由于局域网下仅有Worker 和Client 的消息交互,消息结构设计比较简洁,如图5 所示。当有中间代理的出现时,消息的传递就需要Router 路由套接字的转发,传输协议就相对丰富。基本结构如图6 所示。对象类型取WORKER/CLIENT,用于区分发送消息的节点的类型;消息类型可以根据需要扩展,指代消息采取的动作;目的节点ID 为Worker/Client 的ID 号,Router 实现消息路由的基础就是根据所有连接节点的IDENTITY 选项进行身份识别,由于一个节点只能绑定一个IP 地址的一个端口,因此利用哈希函数生成每个节点唯一的身份ID;以上是消息的路由信息,JSON 字符串和二进制的文件数据data 是消息的实体部分。具体两个节点之间的具体消息结构根据目的对基本结构进行删减,最大程度减小消息的尺寸。

3.3 互联网中Worker的发现机制

互联网中Worker 的发现通过Manager 进行,Manager 管理着所有的Worker 信息。首先,Worker 向Manager 进行注册,并附带自己的服务信息;注册完毕后,Manager 需要实时地确保Worker 存在且可用,于是Worker 将每隔一段时间发送心跳包给Manager,告诉Manager“我还在”,如果超过指定时间Manager 没有收到心跳包,将删除该Worker 的所有信息,宣告其“死亡”。

Client 发起一个服务请求之后,对应的Worker 将进入忙碌状态,当Worker 计算处理完毕并将结果返回Client 或继续向流程下游的Worker 发起下一个服务请求后,将向Manager 反馈消息,回到待机状态。

3.4 互联网中的大型文件传输

由于使用中间代理Manager 对所有消息进行转发,Dealer 套接字也不遵循Req/Rep 一问一答式的固定顺序,所以在大型文件的分块传输时,可以对传输过程进行拥塞控制,增加大型文件传输的速度。根据袁小军的测试,三种传输方式:

(1)文件块连续传输,发送方直接连续发送文件块而无需收到接收方的回复。

(2)分块请求同步传输,发送发与接收方模拟Req/Rep 一问一答式的文件传输。

(3)容量因子控制传输,发送时容量因子减少,接收到回复后容量因子增大,如此达到拥塞控制的效果。

其中连续传输速度最快,但是占用内存最多,容量因子控制传输方式速度和内存占用均居中。因此采用容量因子控制传输的方式。其基本过程如图8。

3.5 互联网中Worker/Client直接连接

当Client 发现Worker 位于同一局域网时,会跨过Manager 直接与Worker 进行交互,判断是否为同一局域网的方法是尝试发送测试字符串,若在指定时间内接收到回复,则确定可以与其直连,否则二者不在同一局域网。

4 测试

4.1 测试框架设计

本文选择远程彩色3D 打印系统作为测试。远端的计算机将彩色3D 打印所需的AMF 文件发送至彩色3D 打印切片软件,切片软件切片完成后将切片数据发送到加工设备进行加工。结构及结果图如图9 所示。

其中,客户端只需要一个Client 请求服务;切片软件需要提供接收AMF 文件并切片,最后发送到加工设备,需要一个Worker 和Client;加工设备只需要提供接收切片文件并加工的Worker。

4.2 测试结果

经测试,局域网中和跨局域网模式皆可用。局域网测试环境为Intel(R) Core(TM) i3-4150 cpu @ 3.50GHz 3.50GHz 处理器、8GB RAM、x64 版本,局域网网关为千兆网。

局域网下的测试数据如表1 所示。

5 总结

本文选择ZeroMQ 消息中间件和JSON 数据格式建立了局域网内和跨局域网两套SOA 远程传输的中间件。具有如下特点:

(1)轻量化,使用者只需要使用头文件、一个.lib 和.dll 文件就可以使用简短的代码创建服务和请求服务。

(2)快速,基于目前最快的开源消息对列ZeroMQ 和最快的JSON 开源库

(3)适应性强,适应于局域网内的数据传输,构建本地的SOA 分布式系统,同时也可以跨局域网,构建云服务系统。

(4)支持大型文件传输,根据网络传输设备的状况设置合理的文件分块大小。

(5)可靠传输,可靠性源于ZeroMQ 消息传输的稳定性。

分布式系统的每个进程既可以作为服务请求端,也可以作为服务端,同时也可以同时作为服务端和服务请求端,这取决于服务的划分和任务的流程。每个进程就可以简单地持有Worker 或者Client来建立整个服务网络。

猜你喜欢
序列化局域网队列
轨道交通车-地通信无线局域网技术应用
队列里的小秘密
基于VPN的机房局域网远程控制系统
在队列里
基于802.1Q协议的虚拟局域网技术研究与实现
丰田加速驶入自动驾驶队列
局域网性能的优化
Java 反序列化漏洞研究
作文训练微格化、序列化初探
Java序列化技术的探讨