黄色网页视频 I 影音先锋日日狠狠久久 I 秋霞午夜毛片 I 秋霞一二三区 I 国产成人片无码视频 I 国产 精品 自在自线 I av免费观看网站 I 日本精品久久久久中文字幕5 I 91看视频 I 看全色黄大色黄女片18 I 精品不卡一区 I 亚洲最新精品 I 欧美 激情 在线 I 人妻少妇精品久久 I 国产99视频精品免费专区 I 欧美影院 I 欧美精品在欧美一区二区少妇 I av大片网站 I 国产精品黄色片 I 888久久 I 狠狠干最新 I 看看黄色一级片 I 黄色精品久久 I 三级av在线 I 69色综合 I 国产日韩欧美91 I 亚洲精品偷拍 I 激情小说亚洲图片 I 久久国产视频精品 I 国产综合精品一区二区三区 I 色婷婷国产 I 最新成人av在线 I 国产私拍精品 I 日韩成人影音 I 日日夜夜天天综合

ESFramework介紹之(7)-- 服務器代理IServer

系統 2262 0
(本文原作于2006.03.15,第一次修正于2006.06.06,修正后適用于ESFramework V0.3+)
(本文是 ESFramework 對客戶端開發的支持特性之一 ,如果要按順序閱讀,請轉到 ESFramework介紹( ))

分布式系統的構建一般有兩種模式,一是基于消息(如 Tcp http 等),一是基于方法調用(如 RPC WebService Remoting )。深入想一想,它們其實是一回事。如果你了解過 .NET Proxy ,那么你會發現,方法調用和消息請求 / 回復實際上是可以相互轉換的, .NET Proxy 的實現,就是在堆棧幀和消息之間相互轉換的過程。關于這方面的詳細論述可以參見《 .Net 本質論》一書。

我覺得 IServerAgent 是我在開發 ESFramework 期間非常滿意的一個想法,相信大家也會對它感興趣的。因為它 使得使用基于消息請求 / 回復的交互就像方法調用一樣簡單
客戶端與服務器之間的所有通信都可經過IServerAgent,包括要轉發的P2P消息。它的主要目的是:
(1)屏蔽客戶端與服務端之間的通信協議(Tcp/Udp),ITcpServerAgent、IUdpServerAgent
(2)可將異步的消息請求/回復轉化為同步的方法調用。

ESFramework 主要支持基于 Tcp Udp C/S 系統,所以客戶端和服務端之間是通過消息進行交互的。如果僅僅是客戶端發出請求、服務器給出服務這種情況很容易處理,但是如果服務端有主動發消息給客戶端的情況,事情就會變得稍微復雜。通常,客戶端會有一個專門的接收線程來負責從網絡接收數據,然后把接收的消息交給對應的處理器處理,或者,這個接收到的消息是個服務端給出的回復,那么這個回復就應該交給發出請求的請求者,但是對應的請求者在哪里了?這種回復消息與請求消息的匹配是比較繁瑣的,特別是在上述服務端可以主動給客戶端發送消息的情況下。為了簡化這個過程, IServerAgent 出現了,它用于客戶端,像它的名字一樣,可以把它當作服務器。 IServerAgent 的主要目的就是將消息請求 / 回復轉換成方法調用,就像該接口定義的一樣:
ESFramework介紹之(7)-- 服務器代理IServerAgent

public interface IServerAgent
{
/// <summary>
/// 如果超時仍然沒有回復,則拋出超時異常
/// 如果dataPriority!=DataPriority.CanBeDiscarded,則checkRespond只能為false
/// </summary>
NetMessageCommitRequest(NetMessagerequestMsg,DataPrioritydataPriority, bool checkRespond);
}

public enum DataPriority
{
High,
// 緊急命令
Common, // 如普通消息,如聊天消息
Low, // 如文件傳輸
CanBeDiscarded // 如視頻數據、音頻數據
}

首先解釋一下參數 dataPriority 的意義, dataPriority 參數僅僅對 Tcp 協議起作用,當有多個請求要同時發送時,它決定了發送的優先級。 CanBeDiscarded 表明這個消息在網絡繁忙時可以被拋棄,比如即時通訊的音頻數據、視頻數據等。關于這個數據發送的優先級機制的實現是 ITcpAutoSender ,這個組件會在后文中介紹。

CommitRequest 方法提交一個請求消息該給服務器,并返回一個回復消息給請求者。這就是一個方法調用!!!其間隱藏了通過網絡將消息發送給服務器并從服務器獲取結果的中間細節。這是怎么做到的?思路其實很簡單,只是描述起來有些復雜。主要要解決兩個問題,一是如何將請求消息與對應的回復匹配起來,二是 CommitRequest 從哪里找到匹配的回復。
對于第一個問題,相信大家還記得 IMessageHeader 定義中有個 CorrelationID 屬性,正如其名,這是一個隨機數,每生成一個新的請求消息,就會產生一個隨機數賦值給 CorrelationID 屬性,由于隨機數重復的可能性很小,所以可以把它當作是唯一的。這樣一個隨機數就唯一的標志了一個請求,當服務端收到這個請求后,就處理這個請求,并把回復消息的消息頭中的 CorrelationID 屬性設為與對應的請求消息的 CorrelationID 一樣的值,這樣,客戶端收到回復消息后,就可以和對應的請求消息一一對應起來了。

對于第二個問題的解釋,就需要涉及到 ESFramework 中支持客戶端開發的其它兩個組件: EsbPassiveDataDealer IResponseManager EsbPassiveDataDealer 是客戶端用戶處理所有接收到的消息的處理器,而 IResponseManager 組件用于暫存所有的來自服務端的回復。對于每個接收到的消息, EsbPassiveDataDealer 判斷其是否為回復,如果是,則將其交給 IResponseManager 暫存。 IResponseManager 為暫存的每個回復都設置的生存期 TTL ,如果回復在 IResponseManager 中的時間超過了這個 TTL ,則會被刪除。

你也許已經想到第二個問題的解決方法了。是的, CommitRequest 方法將請求發送到網絡之后,就定時從 IResponseManager 中尋找 CorrelationID 為請求消息頭的 CorrelationID 值的回復消息,如果找到,就返回它,否則就等待循環,直至超時拋出 TimeoutException 異常。下面給出 IResponseManager 的接口定義:
ESFramework介紹之(7)-- 服務器代理IServerAgent

public interface IResponseManager
{
void Initialize();

void PushResponse(NetMessageresponse);
NetMessagePopRespose(
int correlationID, int serviceKey); // 立即返回
NetMessagePickupResponse( int serviceKey, int corelationID); // 在TimeoutSec時間內不斷的PopRespose

/// <summary>
/// ResponseTTL如果一個回復在管理器中存在的時間超過ResponseTTL,則會被刪除。如果ResponseTTL為0,則表示不進行生存期管理
/// </summary>
int ResponseTTL{ set ;} // s

/// <summary>
/// 如果在TimeoutSec內,仍然接收不到期望的回復,則拋出異常。取0時,表示不設置超時
/// </summary>
int TimeoutSec{ set ;}
}

IServerAgent的具體實現包括TcpServerAgent和UdpServerAgent,分別支持Tcp協議和Udp協議的客戶端開發。從它們的接口定義中可以看到它們都借助于IServerAgentHelper實現自己。

public interface IServerAgentHelper
{
IEsbLoggerEsbLogger{
set ; get ;}
IContractHelperContractHelper{
set ; get ;}
INetMessageHookNetMessageHook{
set ; get ;}
IPassiveHelperPassiveHelper{
set ; get ;}

IResponseManagerResponseManager{
set ; get ;}

ISingleMessageDealerSingleMessageDealer{
set ; get ;}

IMessageDispatcher ConstructDispatcher ();
}

我們要特別注意其ConstructDispatcher方法,該方法構建了一個客戶端比較常用的消息分配器實例。在介紹 IMessageDispatcher 時,我們講過,客戶端通常不需要對消息Spy,僅僅需要Hook就可以了,所以IServerAgentHelper正是通過對各組件的組裝做到了這一點:

public IMessageDispatcherConstructDispatcher()
{
// NakeDispatcher
EsbPassiveDataDealerdealer = new EsbPassiveDataDealer( this .responseManager, this .passiveHelper, this .singleMessageDealer);
EsbPassiveDealerFactoryfactory
= new EsbPassiveDealerFactory(dealer);
NakeDispatchernakeDispatcher
= new NakeDispatcher();
nakeDispatcher.ContractHelper
= this .contractHelper;
nakeDispatcher.DataDealerFactory
= factory;

// MessageDispatcher
IMessageDispatchermessageDispatcher = new MessageDispatcher();
messageDispatcher.ContractHelper
= this .contractHelper;
messageDispatcher.NetMessageHook
= this .netMessageHook;
messageDispatcher.NakeDispatcher
= nakeDispatcher;

return messageDispatcher;
}

在IServerAgent的基礎之上,我們就可以從一個新的角度來設計客戶端的結構的,那就是采用和功能服務器一樣的插件方式。在ESFramework的支持下,我們的應用開發變得非常簡潔和簡單,所要做的主要內容就是 開發服務端的“業務功能插件”和對應的客戶端的“PassiveAddin”(客戶端插件)。 如果我們的應用已經發布投入使用,而此時用戶要求添加一項新的業務,那將是非常簡單的事情,那就是開發一個實現了新業務的功能插件動態加載到功能服務器中、再開發一個對應的客戶端插件動態加載到客戶端中,這樣就可以了。服務器不用重編譯、甚至不用停止服務;客戶端也不用重編譯、甚至不用停止使用。一切都是在運行中動態完成的。

這是如何做到的?請關注本系列文章。

轉到: ESFramework 可復用的通信框架(序)

ESFramework介紹之(7)-- 服務器代理IServerAgent


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論