一种用于分布式处理的高性能通用网络框架的设计方法

文档序号:6515179阅读:232来源:国知局
一种用于分布式处理的高性能通用网络框架的设计方法
【专利摘要】本发明公开了一种用于分布式处理的高性能通用网络框架的设计方法,主要实现了以下功能:A.采用“多Reactor+线程池”机制有效的解决了密集型IO和计算的问题;B.良好的接口设计简化了TCP网络编程开发;C.采用智能指针以及其它多线程安全措施,对系统资源进行合理有效的管理,增加了程序的鲁棒性;D.采用第三方中间语言来描述消息格式,解决了升级与跨语言的问题。本发明利用C++语言的高效、稳定和灵活性,创建了一个通用的网络框架,极大的减少了网络应用开发,让企业可以更专注于业务的开发而不必考虑数据传输、通讯和管理等方面的技术细节。
【专利说明】一种用于分布式处理的高性能通用网络框架的设计方法
【技术领域】
[0001]本发明属于网络通讯领域,尤其涉及一种用于分布式处理的高性能通用网络框架,其为一个提升网络性能、缩短软件开发周期的网络框架。
【背景技术】
[0002]随着互联网/移动互联网的飞速发展,业界对高性能的分布式处理服务器需求越来越高,传统的服务器网络框架已经无法满足需求。另一方面,在面对纷繁复杂的业务需求时,更短的软件开发周期意味着更丰厚的回报。
[0003]传统的网络框架基于阻塞式网络编程,程序流程通常阻塞在读取数据过程上。TCP是个全双工的协议,同时支持读写操作,当一个线程/进程阻塞在读取数据上时,给这个连接发送数据就必须等待读取数据完成,这就导致了程序效率低下。
[0004]近些年来,IO多路复用技术应用十分广泛,也就是通过select/poll/epoll等多路选择器,让可一个线程可以处理多个连接,从而提高数据收发效率。同时,通过Reactor的思想,将网络部分的通用代码提取为公用的框架或库,用户只需要编写业务逻辑代码,并通过事件回调注册到框架中,就可以实现完整的网络服务。
[0005]通常情况下,网络库设计事件回调的方式是定义一个接口,包含网络事件对应的处理函数。用户的代码去继承这个接口并加以实现,然后把对象注册到网络库中,事件触发时就回调对应的函数,这是传统的C++网络库的做法。这种做法在C++中面临的一个直接问题是对象的生命周期管理,因为C++的动态绑定只能通过指针和引用来实现,你必须把基类指针传给框架,才能获得事件的回调。那么这个派生类对象何时销毁就成为了难点,它的所有权到底归谁?
[0006]其次,传统的网络库对资源的管理容易导致一系列的问题。如TCP的连接是一个短生命周期对象,但是当连接被动断开的时候,网络库不能立刻销毁对象,因为用户可能还持有它的引用准备用来收发消息,如果直接delete,有可能造成空悬指针,导致程序挂死。
[0007]最后,对于CS结构,服务端添加新功能后,不一定所有的客户端都可以马上升级并使用新功能,因此新的服务端在上线之后要保证和现有的客户端的功能和协议保持兼容,这样才能平稳升级。传统的网络库在消息格式设计中放入版本号,服务端每次收到消息都需要根据版本号做分发,如此很容易在服务端留下一堆冗余代码且容易导致混乱。另一种错误是采用c struct的格式设计消息,这样的缺点也显而易见:首先不易升级,其次是不跨语言,需要时刻维护其他语言的打包、解包代码,同样容易导致混乱。

【发明内容】

[0008]本发明利用C++语言的高效、稳定和灵活性,创建了一个通用的网络框架,极大的减少了网络应用开发,让企业可以更专注于业务的开发而不必考虑数据传输、通讯和管理等方面的技术细节。
[0009]本发明采用的技术方案为:一种用于分布式处理的高性能通用网络框架的设计方法,该方法包括如下步骤:
[0010]1.网络服务设计:
[0011]网络设计方案采用了“多Reactor+线程池”的策略,主Reactor负责accept客户端的连接,收到连接请求后,将连接分配给某个子Reactor,这样该连接的收发操作都在这个子Reactor中完成,多个连接就被分发至多个子线程中,同时,具体的运算任务也分配给了运算线程池来处理,从而充分的利用了 CPU ;
[0012]2.接口设计:
[0013]事件回调采用了 boost::function+boost::bind的做法,这种做法不必担心对象的生命周期,对用户代码的class类型、成员函数名没有限制,只对函数的参数和返回值类型有部分限制,传给网络库的都是值语义的boost: !function对象,没有指针和引用的概念,从而解决了对象生命周期和空悬指针的问题;[0014]3.资源管理:
[0015]采用了智能指针来管理资源,智能指针使用RAII的方法来对资源进行管理,RAII的基本原理是利用对象封装资源,采用对象引用计数记录对象的引用次数:对象初始创建的时候引用次数为1,在对象被使用时引用计数加1,对象释放时引用计数减1,直至引用次数为O时对资源进行释放。故而能够自动管理内存的释放,避免了空悬指针等bug的产生,同时降低了资源的维护成本,缩短了程序的开发周期;
[0016]4.消息格式设计:
[0017]采用Google Protobuf (即第三方中间语言)来描述消息格式,Protobuf是用于结构化数据串行化的方法,能够定义自己的数据结构,然后使用代码生成器生成的代码来读写这个数据结构,每个Protobuf信息是一小段逻辑记录,包含一系列的键值对,每个消息类型拥有一个或多个特定的数字字段,每个字段拥有一个名字和一个值类型,值类型可以是数字、布尔型、字符串、原始字节或者其他Protobuf类型,还允许数据结构的分级;可以运行Protobuf编译器,将定义的.proto文件编译成特定语言的类;可以在不影响向后兼容的情况下随意给数据结构增加字段,旧有的数据会忽略新的字段,所以使用Protobuf作为通信协议,可以无须担心破坏现有代码的情况下扩展协议。至此完成用于分布式处理的高性能通用网络框架的设计。
[0018]本发明主要实现了以下功能:
[0019]A.采用“多Reactor+线程池”机制有效处理密集型IO和计算的问题;
[0020]B.良好的接口设计不会给使用者带来不要的限制(稱合),用boost::function、boost::bind机制取代了继承这种强的耦合设计,从而更易于开发;
[0021]C.采用智能指针以及相关的一些多线程安全措施,对资源进行合理有效的管理,从而避免了开发过程中可能会导致的一些bug,增加了程序的鲁棒性;
[0022]D.米用Google Protobuf (第二方中间语目)来描述消息格式,然后生成不同语目的解析与打包代码,从而解决了升级与跨语言的问题。
[0023]本发明与现有技术相比的优点在于:
[0024](I)、本发明同时适合计算和IO密集型应用;
[0025](2)、本发明良好的接口设计易于开发人员开发,简化TCP网络编程;
[0026](3)、本发明有效的资源管理保证程序的稳定性;[0027](4)、本发明采用第三方中间语言描述消息格式解决程序升级和跨语言的问题。
【专利附图】

【附图说明】
[0028]图1是传统单Reactor的网络框架设计方案;
[0029]图2是本发明所采用的网络框架设计方案;
[0030]图3是本发明所采用的消息格式设计方法;
[0031]图4是本发明应用在音频质量检测系统上的部署示意图;
[0032]图5是性能、稳定性测试中音频质量检测系统的部署示意图;
[0033]图6是QCClient的处理速度随分布节点数目变化趋势图;
[0034]图7是QCServer的网络带宽随分布节点数目的变化趋势图;
[0035]图8是QCClient的CPU占用在3*24小时的监视情况的示意图;
[0036]图9是QCClient的系统句柄占用在3*24小时的监视情况不意图;
[0037]图10是QCClient的虚拟内存在3*24小时的监视情况。
【具体实施方式】
[0038]下面结合附图以及 具体实施例进一步说明本发明。
[0039]1.网络服务设计
[0040]传统的阻塞式编程方案显然已经不能满足如今的IO需求了,附图1显示的是目前使用较多的单Reactor设计方案,在没有事件触发时,IO线程等待在poll/epoll上,事件到达后由网络库处理10,完成后将结果发送(回调)给客户端代码。
[0041]这种方案的优点前面已经提到,数据收发由网络库来完成,程序只用关心具体的业务逻辑;缺点则是:适合IO密集型的应用,不适合CPU密集型的应用,难以发挥多核多线程的威力。
[0042]附图2显示的是本发明采用的网络设计方案,采用了“多Reactor+线程池”的策略,主Reactor负责accept客户端的连接,收到连接请求后,将连接分配给某个子Reactor,这样该连接的收发操作都在这个子Reactor中完成,多个连接就被分发至多个子线程中。同时,具体的运算任务也分配给了运算线程池来处理,从而充分的利用了 CPU。可以看出,这种方案适合处理密集型10和运算的应用,在现如今多核发展迅速的时代,可以极大的提升程序的性能。
[0043]2.接口设计
[0044]传统C++网络库设计中,网络库会定义一些抽象基类,使用者需要继承这些基类以获取事件的回调通知。由于C++动态绑定只能通过指针和引用来实现,使用者必须将子类对象的指针或者引用注册给网络库。在这些子类的生命周期结束后到底由谁来释放,如何保证一个地方在释放对象时其他地方没有继续引用这个对象,这些都是难点。
[0045]本发明中,事件回调采用了 boost::function+boost::bind的做法,这种方式不必担心对象的生命周期,对用户代码的class类型、成员函数名没有限制,只对函数的参数和返回值类型有部分限制,传给网络库的都是值语义的boost: !function对象,没有指针和引用的概念,从而解决了对象生命周期和空悬指针的问题。
[0046]网络编程最本质的是处理以下问题:[0047]I)连接建立。包括服务端接受(accept)新连接和客户端发起(connect)连接。
[0048]2)连接断开。包括主动断开(close或shutdown)和被动断开。
[0049]3)消息收发。包括如何处理消息分包和解包,如何设计消息缓冲等。
[0050]本发明的使用非常简单,不需要继承,只需要采用boost::function+boost::bind机制注册回调去处理前面提到的连接建立断开、消息收发问题:
[0051]服务端注册连接建立、断开回调:[0052]server」setConnectionCallback(boost::bind(&Server::onConnection, this, _D);
[0053]服务端注册消息收发回调:
[0054]server_.setMessageCal lback (boost:: bind (&Server:: onMessage, this,_l,_2));
[0055]3.资源管理
[0056]传统的资源管理方式会导致一些不可预估的错误。如在TCP连接中,当连接被动断开的时候,网络库不能立刻销毁对象,因为用户可能还持有它的引用,准备用来收发消息,如果直接delete,有可能造成空悬指针,导致程序挂死。
[0057]本发明采用了智能指针来管理资源,智能指针使用RAII的方法来对资源进行管理,RAII的基本原理是利用对象封装资源,采用对象引用计数记录对象的引用次数:对象初始创建的时候引用次数为1,在对象被使用时引用计数加1,对象释放时引用计数减1,直至引用次数为O时对资源进行释放。故而能够自动管理内存的释放,避免了空悬指针等bug的产生,同时降低了资源的维护成本,缩短了程序的开发周期。
[0058]例如,对TCP传输中的消息采用智能指针(boost:: shaed_ptr)管理,消息定义如下:boost:: shared_ptr〈google::protobuf::Message>message。程序运行中就无需考虑消息在何时释放,当消息使用完毕后会自动析构释放资源。
[0059]4.消息格式设计
[0060]本发明采用Google Protobuf (第三方中间语言)来描述消息格式。Protobuf是用于结构化数据串行化的灵活、高效、自动的方法,有如XML,不过它更小、更快、也更简单。你可以定义自己的数据结构,然后使用代码生成器生成的代码来读写这个数据结构。你甚至可以在无需重新部署程序的情况下更新数据结构。只需要在一个.proto文件中定义你需要做串行化的数据结构信息。每个Protobuf信息是一小段逻辑记录,包含一系列的键值对。
[0061]这里有个非常简单的.proto文件定义了个人信息:
[0062]message Person {
required string namc=l;
required inl32 id=2;
optional siring cmail=3;
etium PhoneType {
MOBILE=O;
HOME=I;
WOR 1(=2;

}
message PhoneNumber {
required siring number=!;
optional PhoncTypc typc=2 [dcfault=HOME];

}
repealed PhoncNumbcr phonc=4;

} [0063]可以看出,消息格式很简单,每个消息类型拥有一个或多个特定的数字字段,每个字段拥有一个名字和一个值类型。值类型可以是数字(整数或浮点)、布尔型、字符串、原始字节或者其他Protobuf类型, 还允许数据结构的分级。你可以指定可选字段,必选字段和
重复字段。
[0064]—旦定义了自己的报文格式(message),你就可以运行Protobuf编译器,将你的.proto文件编译成特定语言的类。
[0065]例如选择C++语言,运行编译如上的协议文件生成类叫做Person。随后就可以在
应用中使用这个类来串行化的读取报文信息。代码如下:
[0066]
Person person;
pcrson.sc1_namc("John");
person.sct_id( 1234);
pcrson.scl_cmail("xxx@ I 63.com");
(stream.0utput(”myl、ilc”,ios::out | ios::binary);
pcrson.SerializeToOsl_rcam(&ouiput);//消息内容序列化至文件中
[0067]然后就可以读取报文中的数据,代码如下:
[0068]
fstrcam input("myfilc'?,ios::1n | ios:binary);
Person person;
per son.Parse From I strcam(&input);// 从文件中解析消息内容[0069]

cout << ’’Name: M << person.namc() << cndl;
coul << "E-mail: ” << pcrson.cmail() << cndl;
[0070]可以在不影响向后兼容的情况下随意给数据结构增加字段,旧有的数据会忽略新的字段。所以使用Protobuf作为通信协议,可以无须担心破坏现有代码的情况下扩展协议。
[0071]综上所述,本发明采用了附图3所示的消息格式设计。注意,消息中添加了校验和字段,对于一些关键的网络应用,进一步保证了可靠性。
[0072]1.性能、稳定性评估
[0073]目前,本发明已经应用在音频质量检测系统上,该系统旨在使用自动化的方法对音频的内容质量进行检测,代替传统的人工检测的方法,提升检测的效率和效果。附图4是该系统的部署图。系统采用一台中心节点(QCServer)和多个分布节点(QCClient)。中心节点负责管理分布式节点,分发请求任务,整合处理结果。分布节点负责完成音频质量检测,并向中心节点返回处理结果,其中对音质的检测较为消耗CPU。整个系统属于典型的CPU和IO密集型系统。本发明负责QCServer和QCClient之间的IO通信管理以及QCClient的密集型CPU任务处理。
[0074]I)性能测试。
[0075]采用3个服务器(4核2G、4核4G、4核8G),部署方式如附图5所示。
[0076]下面是引擎性能数据与分布节点数目的关系表:
[0077]表1引擎性能数据与分布节点数目的关系表
【权利要求】
1.一种用于分布式处理的高性能通用网络框架的设计方法,其特征在于,该方法包括如下步骤: (1)、网络服务设计: 网络设计方案采用了“多Reactor+线程池”的策略,主Reactor负责accept客户端的连接,收到连接请求后,将连接分配给某个子Reactor,这样该连接的收发操作都在这个子Reactor中完成,多个连接就被分发至多个子线程中,同时,具体的运算任务也分配给了运算线程池来处理,从而充分的利用了 CPU ; (2)、接口设计: 事件回调采用了 boost::function+boost::bind的做法,这种做法不必担心对象的生命周期,对用户代码的class类型、成员函数名没有限制,只对函数的参数和返回值类型有部分限制,传给网络库的都是值语义的boost:: function对象,没有指针和引用的概念,从而解决了对象生命周期和空悬指针的问题; (3)、资源管理: 采用了智能指针来管理资源,智能指针使用RAII的方法来对资源进行管理,RAII的基本原理是利用对象封装资源,采用对象引用计数记录对象的引用次数:对象初始创建的时候引用次数为1,在对象被使用时引用计数加1,对象释放时引用计数减1,直至引用次数为O时对资源进行释放。故而能够自动管理内存的释放,避免了空悬指针等bug的产生,同时降低了资源的维护成本,缩短了程序的开发周期; (4)、消息格式设计: 采用Google Protobuf (即第三方`中间语言)来描述消息格式,Protobuf是用于结构化数据串行化的方法,能够定义自己的数据结构,然后使用代码生成器生成代码来读写这个数据结构,每个Protobuf信息是一小段逻辑记录,包含一系列的键值对,每个消息类型拥有一个或多个特定的数字字段,每个字段拥有一个名字和一个值类型,值类型可以是数字、布尔型、字符串、原始字节或者其他Protobuf类型,还允许数据结构的分级;可以运行Protobuf编译器,将定义的.proto文件编译成特定语言的类;可以在不影响向后兼容的情况下随意给数据结构增加字段,旧有的数据会忽略新的字段,所以使用Protobuf作为通信协议,可以无须担心破坏现有代码的情况下扩展协议。至此完成用于分布式处理的高性能通用网络框架的设计。
2.根据权利要求1所述的一种用于分布式处理的高性能通用网络框架的设计方法,其特征在于,消息中添加了校验和字段,如下表所示:


I息总长度 ^




Protobuf消息名长度




Protobuf消息名称




Protobuf序列化数据




校验和_ 对于一些关键的网络应用,进一步保证了可靠性。
【文档编号】G06F9/44GK103513990SQ201310474365
【公开日】2014年1月15日 申请日期:2013年10月11日 优先权日:2013年10月11日
【发明者】杨溥, 吴维昊, 史峰 申请人:安徽科大讯飞信息科技股份有限公司
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1