在ESFramework 4.0 概述一文中,咱們提到ESFramework.dll做爲通訊框架的核心,定義了消息處理的骨架流程,本文咱們來詳細剖析這個流程以及該骨架中所涉及的各個組件。ESFramework的骨架流程以下圖所示: html
一.全部的網絡引擎都使用同一消息處理骨架流程緩存
ESFramework支持TCP/UDP、二進制協議/文本協議、服務端/客戶端組合而成的2x2x2=8種引擎,不管是哪種引擎,都實現了INetEngine接口,也都使用上圖所示的消息處理骨架流程來處理所接收到的全部消息。服務器
因此,只要掌握了這一消息處理的骨架流程,就掌握了ESFramework的核心機密。也只有掌握了該骨架流程,咱們才能輕鬆自如地駕馭ESFramework。網絡
在該骨架流程中,涉及了多個消息組件,像消息分派器IMessageDispatcher、消息管道IMessagePipe、消息轉換器IMessageTransformer、消息監控器IMessageSpy、消息內層分派器NakeDispatcher、消息處理器工廠IMessageProcesserFactory、以及消息處理器IMessageProcesser。框架
注意,MessagePipe能夠看作是MessageTransformer以及GatewayMessageSpy和InnerMessageSpy的封裝。NakeDispatcher內部則包含了MessageProcesserFactory,而且利用MessageProcesserFactory來建立MessageProcesser。post
網絡引擎從網絡接收到一個消息後會交給MessageDispatcher進行分派,MessageDispatcher在分派消息的時候,首先會讓接收到的消息通過MessagePipe進行必要的監控和轉換,而後調用NakeDispatcher對消息進行最終分派處理並獲取其返回應答消息,而後再讓應答消息通過MessagePipe進行必要的監控和轉換,最終返回給網絡引擎,網絡引擎會將最後獲得的應答消息經過網絡發送出去。加密
消息處理的骨架流程圖中使用箭頭表示出了消息在框架中的流動路徑與方向,能夠看到,消息進入的路徑與返回的路徑是對稱的。而消息由進入轉向爲返回的轉折點是在消息處理器MessageProcesser,即消息處理器處理接收到的消息並返回應答消息。若是沒有應答消息,則消息的路徑到達MessageProcesser節點被處理後即終止了。spa
二.消息分派器MessageDispatcher插件
消息分派器IMessageDispatcher的基礎接口定義以下: 調試
首先,消息分派器須要使用MessagePipe組件和NakeDispatcher組件,它其實是要保證一個流程,即在實現DispatchMessage方法的時候,讓進入的消息必須先通過消息管道MessagePipe,而後才提交給NakeDispatcher去處理,若是有應答消息,則還要保證應答消息也必須通過MessagePipe以後,才能返回給網絡引擎。IMessageDispatcher接口的實現--MessageDispatcher類,保證了這一點。
其次,DispatchMessage方法的參數便是接收到的進入的消息,返回值便是應答消息,若是沒有應答,則返回值爲null。
再次,框架要求DispatchMessage方法毫不能拋出異常,不然,這個異常將衝擊到網絡引擎組件,這可能致使對應鏈接或Session上的後續消息將不能被接收。
ESFramework.Core.MessageDispatcher類確保了DispatchMessage方法不會拋出異常,由於其在實現DispatchMessage方法的內部截獲了全部的異常,並將其經過IAgileLogger 記錄到日誌。這就是爲何IMessageDispatcher須要注入EsfLogger屬性的緣由。(關於ESFramework中採用的日誌模式的更多信息,能夠參見ESFramework 4.0 快速上手 -- 異常日誌 )
通常來講,DispatchMessage內部拋出異常的來源一般是兩個:
(1)消息處理器拋出異常。因爲真正處理消息的是咱們應用程序提供的消息處理器,因此若是在業務處理的過程當中沒有捕獲拋出的異常,那麼這個異常最終會被框架的消息分派器捕獲。
(2)消息管道在監控或變換消息時拋出的異常。好比,加密解密失敗等。
三.消息管道MessagePipe
MessagePipe由MessageTransformer以及GatewayMessageSpy和InnerMessageSpy構成,其主要做用是監控和變換消息。
IMessagePipe接口的定義以下:
當消息由MessageDispatcher分派給MessagePipe時,首先通過的組件是GatewayMessageSpy,其次通過MessageTransformer,最後通過InnerMessageSpy到達NakeDispatcher組件;而NakeDispatcher返回的應答消息所通過的路徑恰好與此相反。ESFramework.Core.MessagePipe類的PipeInMessage方法和PipeOutMessage方法的實現保證了這一點。
1.消息監控器MessageSpy
GatewayMessageSpy和InnerMessageSpy都是MessageSpy類型的組件,其都繼承自IMessageSpy接口:
對於進入的消息,將調用SpyMessageReceived方法對其監控;而對於返回的消息,將調用SpyMessageToBeSent方法對其監控。
之因此稱爲「監控」,是由於MessageSpy不會修改消息,它不會改變消息的任何內容;而負責修改或變換消息的是MessageTransformer組件。這一點正是MessageSpy和MessageTransformer的本質區別所在,不一樣類型的消息組件,職責不同。
至於MessageSpy要作什麼樣的監控,是由咱們具體的應用決定的,好比,咱們的應用須要記錄接收到的最後10條「原始」的消息,那麼就能夠實現一個MessageSpy,掛接在MessagePipe的GatewayMessageSpy屬性上便可。
除了監控以外, MessageSpy還有個特權 -- 它能決定放行哪些消息、丟棄哪些消息,這由SpyMessageReceived方法和SpyMessageToBeSent方法返回的bool值體現出來。若是返回值爲false,則表示丟棄該消息。消息被丟棄後,到此終結,不會再被傳遞到下一個消息組件。
通常在什麼狀況下,MessageSpy會丟棄消息了?好比說,被監控的消息格式不正確,消息多是黑客模擬的"惡意"的消息;又好比說,在使用UDP通訊時,接收到的消息是不完整的,等等。這些消息在MessageSpy這個環節就被過濾掉,不會污染到後續的消息組件,特別是不會給消息處理器帶來額外的麻煩。
若是咱們的應用不須要任何消息監控,則可使用一個「佔位符」消息監控器掛接到MessagePipe,ESFramework提供了null object模式實現的ESFramework.Core.EmptyMessageSpy做爲這個「佔位符」供應用直接使用。
2.消息轉換器 MessageTransformer
剛剛提到,MessageTransformer對消息能夠進行變換,通過MessageTransformer組件的消息,其內容會被改變。這就爲咱們對消息的加密、解密、壓縮、解壓等等提供了掛接點。好比,網絡上傳遞的消息都是加密的,網絡引擎接收到這些加密的消息傳遞給分派器,分派器又傳遞給了MessagePipe,因此,GatewayMessageSpy做爲消息進入MessagePipe的第一個組件,其監控到的就是加密的消息;而接下來當消息通過MessageTransformer被解密後,被傳遞給InnerMessageSpy的消息是已經解密的、是明文的,這種消息在ESFramework中稱爲赤裸消息「NakeMessage」。因此,InnerMessageSpy監控到的消息是明文的NakeMessage。當接收到的消息被傳遞到內層消息分派器NakeDispatcher被分派時,已是NakeMessage,因此內層的消息分派器被稱爲NakeDispatcher。
對於NakeMessageDispatcher返回的應答消息,也是明文消息,當其沿出去的方向通過MessageTransformer時,MessageTransformer會對其進行加密,因此,最終網絡引擎發送出去的應答消息也是加密的。
從上面的描述,咱們已經看出,InnerMessageSpy和GatewayMessageSpy所處的位置不一樣而決定了它們監控到的消息的狀態不同。一般,GatewayMessageSpy看到的消息都是通過加密的、壓縮的,是密文;而InnerMessageSpy看到的消息都是已經被解密的、被解壓縮的,是明文。因此,你的應用究竟是須要掛接一個GatewayMessageSpy仍是一個InnerMessageSpy,取決於你須要監控到什麼狀態的消息。
消息過濾器必須實現IMessageTransformer接口:
對於進入的消息,框架將調用MessageTransformer的CaptureReceivedMessage方法對消息進行轉換;對於要出去的應答消息,將調用MessageTransformer的CaptureBeforeSendMessage方法對消息進行變換。
從IMessagePipe接口的定義咱們看到,其只能掛接一個MessageTransformer實例,若是咱們要掛接多個MessageTransformer,好比,一個MessageTransformer用於加密/解密,一個MessageTransformer用於壓縮/解壓縮,那該怎麼辦了?ESFramework提供了一個容器類型MessageTransformer-- ESFramework.Core.ContainerStyleTransformer,它是一個實現了IMessageTransformer接口的容器,內部有一個列表能夠容納多個MessageTransformer實例。因爲,ContainerStyleTransformer實現了IMessageTransformer接口,因此,能夠將其掛接到MessagePipe組件,而咱們再把須要用到的多個MessageTransformer實例放到這個容器裏就解決了MessagePipe須要掛接多個MessageTransformer的問題。
同MessageSpy的狀況同樣,若是咱們的應用不須要加密/解密消息等任何變換,則可使用一個「佔位符」消息過濾器掛接到MessagePipe,ESFramework提供了null object模式實現的ESFramework.Core.EmptyMessageTransformer做爲這個「佔位符」供應用直接使用。
四.內層消息分派器NakeDispatcher
剛纔已經提到,當進入的消息被傳遞到NakeDispatcher時,已是明文的了,這個時候,該消息就能夠直接被消息處理器處理了。內層消息分派器的接口INakeDispatcher定義以下:
NakeDispatcher須要經過DispatchMessage方法最終分派並處理消息,且返回應答消息(若是有的話)。消息須要被消息處理器處理,那麼從哪裏獲取正確的消息處理器了?是消息處理器工廠。因此NakeDispatcher須要依賴於ProcesserFactory。ESFramework已經提供了ESFramework.Core.NakeDispatcher類實現了INakeDispatcher接口,供咱們使用,咱們沒必要再實現此接口。
1.消息處理器工廠 ProcesserFactory
處理器工廠用於建立或返回正確的消息處理器,IProcesserFactory的接口定義以下:
CreateProcesser方法建立或返回什麼樣的處理器取決於消息的類型。即NakeDispatcher會從消息的頭部IMessagHeader(詳情請參見ESFramework 4.0 進階(01) -- 消息)中取出MessageType屬性的值,而後調用IProcesserFactory的CreateProcesser方法獲取正確類型的處理器。若是工廠中沒有對應的處理器存在,CreateProcesser方法將返回null,這種狀況通常只會在調試階段出現,一般是由於爲對應的消息尚未定義相應的消息處理器。若是在正式運行階段出現CreateProcesser返回null,那將是很是嚴重的,要麼是服務端和客戶端所引用的程序集的版本不一致,要麼是黑客在向服務器發送試探消息。當CreateProcesser返回null時,NakeDispatcher會將詳細的信息記錄到日誌文件,咱們能夠從日誌中查看到消息的具體內容。
ESFramework已經提供了ESFramework.Core.ProcesserFactory類實現了IProcesserFactory接口,其內部能夠掛接多個消息處理器實例,大部分狀況下,咱們直接使用這個實現就好了,不須要再實現本身的處理器工廠。而且,ESFramework.Core.ProcesserFactory類的實現採用的緩存,當根據消息類型尋找處理器時,不須要每次都遍歷全部的處理器。
當NakeDispatcher從IProcesserFactory獲取到正確的處理器以後,即將消息提交給處理器進行處理。
2.消息處理器MessageProcesser
消息處理器接口IMessageProcesser定義以下:
一個消息處理器可以處理哪些類型的消息,它本身是最清楚的,因此IMessageProcesser提供了CanProcess方法來讓咱們能夠查詢它是否能處理目標類型的消息。
同須要定義哪些類型的消息同樣,繼承了IMessageProcesser接口的消息處理器的實現是由具體的應用來作的,當它們定義好以後,咱們即可以將它們掛接處處理器工廠來處理消息了,框架會根據消息的類型來自動調用它們(IOC模式)。
附帶說一下,之因此採用ESPlus能夠加快ESFramework的開發,就是由於ESPlus已經爲咱們預約義好了一批消息類型、消息協議以及消息處理器,咱們只要將它們掛接到ESFramework的消息骨架上,就能夠運轉起來。而若是咱們直接基於ESFramework.dll開發,則這些事情須要咱們本身手動去打造,雖然麻煩一些,可是足夠靈活。ESPlus雖然讓咱們省了很多事,可是也限制了ESFramework的某些方面,正所謂「有得必有失」。
五.實例化一個最簡單消息處理流程
假設咱們的某個項目不須要任何類型的消息監控,也不須要任何類型的消息變換,僅僅須要掛接兩個消息處理器便可。那麼,咱們能夠經過相似下面的代碼來實例化這個處理流程:
上面的組裝代碼看視簡單,卻具備很是強的擴展性,若是項目須要,咱們能夠構造出很是複雜的流程。甚至,因爲採用了接口分離原則,咱們還能夠實現本身的消息分派器或處理器工廠,好比,咱們能夠實現一個插件處理器工廠,專門從插件中動態地加載所須要的消息處理器(ESPlus提供的ESPlus.Core.Addins.ServerAddinProcesserFactory正是作這件事情的)。
若是有通訊引擎實例,那麼將構造好的messageDispatcher對象掛接到通訊引擎,就可使整個流程運轉起來了。
關於ESFramework消息處理的骨架流程,就介紹這麼多,下一篇咱們將深刻到ESFramework提供的各類網絡引擎內部去看看。that's all,thanks.