QuickFix/J 原始碼研究(一)
liyayawodeai@163.com
(〇)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,判斷是否已經登陸。在SessionConnector中定義了
SessionTimerTask,這個Timer的主要目的是例行檢查和更新Session的狀態,發現有問題及時操作。
SessionTimerTask發現登陸狀態錯誤之後能自動重連登陸、更新Session時間戳記、發送心跳訊息。檢查的具體邏輯在Session.next()中,細節請參考5。
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產生的可讀的SessionID字串組成為:beginString:senderCompID/senderSubID/senderLocationID->targetCompID/targetSubID/targetLocationID/sessionQualifier。如果可選值未設定則在Session ID字串中預設Null 字元串。
5. quickfix.Session:Session是FIX訊息通訊中最基本的抽象。
a) fixSession維護Session內部訊息的自增序號、自動錯誤恢複、與通訊對方(counterpart)建立通訊通道(communication channel)。
b) Session是獨立於特定的傳輸層協議的。Session被
建立時,訊息序號置為1,每次通訊序號自增,直到Session被重設(reset)。每個Session能夠跨越多個傳輸串連(並非同時跨越,而是說第一次網路連接斷開後,隨後重連,雖然底層的網路連接已經是建立的了,但是Session還能保持跟斷網之前是同一個Session)。
c) fixSession中核心邏輯在next()方法中。
quickfix.Session.next():主要檢查了以下8個狀態,並且根據不同的狀態做了響應的操作。next()在SessionTimerTask中被定時執行,作為例行檢查Session狀態的任務。
1) 檢查Session是否是enabled。enabled狀態表示什麼。enable是Session中記錄當前是否處於登陸狀態。當執行了logon時將enabled置為true,當logout時將enabled置為false。
如果不是enabled狀態並且不是loggedOn狀態,則說明正常退出登陸。結束此次檢查並返回。
如果不是enabled狀態,但是是loggedOn狀態說明Session上次非正常logout或者logout訊息已經發出,正在返回的途中,那麼就檢查是否isLogoutSent,如果還沒有發送logout,那麼現在就發送logout訊息。
2)檢查完Session的enabled狀態之後,檢查checkSessionTime。沒有必要接收到每條訊息都去檢查Session的時間戳記,頂多1秒檢查一次就可以了,否則容易影響效能。如果checkSessionTime失敗,則reset Session。( reset時使用了java.util.concurrent.automatic. AutomicBoolean 作為鎖,使得reset成為一個原子操作)reset做了什麼事情。首先回調使用者介面onBeforeSessionReset,然後產生logout訊息,並在state的中記錄logoutSent的狀態為true,然後關閉I/O串連,關閉I/O串連之後,設定一系列state的狀態。最後resetState(將messageStore重設)。
3) 檢查hasResponder,如果沒有responder,則返回。關於responder的解釋,請參考15。
4)檢查isLogonReceived,只有receivedLogon才算真正處於登陸狀態。如果沒有收到logon響應,檢查state看是否需要發送logon訊息(對Initiator而言,如果還未發logon則需要發送logon訊息)如果需要發送logon訊息,檢查此時是否可以發送logon訊息(為了防止不斷重複發送logon,對發送logon訊息有頻率限制),如果可以發送則回調使用者介面canLogon,判斷使用者是否允許發送logon訊息,如果不允許則直接返回;如果允許則generateLogon並發送。發送完畢logon訊息之後,返回。
5)檢查getHeartBeatInterval是否為0。看到這裡,發現了一個QuickFix/J的bug,搜尋了全部代碼,沒有發現作為用戶端的Initiator在任何地方設定了state的heartBeatInterval。而作為伺服器端的Acceptor在quickfix.mina.acceptor.AcceptorIoHandler.processMessage中設定了state的heartBeatInterval,代碼如下: Java代碼 qfSession.setHeartBeatInterval(heartbeatInterval);
qfSession.setHeartBeatInterval(heartbeatInterval);
因此在寫客戶化的Initiator時,記得fix這個bug,在用戶端收到了來自伺服器端Logon訊息響應之後,從中取出HeartBtInt欄位的值,設定到fixSession的heartBeatInterval中去。設定可以參考上述代碼。
6) 檢查state isLogoutTimedOut(logout訊息已經發出去,並且Session已經逾時,說明用戶端確實已經收到了伺服器端發送的Logon訊息,並且用戶端已經logout了),則斷開I/O串連。
7) 檢查state isWithinHeartBeat。目前發現QuickFix/J並沒有設定state.withinHeartBeat,始終使用的是預設初始值false,不知道為何。既然這個檢查始終返回false,則不會返回,繼續下一步檢查。
8) 檢查stateisTimedOut(如果距離上次收到HeartBeat已經超過了2.4*HeartBeat秒,則認為已經TimeOut)。如果已經timeOut,沒有取消HeartBeatCheck的話,則斷開I/O串連,然後回調使用者的onHeartBeatTimeout介面。如果仍然未timeout,則根據需要發送TestRequest或者Heartbeat訊息。
STEP1.0.0中的Logon訊息響應中,還給出了MaxFailInt,即允許心跳最大失敗次數,超過此次數之後便認為對方已經逾時,可以中斷連線,準備重新登陸。為Session設定MaxFailInt可以將取到的MaxFailInt乘以HeartBtInt乘以1000換算成秒,然後為Session添加API,使得能夠對state的isTimedOut屬性進行操作設定,然後在在此判斷timeout。
首先為quickfix.SessionState添加一個屬性
Java代碼 private long timeoutMs = 24000L;
private long timeoutMs = 24000L;
然後添加一個setTimeout一個getTimeout,然後為Session添加響應的setter和getter。最後在收到了Logon訊息的響應時調用Session的setTimeout設定逾時時間。
quickfix.Session.next(message):
首先檢查SessionTime,如果超過1秒未重新整理則重新整理時間戳記;如果發現Session不存在,則執行reset,重設Session,並返回。
然後檢查訊息的header中的beginString,如果和SessionID中記錄的beginString不匹配則拋異常,返回。
然後檢查msgType,如果是Logon訊息,根據版本的不同,設定Session中的targetDefaultApplVerID。targetDefaultApplVerID有什麼用處呢。因為FIXT協議和之前版本的FIX協議的ApplVerID有不同的組成格式,這個targetDefaultApplVerID主要是給quickfix.MessageCracker用來解析FIXT訊息的。
然後檢查dataDictionaryProvider, 根據customApplVerID和applVerID取到資料字典(applicationDataDictionary及sessionDataDictionary,細節請參考16)。
然後根據取到的字典驗證message是否合法,不合法的根據情況列印警告日誌或者直接拋出異常退出。
然後根據訊息類型,分別做相應的狀態設定(通過nextLogon,nextHeartBeat,nextTestRequest,nextSequenceReset,nextLogout),然後調用使用者call back API(在verify中)。
處理完畢該訊息之後,調用nextQueued()遞迴處理下一個隊列中的訊息。
請注意verify(message)函數,所有的普通訊息通過這個函數去回調application的fromApp(message, sessionID)的。
verify -> veriry -> fromCallback -> fromAdmin/fromApp。關於訊息的解析,其中普通Message是通過quickfix.MessageUtils.parse將String類型的訊息解析成Message。
next(message)被SingleThreadedEventHandlingStrategy或者ThreadPerSessionEventHandlingStrategy處理每一條收到的訊息時調用,是處理收到訊息的源頭。
quickfix.Session.next(string):就是next(message)的重載函數,將輸入的字串解析成Message,然後調用next(message)處理。
quickfix.Session.verify(msg, checkTooHigh, checkTooLow):收到的訊息的MsgSeqNum與預期的不一樣時所做的狀態檢查和操作。操作比如,如果發現丟包,則自動重發ResendRequest請求或者僅僅記錄警示日誌;如果發現重複MsgSeqNum,如果訊息中設定了可能重複標誌,則正常返回,否則說明訊息有誤,則Logout,disconnect。
quickfix.Session.verify(msg):verify(msg, checkTooHigh,checkTooLow)的重載函數。根據是否是Admin類型的訊息及從配置中讀取的checkGapFieldOnAdminMessage確定checkTooHigh和checkTooLow這兩個標誌。如果不是Admin訊息則checkTooXX,如果checkGapFieldOnAdminMessage為真則checkTooXX。
quickfix.Session.doTargetTooHigh(message):收到的訊息MsgSeqNum比預期的大,即丟失了訊息,先檢查是否需要reset或者disconnect(至於是否需要reset或者disconnect由建立Session時通過建構函式的參數指定,預設均是不需要,也可以通過指定配置ResetOnError和DisconnectOnError),如果需要則reset或者disconnect後返回。 如果不需要,則將這條訊息入隊(enqueue)存入state中,然後產生ResendRequest並發送出去,請求從expectedTargetNum開始到此訊息前的所有訊息。由此可見, 在state的messageQueue中儲存的訊息都是因為發現訊息序號跳躍缺失訊息後,臨時儲存的跳躍之後新訊息。而這些臨時儲存在state中的訊息,會在收到並處理完每種訊息之後都會去檢查並處理,比如next(msg),nextHeartBeat(msg),nextLogon(msg),nextReject(msg),nextTestRequest(msg)這些處理過程的最後都會檢查並處理state臨時儲存的訊息。
quickfix.Session.doTargetTooLow(message):收到的訊息MsgSeqNum比預期的小,即收到了重複訊息,應該做的事情。
quickfix.Session.parseMessage(stringData):將輸入的字串資料解析成Message。實際是調用quickfix.parse(session, stringData)解析的。
quickfix.Session.send(String messageString): 真正通過底層Mina發送訊息給對方的API。在QuickFix/J中,send(msgString)通過responder發送訊息,responder最終調用了Mina的IoSession.write(object)將資料寫入網路中。
responder是通過quickfix.Session.setResponder(Responderresponder)在quickfix.mina.initiator.InitiatorIoHandler中建立Session時設定的。關於responder的細節請參考15。
quickfix.Session.sendRaw(message, int num):
Session內部的helper方法,首先將傳入的訊息和該訊息的MsgSeqNum拼裝好,把訊息頭中與系統相關的參數設定好,參數比如BeginString,SenderCompID,TargetCompID,SendingTime。
然後根據訊息類型回調響應的使用者call backAPI,如果是Admin類型的訊息,回調使用者的toAdmin,非Admin類型的訊息,回調使用者的toApp。如果是LOGON訊息,還要對是否重設SeqNum做相應的處理(具體處理過程是:如果訊息中有ResetSeqNumFlag這個欄位,並且這個欄位被設定true則重設。如果重設,則首先resetState,取到期望的seqNum設定到訊息頭的MsgSeqNum欄位中。無論是否重設,都將resetSeqNumFlag寫入state中)。
最後把拼裝好的message調用toString轉換成字串,然後調用send(string)方法真正的將訊息發送到網路上去。
整個處理過程需要鎖定SenderMsgSeqNum,直到全部操作完成返回。因為如果call back調用失敗,則roll back會更高效。
sendRaw被Session中generateXXX調用,比如generateHeartbeat,generateLogon,generateLogout,generateReject,generateBusinessReject,generateResendRequest,generateSequenceReset,generateTestRequest。這些generateXXX中產生了相應訊息之後,最後調用sendRaw把訊息通過網路發送出去。
quickfix.Session.send(Message message):做了兩件事,首先去掉訊息頭中的PossDupFlag和OrigSendingTime,然後調用sendRaw把訊息發送到網路上去(設定seqNum為0)。主意send(message)返回的boolean並不能說明訊息是否成功發送出去,僅僅是說明訊息已經成功放入發送隊列了,因為預設情況下QuickFix/J使用非同步IO發送網路資料。
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),鎖定和解鎖發送/接受的SequenceNumber,擷取、設定自增的下一個SequenceNumber,重設(即將Sequence清空,重新從1開始計數),設定、擷取Logout的原因。
10. quickfix.mina.EventHandlingStrategy:用於不同版本FIX協議處理事件的策略的介面,是應用級處理訊息回調介面的根源。當傳輸層訊息到達時調用此介面onMessage,可以這麼理解,onMessage是EventHandlingStrategy的輸入,這個輸入來自底層。getSessionConnector擷取和這個策略相關的SessionConnector,即擷取和這個Session相關的Initiator/Acceptor去處理響應的輸入訊息,一般情況下是逐層將訊息向上層傳出,回調使用者的函數處理該訊息。getQueueSize擷取當前被處理訊息佇列的長度。EventHandlingStrategy一般作為AbstractSocketInitiator的成員,建立IoSessionInitiator時傳給IoSessionInitiator,請參考3b)。目前在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。可以這樣理解,
onMessage是事件處理策略底層Message的輸入,SessionEvent中的processMessage是Message的輸出,未來被應用程式層級的函數處理。
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:是一個介面,為指定的sessionprotocol或者applicationversion提供資料字典。getSessionDataDictionary根據提供的beginString即協議版本擷取相應的資料字典。getApplicationDataDictionary根據提供的application versionID和custom application ID擷取資料字典。application versionID在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之前版本的資料字典,每個資料字典會被同時添加進入到傳輸資料字典和應用程式資料字典中。
15. quickfix.Responder:這是個介面,供Session使用,用於發送原始FIX訊息(raw FIXmessage)或者切斷和對方的I/O串連。send(stringData)發送原始的FIX訊息,disconnect()切斷底層的串連,getRemoteIPAddress()提供對方的IP。目前的QuickFix/J中quickfix.mina.IoSessionResponder實現了Responder介面。IoSessionResponder.send(string)調用了Mina的IoSession.write(object)將資料寫入網路中。預設是非同步寫入。(如果同步寫入在建立responder時顯示傳入synchronousWrites為true。同步寫入實際是write(object)之後,取到ioSession的WriteFuture,然後向WriteFuture發送join指令。)
16. quickfix.DataDictionary:為不同版本的FIX協議提供訊息的中繼資料(metadata),提供helper方法協助判斷field類型,判斷訊息的類型,判斷組、組件的類型,檢查message、field,field等。
總共有3種方式產生DataDictionary,首先可以通過一個系統路徑讀入設定檔產生,或者載入inputStream產生,或者從已有的DataDictionary拷貝產生。
DataDictionary中最複雜的方法就是load,思想是根據fixXXX.xml中的定義,判斷xml檔案是否合法,然後根據DTD取到各個section中的資訊並存入預先定義好的Map、List、Set中,便於之後的Helper方法能夠快速準確的搜尋和判斷相關資訊。
17. quickfix.CompositeLog:將日誌資訊輸出到多個logger中。多個logger在newCompositeLog(Log[]logs)時傳入。但是多個logger沒有優先順序,也不能控制多個logger,僅僅是日誌來了,同時寫入這多個logger中。CompositeLog由CompositeLogFactory建立。
QuickFix/J中其他的logger還有:
quickfix.ScreenLog,將日誌通過System.out列印到螢幕,相應的有quickfix.ScreenLogFactory。
quickfix.FileLog,將日誌寫入本地檔案中。相應的有quickfix.FileLogFactory。
quickfix.JdbcLog,將日誌寫入資料庫中。相應的有quickfix.JdbcLogFactory。
quickfix.SLF4JLog,使用SLF4J wrapper寫日誌,SLF4J支援JDK logging,log4j等。
另外多數Log的實現都extends了quickfix.AbstractLog。AbstractLog的作用是添加了對HeartBeat是否記錄日誌的開關。
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)回調使用者處理邏輯。