行情服务系统总体设计-转码服务器

一.整体架构


1.整体概述
转码服务器逻辑上分为3层,驱动层、数据层和服务层。
驱动层(DriverIO):管理和连接各市场二级驱动(Dll),为数据层提供驱动原始数据。

数据层(DataIO):主要提供实时数据和历史数据的转码,把驱动源数据转为行情数据,并保存在内存或文件中。

服务层(RequestIO、UserIO):为客户端(或下级服务器)提供行情数据的请求和实时数据推送。从数据层获取数据组包发送到客户端。

此外,通过一个工作线程(ProcessIO)对系统状态进行维护和管理。

2.工作状态
       转码服务器的运行与2个时间相关,行情时间与本机时间。原则是本机状态的变化一律以本机时间为标准。而行情时间只用于计算转码,与系统状态无关。系统状态如图:

      

       转码机包括正在初始化、行情中、正在收盘、盘后四个状态,状态变化由工作线程判断本机时间触发,比如上午9:00初始化、下午16:00收盘。其中初始化状态断开客户端(不断开下级Cache服务器),不提供服务。收盘状态不提供历史数据的请求。

 

 

3.整体类图


 

 

二、驱动层
1.驱动模块在转码服务器中的主要职责
驱动模块主要是连接“传输系统驱动”和“转码系统数据转换模块”的桥梁。他是将驱动整合后向转换模块提供数据。对职责初步总结如下:

能够支持同一个市场的多个驱动
能够在驱动发生问题时根据相应的规则自动切换
能够支持各个驱动之间码表不匹配问题
为转码模块提供已经解析完毕的数据
2.驱动模块启动流程


注意:

驱动模块启动时需要将所有配置的驱动DLL全部装载(LoadLibrary)
驱动模块启动时不需要调用驱动DLL的BeginWork函数,真正启动驱动DLL工作的是驱动模块线程,而不是驱动模块启动流程(主线程)
驱动模块启动时必须把其他涉及到的变量全部进行初始化
驱动模块启动时负责启动该模块的线程,其他线程失败立即返回错误
3.驱动模块的主要线程工作流程
首先,需要说明的是每一个驱动都有以下几种状态:

装载状态:该驱动仅仅装载到内存,并没有调用启动函数,但随时可以进行启动。
启动状态:该驱动已经调用驱动函数,但驱动状态还不是行情中,有可能正在初始化,有可能发生了其他错误。
正常状态:该驱动已经启动且驱动状态处于行情中,并且为当天的日期(驱动的市场日期=本机日期,另外,该项也可以通过配置决定是否判断),可以进行正常的服务。
冻结(禁用)状态:可以是自然冻结,如该驱动曾经驱动过,但是启动过程(BeginWork)出现错误,则将他冻结。也可以是人为冻结,如使用控制器操作。冻结后该驱动就不可用了,当然也可以通过控制功能重新启动。注意:冻结只是另外一个标志,不会覆盖以上的其他三种状态,否则解冻后就不知道原有的状态了。


4.设置当前驱动函数流程
在驱动主要线程中有设置当前驱动的流程,下面对这个流程描述如下:

 

5.关于推送响应函数原则
驱动DLL推送时要调用响应函数,驱动模块推送时也要调用转码模块的推送函数,他们之间关系如下图:

 

6.关于驱动与标准码表原则
因为使用的驱动有多个,且可能来源不同。甚至不同来源中商品数量、类别、类别的价格放大倍数也不相同,所以为了保障数据的统一性,必须确定标准的码表、类别、价格放大倍数等。在这里统称为标准码表。

标准码表为启动后第一次从正常驱动中获取的码表、市场信息等数据。一旦确立,就不能进行修改(除了全部重新初始化、停止驱动模块等)。

注意:转码服务程序会在每一个交易日初始化时启动驱动模块,并且在该交易日结束时停止该模块。

二、数据层
1.实时数据
1.1市场信息 MMarketInfo

1.2名称代码表 MNameTable

1.3行情快照 MBaseData

1.4明细 MTickData

1.5走势 MTrendData

2.实时数据文件
实时数据是指,转码机定时将内存中的数据快照保存到文件中,如果转码机关闭,或异常停止,可以从这些实时数据文件中恢复,使数据的丢失减到最小。实时数据文件包括:

infodata.xxx:市场信息

infodata.xxx_bak:市场信息备份文件

namedata. xxx:名称代码表

namedata. xxx _map:名称代码表映射文件

nowdata. xxx:行情快照

stock300. xxx:300参数文件

tickdata. xxx:明细文件

hisdata. xxx:走势文件

更加详细的内容,可以查看相关目录文件的文档。

3.实时数据恢复策略
4.历史数据
数据层历史数据不保留内存,只提供接口可以获取指定一段历史数据。定位历史数据不再创建索引,而是使用和缓存模块相同的算法“二分查找”来定位数据。具体算法可以查看《历史数据缓存模块设计》文档。它们算法之间的细微差别在于:通过历史文件获取数据使用文件指针定位,在获取分钟线时,调整为以天为单位获取历史数据。

5.插件
为了支持转码机的扩展性,服务层留出插件的接口,支持实时行情数据的推送,以及各种实时和历史数据的请求接口。插件所有数据的推送和获取都通过函数的方式传递内存。

6.数据请求
为了让数据层接口与协议无关,所有获取数据的接口一律返回原始内存数据的拷贝,然后由服务层把内存数据拼为请求协议的结构。数据请求统一从MDataIO获取数据,由MdataIO统一维护各个数据模块的数据存取。

7.数据推送
为了防止服务层需要回头调用数据的情况,更新内存行情快照时,同时把更新后的快照数据memcpy出来供服务层推送。

 

其中BaseData更新内存数据时,会同时把内存数据copy到buf,这样在服务层就可以直接使用内存数据。Buf之所以定义为非特定数据指针,好处是比较灵活,以后其他类型动态包也可以如此处理。只是需要在代码中作足够的注释,并且同时维护数据层和服务层的相应处理部分的代码。

为了节省磁盘IO,并不是每次推送数据都更新到realtime文件,而是由工作线程定时触发写文件。其中走势、明细数据从内存中另外开辟一小块缓冲区,用于保存未写入文件的数据。

三、服务层
服务层包括RequestIO(数据请求)、CacheIO(历史数据缓存)、UserIO(数据主动推送)。

1.RequestIO
请求模块由MRequestIO组成,MRequestIO主要包含一个注册请求接口,以及若干个协议相关的具体函数。为了尽量避免重复代码,所以每个请求协议不再单独占用一个请求函数,而是由统一的一个处理函数来处理,每个协议只提供获取指定祯数据的函数,供请求处理调用。(除了一些特殊请求需要单独处理,比如登录、历史数据请求)

这样,增加一个请求协议时,只需要增加两个数据相关的函数就可以了,避免了每个协议都需要写重复的外围代码。

2.CacheIO
具体Cache详细方案请看Cache模块详细设计文档。

3.UserIO
用户信息以分组的形式保存,每个用户组中,包含了登录用户信息,市场商品信息,并用链表把他们串联起来,除此之外下级服务器的信息单独用一个列表维护,如图:

 

每个用户组之中,MarketPushData包含整个市场的所有商品,同时每个用户有一个UserPushData数组,包含固定数量的商品推送信息(比如20个)。MarketPushData的每个商品连接出一个链表,该链表把注册了该商品的用户推送信息串起来。

当用户注册某个商品时,从UserPushData中提取出一个未使用的PushData单元,把该PushData填入该商品信息,并插入该商品链表的头部,如图:

 

当用户取消商品推送时,通过商品序号找到商品链表,把PushData从该链表剥离。

当商品实时行情快照到来时,同样通过商品序号找到商品链表,遍历该链表,通过链表上的PushData信息向客户端推送数据。

四、数据格式和接口
各市场有区别,请参照具体文档。
————————————————
版权声明:本文为CSDN博主「Hanson85」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ljemailbox/article/details/96558871

posted @ 2020-01-09 14:44  jimshi  阅读(674)  评论(0编辑  收藏  举报