证券行情系统中转码服务器的技术方案设计

一.整体架构

1.整体概述

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

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

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

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

2.工作状态

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

      

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

3.整体类图

二、驱动层

1.驱动模块在转码服务器中的主要职责

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

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

2.驱动模块启动流程

注意:

  1. 驱动模块启动时需要将所有配置的驱动DLL全部装载(LoadLibrary)
  2. 驱动模块启动时不需要调用驱动DLL的BeginWork函数,真正启动驱动DLL工作的是驱动模块线程,而不是驱动模块启动流程(主线程)
  3. 驱动模块启动时必须把其他涉及到的变量全部进行初始化
  4. 驱动模块启动时负责启动该模块的线程,其他线程失败立即返回错误

3.驱动模块的主要线程工作流程

首先,需要说明的是每一个驱动都有以下几种状态:

  1. 装载状态:该驱动仅仅装载到内存,并没有调用启动函数,但随时可以进行启动。
  2. 启动状态:该驱动已经调用驱动函数,但驱动状态还不是行情中,有可能正在初始化,有可能发生了其他错误。
  3. 正常状态:该驱动已经启动且驱动状态处于行情中,并且为当天的日期(驱动的市场日期=本机日期,另外,该项也可以通过配置决定是否判断),可以进行正常的服务。
  4. 冻结(禁用)状态:可以是自然冻结,如该驱动曾经驱动过,但是启动过程(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信息向客户端推送数据。

四、数据格式和接口

各市场有区别,请参照具体文档。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值