颜光伟,刘 扬,陈勃翰
(中国电子科技集团公司第三十研究所,四川 成都 610041)
随着网络技术的飞速发展和广泛应用,不管是面向企业用户还是个人消费者,网络功能都是应用软件不可或缺的基本功能。目前应用软件的网络功能开发具有过程复杂、周期漫长、通信协议不统一和复用程度低等缺点,改善并简化网络通信开发过程是当前应用软件研发的重要研究方向[1]。互联网上的开源网络库也比较多,比如Mudoo、Boost.Asio、ACE、LibEvent等。这些开源库都实现了网络通信功能封装,但是无法与应用业务实现按需关联,且必须二次开发才能进一步满足应用软件的具体业务需求。因此,研究一种通用的高性能网络框架对于简化应用软件的网络开发工作,提高软件开发成果的复用性具有重要作用。本文基于异步网络通信机制,提出一种通用的高性能网络框架实现方案,能够满足应用软件的网络通信和业务处理需求,使研发人员更加专注于业务层面的用户需求。
Boost是一个开源的跨平台C++程序库。它功能组件众多,很多组件已被收录进C++标准,所以也被称为C++“准”标准库[2]。Boost库提供了常用的网络通信、并发编程、日期与时间、字符串、正则表达式等模块,弥补了C++语言在许多方面的不足,极大增强了C++语言的活力和生命力。Asio是Boost库中非常出名的用于处理网络通信的轻量级类库,支持高并发输入/输出(Input/Output,I/O)处理和多种网络协议,内部集成了网络编程常用的操作接口,并支持同步和异步调用方式,相关接口以类的形式进行封装并对外提供调用[3]。由于综合表现出色,Boost.Asio库正逐渐成为C++网络库的工业化标准,被越来越多的网络应用作为基础框架。
Boost.Asio库的异步网络通信采用Proactor模式。在Proactor模式中,任意网络操作(accept、connect、send、receive等)都是调用的异步I/O,在调用相关操作接口时只是向操作系统进行了操作注册,由操作系统进行实际的操作处理,待操作系统完成操作后再通过回调主动通知,实现后续的业务流程[4]。
本文设计的网络框架采用层次化、模块化思路,使用C++编程语言进行开发,基于Boost.Asio库实现高性能异步网络通信,通过XML实现框架内外的关联配置,适用于传输控制协议(Transmission Control Protocol,TCP)、用户数据报协议(User Datagram Protocol,UDP)、Internet控制报文协议(Internet Control Message Protocol,ICMP)、原始套接字(Raw Socket,RAW)网络通信模式,可以满足软件轻量级、高并发的网络通信需求,具有跨平台、高性能、通用性、伸缩性、可维性和扩展性等特点。
框架依据层次化设计思路,从下到上依次分为共享操作层、服务调度层、业务逻辑层和业务接口层4个层级(见图1)。共享操作层包含与业务逻辑无关的可复用基础操作集合,这些常用操作在优秀开源库的基础上进行二次封装,实现已有成果的重用,避免重复造轮子[5];服务调度层是整个框架的核心,以Boost.Asio库的异步网络通信为基础,进行框架内外消息数据的通信调度,实现业务逻辑与协议数据的“透明”耦合;业务逻辑层包含具体业务操作集合,依据模块化思路进行业务逻辑独立封装,业务之间通过统一接口进行关联操作;业务接口层实现框架内外通信接口,内部接口以接口函数方式基于统一格式进行封装,外部接口以网络服务方式对外提供。
服务调度层由异步I/O、消息调度子层、网络协议子层和业务协议子层组成,通过接口进行通信。异步I/O基于Boost.Asio库实现大规模网络连接高效率并发处理;消息调度子层将网络连接抽象为网络会话进行全生命期的异步跟踪,对异步I/O的网络操作通知及时分配工作线程池中的空闲线程进行处理,将任意业务逻辑与网络操作行为(accept、connect、send、receive、close)的关系通过状态机模型建立异步关联,实现任意复杂业务的操作流水化,并通过XML配置实现消息调度、网络协议、业务协议和业务逻辑之间的按需关联;网络协议子层实现TCP、UDP等通用网络协议的异步调用接口;业务协议子层实现业务数据格式解析和封装,使业务逻辑不因业务协议的变化而修改,达到业务协议对业务逻辑透明。
服务调度层的异步I/O负责具体的网络操作,网络协议子层负责网络数据(包含业务数据和管理数据)收发,并在对应的网络操作完成后回调通知消息调度子层,由消息调度子层依据网络操作行为进行协议数据解析验证,依据解析验证结果调用业务逻辑层的具体业务模块进行业务逻辑处理。
业务过程如果需要进行业务数据收发,则通过业务协议子层进行数据收发通知,并通过网络协议子层完成网络操作。在发送数据时,通过业务协议子层完成业务协议数据封装,实现业务逻辑与协议数据格式的“透明”耦合,业务逻辑不用关心具体的业务协议数据格式,进入业务逻辑都是已经剥离相关业务协议格式的原始业务数据,实现相同业务逻辑在不同项目的重复使用。框架内部关联关系详见图2。
业务协议子层采用C++多态机制,完成业务协议数据网络收发通知、格式封装和解析验证,实现业务协议格式与业务逻辑的彼此独立和按需组合。
框架支持定长和不定长两种业务协议数据。定长业务协议数据区分协议头和协议体两部分,网络接收数据时先收协议头,然后进行协议头解析并获取协议体长度,接着继续接收协议体。待对应的协议头和协议体数据接收完成后,再进行业务数据的完整解析验证,并在完成验证后将数据送入业务模块处理。对于没有区分固定协议头和协议体的不定长数据,按照“应收尽收”原则,尽可能接收足够数据。网络发送数据前对待发送数据按照业务协议格式进行封装,然后再进行发送。
业务协议子层模块部分接口设计如下:
// 接收固定协议头
virtual int async_recv_head(IN CSession*session,IN u_int timeout) = 0;
// 接收固定协议体
virtual int async_recv_body(IN CSession*session,IN u_int timeout) = 0;
// 接收不固定协议数据
virtual int async_recv_some(IN CSession*session,IN u_int minlen,IN u_int timeout) = 0;
// 发送协议数据(协议头+协议体)
virtual int async_send_buf(IN CSession *session,IN u_int timeout) = 0;
// 解析验证接收到的协议头
virtual int unpackaging_head(IN CSession *session)= 0;
// 解析验证接收到的协议体
virtual int unpackaging_body(IN CSession *session)= 0;
// 封装协议头
virtual int packaging_head(IN CSession *session) = 0;
// 封装协议体
virtual int packaging_body(IN CSession *session,IN const void *value = NULL,IN u_int value_size = 0,IN u_int value_len = 0) = 0;
为了确保数据在业务生命周期内的完整性、有效性和唯一性,以及业务处理过程中对数据访问的便捷性和及时性,框架将网络连接及关联数据抽象为网络会话(自定义类CSession),实现网络连接生命期内统一管理。网络会话涉及业务过程中发生的完整关联数据集合,包含网络套接字、数据库连接、业务模块以及业务数据等,如图3所示。
网络会话的生命周期与网络连接保持一致,随着网络连接的建立而创建,网络连接的关闭而销毁。但是在大规模网络连接并发访问场景中,为避免频繁进行资源分配、销毁而影响业务性能,在关闭网络连接时并不进行网络会话的销毁,而只是进行网络套接字的关闭和关联数据的复位,并在新的网络连接建立后再次关联使用。数据库连接通过配置实现数据库类型及其他数据库访问参数的读取,基于“一线程一实例”原则进行创建,运行多少个工作线程就建立相应数量的数据库连接,并在工作线程进行业务处理前与网络会话进行绑定,实现业务过程中基于网络会话的数据库访问操作,又避免多线程并发业务导致的数据库访问冲突。业务协议子层在完成业务协议数据解析后,能够获取业务标识,并依据业务标识在首次业务处理前进行业务模块的创建,并在网络会话关闭时进行业务模块的销毁。业务数据包含独立的网络发送和接收缓冲区,以便实现网络全双工通信,在业务过程中可以依据需要进行收发缓冲区的交换而避免数据拷贝,实现业务数据快速发送。
在业务过程中,业务A创建了业务B,此时称业务A为父业务,业务B为子业务。父业务创建子业务时,通过将父业务的网络会话传递给子业务,此时父业务和子业务都可以获得对方的网络会话,实现相同网络会话在多个关联业务间流转,满足业务关联数据的快速共享访问,提高业务消息传递效率。
框架的所有模块依据“一业务、一模块”原则,使用C++类进行业务逻辑封装,并生成独立动态库。业务间通过接口进行耦合,特定业务流程或者逻辑修改不影响其他业务,同时采用XML配置决定上线运行的模块以及模块间的业务关联关系。框架通过网络服务对外提供接口,并使用XML配置网络服务的相关参数,实现网络服务与业务的关联绑定。如图4所示。
网络服务的配置涉及网络参数、网络协议模块、业务协议模块、消息调度模块、数据库模块和业务模块集合等。网络参数包含服务地址和端口、连接超时值、收发数据超时值、收发数据缓冲区大小等;网络协议模块确定网络服务所采用的通用基础网络协议,如TCP、UDP、ICMP、原始套接字等;业务协议模块与具体项目相关,实现具体业务逻辑关联的应用层数据格式;消息调度模块决定采用哪一种消息调度器,实现消息数据的调度处理;数据库模块包含项目采用的数据库类型(如MySQL)、数据库访问参数(如登录账户信息等)和访问数据库的接口(如unixODBC接口);业务模块通过业务标识唯一确认具体业务,只有配置在该网络服务中的业务模块才允许被触发调用。
框架部分XML配置如下:
该框架在2G的可用内存中实现了20万网络连接并发收发数据测试,对接了达梦、金仓、MySQL等国产化和非国产化的多种类型数据库,并已经在实际多个项目进行应用,涉及软硬件结合项目和纯软件项目,涵盖x86、PowerPC、龙芯、兆芯、飞腾、Windows、Linux、中科方德、中标麒麟等众多软硬件组合的非国产化和国产化环境。各个项目重用了除部分业务协议模块、业务模块外的框架其他成果,并依据实际项目情况调整了部分框架参数,同时在此基础上进行扩展开发,实现了工作成果的复用。
本文针对当前应用软件研发过程中出现的网络功能开发复杂、开发成本大、网络框架无法通用等问题研究了一种通用的高性能网络框架,并基于C++编程语言进行了实现,使用第三方软件对实现结果进行了通信效果测试,验证了框架的应用效果。同时,该框架已经在多个项目进行复用,满足了不同项目的业务需求,降低了项目的研发成本。