当前位置: 首页 > 工具软件 > QuickFIX/J > 使用案例 >

QuickFix/J 源代码研究一

刘焱
2023-12-01

(〇)QuickFix/J简介
FIX是Financial Information eXchange的简称。FIX是一种专门为实时电子证券交易设计的标准消息协议。FIX协议由FIX protocol, Ltd(FPL)所有并维护。FIX协议的网址为http://www.fixprotocol.org
QuickFix/J是实现了FIX协议所有版本及其功能的开源软件,100%使用JAVA实现。
QuickFix/J的网址为http://www.quickfixj.org
QuickFix/J的源代码可以从http://sourceforge.net/projects/quickfixj/files/QuickFIX_J 下载,也可以去QuickFix/J的官方网站,进入下载页面下载源代码。
那么能用QuickFix/J做什么事情呢?关注股票的兄弟们一定留意过LevelII这个名词,他是中国股票交易新行情的简称。简单说,可以将QuickFix/J的代码改造一下,就用来接受深圳证券交易的LevelII行情数据。当然接受行情不是免费的,需要诸多的商务手续,但是本文仅仅讨论QuickFix/J的开源代码的设计和实现,并且侧重于QuickFix/J的客户端实现。服务器端留在以后的文章介绍。关于上海证券交易所的LevelII数据的格式和接受,跟深圳的有诸多的不同,也留在以后讨论。
首先QuickFixJ代码功能主要有两大部分,一部分是Fix协议数据的解析,另外一部分是客户端跟服务器端建立连接并维持回话,传输数据。第一部分将主要介绍QuickFix/J的传输部分的实现。
(一) QuickFix/J传输功能部分
QuickFix/J的连接管理和传输功能是基于MINA框架实现的。MINA是什么?MINA是Apache旗下的一个网络应用框架,能够帮助大家轻松的开发高性能、高扩展性的网络程序。它使用NIO在传输协议(比如TCP/IP,UDP/IP)之上提供了抽象的、事件驱动的、异步处理的API。MINA的网址为http://mina.apache.org。
A). QuickFix/J客户端用到的主要类的功能说明(V1.5.0)
1. quickfix.Initiator:定义了一些从配置中获取通信协议、主机、端口及重连接的时间间隔的KEY,仅仅是key而已,没有其他的。
2. quickfix.mina.SessionConnector:定义了一些helper方法,为initiator和acceptor提供公用的功能,比如获取Session,创建Session,动态添加/删除Session,判断是否已经登陆。最重要的是他定义了SessionTimerTask这个内部类。SessionTimerTask的功能有自动重连登陆、检查和更新Session时间戳、发送心跳消息。
3. quickfix.mina.initiator.AbstractSocketInitiator:是SocketInitiator的基础抽象基类,继承了SessionConnector和Initiator。在QuickFix/J中提供了两种默认的具体实现,分别是SocketInitiator和ThreadedSocketInitiator。这两种具体实现的功能都一样,两者的区别仅仅是处理消息时使用线程的策略不同,具体请参考7和8。抽象类AbstractSocketInitiator提供的功能有:
a) 遍历配置文件取得所有[session]节的配置并创建相应的FixSession (如果[session]中没有指定ConnectionType或者明确指定了ConnectionType为initiator,则建立FixSession(quickfix.Session),其他类型的ConnectionType无效,如acceptor)。配置文件中可以指定多个[session]。

b) 通过已经生成的FixSession和传入的eventHandlingStrategy创建IoSessionInitiator,并保存入initiators(Set类型的缓存)中。
那么FixSession和IoSessionInitiator有什么区别呢?请参考5、6。
c) 启动、关闭客户端(initiator)。启动initiator时首先启动应用层的SessionTimer(请参考2),然后启动连接层的initiator(IoSessionInitiator)。关闭initiator时,先关闭连接层的initiator(IoSessionInitiator),再关闭应用层的SessionTimer。
4. quickfix.SessionID:是Session的唯一标识。SessionID中包含beginString(必须),senderCompID(必须),senderSubID (可选),senderLocationID(可选),targetCompID(必须),targetSubID(可选),targetSubID(可选),targetLocationID(可选),sessionQualifier(可选)。sessionQualifer用于区分具有相同的targetCompID不同的session,只能用在initiator角色中。SessionID.toString生成的可读的Session ID字符串组成为:beginString:senderCompID/senderSubID/senderLocationID->targetCompID/targetSubID/targetLocationID/sessionQualifier。如果可选值未设置则在Session ID字符串中默认空字符串。
5. quickfix.Session:Session是FIX消息通讯中最基本的抽象。
a) fixSession维护Session内部消息的自增序列号、自动错误恢复、与通信对方(counterpart)建立通信信道(communication channel)。
b) Session是独立于特定的传输层协议的。Session被新建时,消息序列号置为1,每次通信序列号自增,直到Session被重置(reset)。每个Session能够跨越多个传输连接(并非同时跨越,而是说第一次网络连接断开后,随后重连,虽然底层的网络连接已经是新建的了,但是Session还能保持跟断网之前是同一个Session)。
c) fixSession中核心逻辑在next方法中。next(message)首先检查SessionTime,如果超过1秒未刷新则刷新时间戳;如果发现Session不存在,则执行reset,重置Session。然后取到消息的header,msgType进行检查,首先beginString不正确,抛异常并退出。然后从dataDictionaryProvider取得数据字典,验证数据字典。然后根据消息类型,分别回调用户接口。回调用户函数的入口在验证完数据字典之后,请注意verify(message)函数,所有的普通消息通过这个函数去回调application的fromApp(message, sessionID)的。verify -> veriry -> fromCallback -> fromAdmin/fromApp。关于消息的解析,其中普通Message是通过quickfix.MessageUtils.parse将String类型的消息解析成Message。
6. quickfix.mina.initiator.IoSessionInitiator:使用MINA提供的传输层的API,建立、维护同服务器之间的传输层的网络连接,而不是应用层的网络连接。这些网络功能都在一个叫做quickfix.mina.initiator.IoSessionInitiator.ConnectTask的一个私有的TimerTask中实现。具体实现功能有连接(包括普通连接和加密SSL连接)、重连、判断是否应该重连、处理连接异常、启动和停止ConnectTask。

7. quickfix.SocketInitiator:使用单独的线程去为所有的Session处理消息。SocketInitiator提供的功能有:
a) 初始化,即用eventHandlingStrategy创建Initiator,然后注册此SocketInitiator所管理的全部Session,然后启动Initiator,最后调用eventHandlingStrategy.blockInThread()在另外的后台线程中去处理SessionTimer收到的插入队列的消息。启动Initiator做的事情依次是:先启动SessionTimer去监听从传输层过来的消息,如果没有Logon则先Logon,然后在收到消息后回调用户代码处理消息;启动reconnectTask去建立和维护传输层的网络连接。
b) 启动Initiator,在另外的线程中后台(Daemon)处理消息。
c) 阻塞Initiator,在同一线程中处理消息。
d) 停止Initiator。分为强制停止和非强制停止。强制或者非强制Logout所有FixSession,停止连接层的Initiator,取消注册所有此SocketInitiator所管理的全部Session。
e) 关于a) b)如何处理来自底层的消息的逻辑,请参考11和12。因为这里所谓的处理消息实际上是直接或者间接调用了SingleThreadedEventHandlingStrategy的block处理消息。
8. quickfix.ThreadedSocketInitiator:为每一个Session使用一个单独的线程去处理消息。功能参考7。除了线程工作模式不一样,功能和7完全一样。
9. quickfix.SessionState: Session和对方通信过程中使用的helper类。主要功能就是存储了Session的所有状态,并且提供了响应API访问这些状态。状态包括heartBeatInterval,heartBeatMillis,是否需要heartBeat,判断Session所在应用程序的角色(客户端Initiator or 服务器端Acceptor),lastReceivedTime,lastSentTime,获取logger,判断作为客户端的Initiator登陆消息是否已经发出,判断作为服务器端的Acceptor登陆消息是否收到,判断是否需要登陆,判断登陆是否TimeOut,messageStore,testRequestCounter,判断是否需要TestRequest,判断是否处于TimeOut状态,将收到的Message入队(enqueue),出队(dequeue),锁定和解锁发送/接受的Sequence Number,获取、设置自增的下一个Sequence Number,重置(即将Sequence清空,重新从1开始计数),设置、获取Logout的原因。
10. quickfix.mina.EventHandlingStrategy:用于不同版本FIX协议处理事件的策略的接口,是应用级处理消息回调接口的根源。当传输层消息到达时调用此接口onMessage,可以这么理解,onMessage是EventHandlingStrategy的输入,这个输入来自底层。getSessionConnector获取和这个策略相关的SessionConnector,即获取和这个Session相关的Initiator/Acceptor去处理响应的输入消息,一般情况下是逐层将消息向上层传出,回调用户的函数处理该消息。getQueueSize获取当前被处理消息队列的长度。EventHandlingStrategy一般作为AbstractSocketInitiator的成员,建立IoSessionInitiator时传给IoSessionInitiator,请参考3 b)。目前在QuickFix/J中有两个具体实现,分别是quickfix.mina.SingleThreadedEventHandlingStrategy和quickfix.mina.ThreadPerSessionEventHandlingStrategy。这两个具体实现类的说明请参考11,12。

11. quickfix.mina.SingleThreadedEventHandlingStrategy:是QuickFix/J处理消息的核心类。处理消息时即便有多个Session也使用单线程模式。
a) 为了不阻塞输入,那么就需要一个eventQueue来临时快速的存储收到的所有消息。
b) onMessage接到底层传入的消息包装成SessionMessageEvent,首先将其存入eventQueue。
c) 那么SessionMessageEvent里面有什么?SessionEvent仅仅是把fixSession和Message包装到一起,并且提供了处理Message的方法processMessage。可以这样理解,。
d) getSessionConnector获取需要处理应用级的connector以便处理eventQueue中的消息。
e) getMessage从eventQueue中取出SessionMessageEvent待处理。
f) block就是应用程序级别处理消息的入口。block判断HandlingMessage是否应该继续运新,如果是则从消息队列中取出SessionMessageEvent,调用其中的processMessage去处理该Message。
g) processMessage如何处理了收到的消息呢?它会调用fixSession的next方法,将消息传给Session,由fixSession再接力将消息回调到用户手中。请参考5。
h) 也许你会注意到处理消息的block在run中始终被调用,而且没有任何sleep时间,难道它在没有消息的时候始终不停的死循环运行且丝毫不休息?CPU会保持100%?实际上效果不是这样的,其中的秘密在于它使用了BlockingQueue做到了和sleep相同效果的事情。在没有消息的时候,这个循环会每休息一秒再执行下一次循环。如何做到这样的效果呢?原因是如果eventQueue中如果没有消息,而该eventQueue设置了阻塞超时1000毫秒,则取消息的操作会等待最多1000毫秒,如果没有等到消息则超时退出不再等待,执行完毕本次循环,如果等到了则按照正常流程处理消息。这样做最大的好处就是,如果eventQueue中有事件,那么就会连续不断的处理,如果没有消息,就会休息timeout毫秒再查看。
i) blockInThread,在新启动的后台线程中处理SessionMessageEvent
12. quickfix.mina.ThreadPerSessionEventHandlingStrategy:同样是QuickFix/J处理消息的核心类。和单线程模式不同的是该策略会为每个session启动一个新的线程去处理消息。
a) 由于是每个Session对应一个线程,因此该策略内部需要一个称之为dispatchersMap作为缓存为每个Session保存响应的处理线程(MessageDispatchingThread)引用。
b) 当onMessage收到来自底层的输入消息时,根据输入的fixSession从dispatchers中取到相应的处理线程,并将该消息加入(enqueue)到该线程内部的消息队列中待处理。
c) 每个dispatcher(MessageDispatchingThread类型)内部均维护了自己的消息队列,和单线程模式不同在于,消息队列中的消息仅仅是Message,不是SessionMessageEvent。处理Message的逻辑从单线程中的SessionMessageEvent中移出到dispatcher中。
13. quickfix.DataDictionaryProvider

:是一个接口,为指定的session protocol或者application version提供数据字典。getSessionDataDictionary根据提供的beginString 即协议版本获取相应的数据字典。getApplicationDataDictionary根据提供的application version ID和custom application ID获取数据字典。application version ID在FIXT.1.1之前由BeginString字段确定。custom application ID是可选值,不是必须的。
14. quickfix.DefaultDataDictionaryProvider:是QuickFix/J提供的DataDictionaryProvider的默认实现。在DefaultDataDictionaryProvider中,有两种数据字典,一种是传输用的数据字典,一种是应用程序用的数据字典,分别缓存在两个Map中。这个DefaultDataDictionaryProvider是在创建Session时由默认的DefaultSessionFactory根据beginString创建的。addApplicationDictionary和addTransportDictionary分别用于向DefaultDataDictionaryProvider添加新的数据字典。目前QuickFix/J的实现中,在DefaultSessionFactory中初始化Session时添加字典。对于fixt之前版本的数据字典,每个数据字典会被同时添加进入到传输数据字典和应用程序数据字典中。
B). 网络数据在QuickFix/J中的流向
ConnectTask -> ioConnector.connect(sockAddress, ioHandler) -> MINA建立和服务器端的通信。收到网络数据,ioConnector触发相应事件,并把事件交给ioHandler(InitiatorIoHandler)的processMessage -> processMessage中调用eventHandlingStrategy.onMessage(quickfixSession, message) ,将消息向外回调 -> SingleThreadedEventHandlingStrategy.onMessage(quickfixSession, message) 将收到的消息入队(enQueue)到eventQueue -> SingleThreadedEventHandlingStrategy.blockInThread中启动单独的后台线程,依次从eventQueue取出消息处理,向session回调 -> SessionMessageEvent.quickfixSession.next(message) -> quickFixSession.next根据msgType判断回调 ->逐层回调 (verify -> veriry -> fromCallback -> fromAdmin/fromApp),从fromAdmin/fromApp(msg, sessionID)回调用户处理逻辑。

 类似资料: