蒲凤平, 陈建政
(西南交通大学牵引动力国家重点实验室, 成都 610031)
近年来,随着互联网的飞速发展,大型的分布式系统得到了前所未有的发展。“云计算”,SOA等的出现成为分布式系统发展过程中的重要里程碑[1]。在一个分布式系统中,一组独立的计算机展现给用户的是一个统一的整体,就好象是一个系统似的。通常,对用户来说,分布式系统只有一个模型或范型。在操作系统之上有一层软件中间件负责实现这个模型[2]。消息队列中间件ZeroMQ[3]在两年前推出,它具有很多方便分布式系统架构的特点,可支持任意大的应用程序。ZeroMQ不是简单的点对点交互,相反,它定义了分布式系统的全局拓扑。ZeroMQ应用程序没有锁,可并行运行。此外,它可在多个线程、内核和主机盒之间弹性伸缩。ZeroMQ现在还在不断更新中,但因它比同类产品有很多优势,逐渐被人们认识,得到很多好评。
ZeroMQ将网络异常、异步、缓冲区、多线程等都封装起来了,并且,ZeroMQ以消息为单位进行收发,这样省却了很多代码;ZeroMQ的API(Application Program Interface 应用程序接口)很少,上手容易。ZeroMQ的消息格式如图1[5]所示。
ZeroMQ以统一接口支持多种底层通信方式:线程间通信,进程间通信,跨主机通信。比如,如果要把多进程软件用跨主机的环境中,只需要将通信协议由“ipc://xxx”改为”tcp://*.*.*.*.*:****”即可,而不需要改动其余代码[6]。
图1 消息格式
ZeroMQ支持4类通讯模式:请求回应模式、发布订阅模式、管道模式以及信号模式,其中,前3种模式使用较多,信号模式使用较少,主要是用来支持传统的TCP socket模型。用这4种模式不仅可以实现传统的一对一的通讯,还能实现一对多,甚至多对多的通讯。
ZeroMQ有超过20种以上的开发语言绑定,诸如C、C++、Java、.NET、Python等,支持绝大多数的操作系统,例如Linux, Windows,OS X,如果开发的分布式系统比较复杂,常常不会只是一种语言或者一种平台,ZeroMQ跨语言、跨平台的特性就显得很重要了。
1)RabbitMQ: 采用Erlang开发的,支持完善AMQP(Advanced Message Queuing Protocol,高级消息队列协议);支持消息持久化和崩溃恢复,应用程序在重新启动之后消息不会丢失。它通过代理的模式实现分布式系统,这种模式使系统的规模伸缩性会比较差,并且会降低系统效率,因为中央节点增加了延迟也让消息的封装更多[7]。
2)ActiveMQ: 支持STOMP(流文本定向消息协,Streaming Text Orientated Message Protocol),有很长的使用历史,并且被广泛使用。容易实现很多高级的技术,但是常常是以它的性能为代价,这对消息传输来说是一个很严重的问题。
3)ZeroMQ也支持高级消息队列协议(AMQP),是很轻量级的消息队列,没有消息服务器来存储和转发消息,把侧重点放在点对点的消息传输上。ZeroMQ刚推出不久,还不是很成熟,不支持消息持久化及崩溃恢复,且稳定度较差。
由于ZeroMQ是用C/C++开发,并且ZeroMQ协议格式定义得很简单,所以它的性能远远高于其他消息队列软件。各种消息队列性能测评结果如图2[8]所示。
图2 各种消息队列性能测评结果
从这几种同类软件的比较得知,它们各有优缺点,我们应该根据自己的项目情况,选择合适的,扬长避短。本课题主要是要设计一个高效的分布式网络,对稳定性要求不是太高,无疑,ZeroMQ是最佳选择。
分布式系统由一个manager、N个客户机和N个服务器组成,N可以是任意数量。客户机和服务器都称作子系统。每个子系统可以随时加入或退出系统,并且子系统的加入或退出不影响其他子系统。在传统的通讯系统中,客户机向服务器发出的请求,以及服务器做出的回应都会经manager中转,这样,随着客户机和服务器数量的增多,manager就会成为一个瓶颈。该系统摒弃了这种传统模式,把manager的功能进行了分割,manager仅保留了目录服务的功能。例如,应用程序X开机后向manager注册,让manager知道它运行在机子Y上,应用程序Z想发送一个消息给应用程序X,那么它就向manager咨询X的位置。当manager回应应用程序X在机子Y上以后,Z可以直接与Y创建连接,并直接和Y通讯,而不用经过manager中转。不仅各个子系统能与manager通信,任何客户机与任何服务器也能相互通信。网络拓扑结构如图3所示。
图3 拓扑图
ZeroMQ有现成的动态链接库libzmq.lib,为了实现我们的分布式通信网络,需要调用动态链接库中的API函数。Libzmq.lib中仅封装了二十多个API函数。因为这些函数的某些参数的数据类型并非基本类型,并且如果直接调用这些函数,必定会有很多代码会被重复编写。为了解决这两个问题,对这些函数都重新进行了封装,
2.2.1开发平台
有人说真正的程序用C,聪明的人用Delphi。因为Delphi开发时间短,提供了许多个可供使用的组件,很方便设计人机交互界面,功能可与C相当。Delphi是采用面向对象的编程语言Object Pascal,而ZeroMQ是利用C语言开发的,为此,首先将原有的C动态链接库中的函数封装成容易看懂的形式,然后再次封装成动态链接库,最后在Delphi下调用这些函数,以实现需要的功能。
2.2.2 方案确定
1)manager对服务器的管理:manager作为目录服务器,它要管理别的服务器,包括服务器是否在线,某服务器是提供什么服务的,服务器的绑定端口等。manager会为同一类型的服务器创建一个队列,对服务器进行管理。为了不要让服务器存在饿死的情况,manager利用最近最少使用算法为客户机选择服务器。为了知道服务器是否在线,manager与服务器之间使用心跳。即,在服务器向manager注册后,在某个固定的时间段内如果manager和服务器没有收到来自对方的消息(包括心跳和连接请求等)则认为对方已下线。
对于客户端和服务器的请求(包括连接请求,心跳,服务请求等),manager使用单一套接字进行处理。因为,与使用两个套接字分别处理客户端和服务器请求相比,manager更方便管理。
2)通讯模式的选择:ZeroMQ主要提供了请求回应、发布订阅、管道和信号4种通讯模式。其中,第4种用得比较少,主要是用来支持传统的 TCP socket 模型。ZeroMQ的套接字必须配对使用,对于请求回应模式有效的套接字对有:REQ and REP、 REQ and XREP 、XREQ and REP、XREQ and XREP、XREQ and XREQ、 XREP and XREP,这4种套接都既能发送消息,又能接收消息,但是REQ和REP要求收发消息,而XREQ和XREP可以异步收发消息;发布订阅有效的套接字对是PUB and SUB,其中,PUB只能发送消息,SUB只能接收消息;管道模式的有效套接字对是PUSH and PULL,其中PUSH只能发送消息,PULL只能接收消息;信号模式的有效套接字对是PAIR and PAIR,这种模式主要用于进程内通信。
manager要能够区分连接到它的不同的子系统,才能将回应或者心跳路由过去,所以manager端的套接字必须有路由能力,只有XREP有这个功能,所以manager使用套接字XREP实现通讯。
服务器与manager之间的心跳机制本身不是同步的,所以不能使用REQ或者REP,所以方案为服务器与manager的连接选择套接字XREQ。而服务器与客户机的通讯同样不是单向和同步的,并且有可能有多个客户机连接到同一个服务器,所以,此时的服务器也要有路由功能,即,只能利用套接字XREP实现。
与manager的通信时,客户机是发送一个消息然后接收一个回应,是同步的双向通信,所以REQ,XREQ,XREP都可以,但是为了后续对消息的封装容易格式统一,选择XREQ实现。而与服务器的通讯是异步的双向通讯,所以同样选择XREQ套接字来实现。
3)消息的封装格式
由于manager使用单一的套接字处理客户机和服务器发来的所有请求,所以这些消息除了包含消息内容还必须封装进消息类型。
客户机发送给manager的消息封装格式为:
Frame1:”Client”(代表客户机)
Frame2:Service name(请求的服务类型)
Manager发送给客户机的回应消息封装格式为:
Frame1:”Client”(代表客户机)
Frame2:Service name(请求的服务类型)
Frame3:Server’s port(服务器端口号)
服务器发送给manager的注册信息:
Frame1:”Worker”(代表服务器)
Frame2:”READY”(表示服务器已准备好)
Fream3:Service name(提供的服务类型)
服务器与manager间发送的心跳:
Frame1:”Worker”(代表服务器)
Frame2:“HEARTBEAT”(心跳)
manager感知整个系统的拓扑。子系统开机后,首先连接上manager,接着发送一个消息给manager(客户机发送请求的服务类型;服务器发送自身的名称、IP地址及提供的服务类型)。manager根据消息类型判断是来自客户机还是服务器,如果来自服务器,并且控制命令是”READY”,则根据service name信息判断是否已经存在这项服务的队列,如果不存在则创建一个新的队列,如果存在则把此服务器信息添加到这个队列最后;如果控制命令是“HEATER”,则重置此服务器的心跳截止时间。若来自客户机,则查找是否存在客户机请求的服务类型对应的服务器,若存在则从服务队列中取出对列首的服务器的端口信息发送给客户机,并将该服务器放到队列末尾;否则回复服务器不存在的消息,如“sorry!”。manager同时利用心跳机制监测服务器的在线信息,如果发现服务器下线,则从服务队列中将该服务器删除。程序流程的粗略框架如图4所示。
图4 manager处理流程图
服务器向manager注册后,等待客户机连接,直接与客户机通讯,并定时向manager发送heartbeat信息。服务器的程序流程粗略框架如图5所示。
客户机向服务器请求前,首先连接到manager,向manager咨询是否有提供某项服务的服务器存在,如果manager返回服务器的端口号,则客户机连接到服务器,直接向服务器 请求数据;如果manager返回的是“sorry!”,则客户机等待一段时间再重新连接。客户机的程序流程的粗略框架如图6所示。
图6 客户机处理流程图
为了测试ZeroMQ的性能,在两台电脑之间发送数据10000000条消息,接收只用了不到0.2 μs。说以,实验证实了ZeroMQ的高效性。测试结果如图7所示。
图7 测试结果
这个系统已经用于轮轨检测的试验中。由于,检测的时间长,并且检测点多等特点,短时间采集到大量数据,采集卡的存储空间远远不够,所以,如何及时把数据转运走就是一个很关键的问题。基于ZeroMQ通讯系统的交互面向消息,并使用消息分批发送,大大提高了传输效率,从而有效地解决了数据存储难的问题。
本系统是一个基于ZeroMQ小型的分布式系统。ZeroMQ的消息传送机制大大提高了传输效率,并且它的接口函数简单容易上手。系统中使用的平台是Delphi2007,它提供了许多个可供使用的构件,方便开发人机交互界面。实践证明本系统能实现高效的数据传输。然而,本系统还存在一个缺点:如果manager出现问题,整个系统就会崩溃掉。这个问题可以根据实际情况通过增加一个备用manager来解决。ZeroMQ刚推出不久,国内外的应用还比较少,主要集中在web应用上,但因其较同类软件具有显著优势,越来越受到大家的青睐。同时,ZeroMQ还存在不支持消息持久化和崩溃恢复等问题。ZeroMQ还在不断完善中,希望这些问题在以后都能得到很好的解决。
[1]温情月朗.分布式系统介绍[EB/OL].http://qa.taobao.com/?p=3527,2009.
[2]SOSO百科.分布式系统[EB/OL].http://baike.soso.com/v5697460.htm.
[4]program_think.开源点评:ZeroMQ简介[EB/OL].http://blog.csdn.net/program_think/article/details/6687076,2011.
[5]邹永斌.Introduction to Message Oriented Middleware[EB/OL].http://wenku.baidu.com/view/3ba1a73710661ed9ad51f394.html,2011.
[6]Dccmx.史上最快消息内核—ZeroMQ[EB/OL].http://itindex.net/detail/4067-%E6%B6%88%E6%81%AF-%E5%86%85%E6%A0%B8-zeromq,2011.
[7]Julien.ActiveMQ or RabbitMQ or ZeroMQ or ActiveMQ[EB/OL].http://stackoverflow.com/questions/731233/activemq-or-rabbitmq-orzeromq-or,2009.
[8]SZSM.测试比较:消息队列软件产品大比拼