1. 程式人生 > >遠端呼叫原理初探(附c#程式碼示例)

遠端呼叫原理初探(附c#程式碼示例)

分散式系統有很多成熟的解決方案。如:微軟的WCF。WCF太過於複雜,配置也麻煩。其實可以自己動手設計一個小的分散式系統。系統的原理完全在自己掌握之中,可以根據業務隨機而變。這裡展示遠端呼叫最核心最基本的處理邏輯,其實遠端呼叫並不複雜神祕。

分散式系統其實是資料流的交換。資料必須快速的從一段傳送到另一端,否則系統性能就大打折扣。對於.net,本人設計一個非常優化易於使用的網路庫(EasyNetMessage)。使用該庫,不需要關心底層細節,所有處理物件是string、byte;傳送時,不需要處理分包(幾十M的資料也可以一次傳送);收包時,不需要處理粘包。本文所示例程式碼,就是基於該網路庫。本文章例項程式碼見底部。

實現目標

實現一個非常簡單的應用   internal int AddCall(int value1, int value2);就是兩個整數相加。呼叫過程和本地完全一樣,只是執行加法操作是在遠端伺服器實現的。操作非常簡單,但是揭示了遠端呼叫的核心處理流程。

處理過程:

a) 資料傳送

每次函式呼叫必須有一個唯一標識CallId,這個也是發包的唯一標識;服務端處理完後,返回的結果也帶有此標識。通過此標識,將傳送端資料和返回資料關聯起來了。

  傳送完資料後,客戶端執行緒要掛起,等待伺服器端的處理結果。執行緒掛起使用ManualResetEvent。並建立起CallId與ManualResetEvent的對應關係。當資料返回時,就能找到對應的ManualResetEvent。

  //callid與事件關聯
        Dictionary<int, ManualResetEvent> _callEventGroup = new Dictionary<int, ManualResetEvent>();
        //callId與返回結果關聯
        Dictionary<int, NetCallAddAck> _callResultGroup = new Dictionary<int, NetCallAddAck>();
        internal int AddCall(int value1, int
value2) { //組織傳送包 NetCallAdd add = new NetCallAdd(); add.Value1 = value1.ToString(); add.Value2 = value2.ToString(); MonitorClient client = GetCurAppClient(); if (client == null) throw new Exception("socket未連線!"); //生成執行緒事件,並與CallId關聯 ManualResetEvent callEvent = new ManualResetEvent(false); lock (_callEventGroup) { _callEventGroup.Add(add.CallId, callEvent); } //傳送資料 EN_SendDataResult result = _netServer.SendData(client.ClientSocket, add.ToEasyMessage().ToNetPacket()); if (result != EN_SendDataResult.ok) { lock (_callEventGroup) { _callEventGroup.Remove(add.CallId); } throw new Exception("網路傳送異常!"); } //等待結果 callEvent.WaitOne(3000); _callEventGroup.Remove(add.CallId); //檢視結果集 lock (_callResultGroup) { if (_callResultGroup.ContainsKey(add.CallId)) { NetCallAddAck ack = _callResultGroup[add.CallId]; return int.Parse(ack.Result); } } throw new Exception("沒有返回結果!"); }

b)資料返回

  資料返回後的處理是在另一個執行緒。資料返回後,先根據CallId查詢對應的ManualResetEvent;如果找不到,有可能伺服器處理太慢,超時了。

  先將返回結果儲存到雜湊陣列中,key為CallId。再呼叫ManualResetEvent的Set函式,喚醒呼叫端執行緒。呼叫端執行緒根據CallId到雜湊表中獲取結果。

        internal void OnRcvAck(NetCallAddAck addAck)
        {
            //根據callid找到事件
            ManualResetEvent callEvent = null;
            lock (_callEventGroup)
            {
                if (!_callEventGroup.ContainsKey(addAck.CallId))
                    return;
                callEvent =_callEventGroup[addAck.CallId];
            }

            //結果存放到雜湊表中
            lock (_callResultGroup)
            {
                _callResultGroup.Remove(addAck.CallId);
                _callResultGroup.Add(addAck.CallId, addAck);
            }
            //設定事件為有訊號,呼叫方掛起的執行緒可以繼續執行
            callEvent.Set();
        }

進一步說明:可以在此基礎上,進一步擴充套件。開發出類似Redis的記憶體庫。客戶端的呼叫也不一定是同步 ,可以採用非同步回撥的方式處理。其實如果知道處理的原理,可以根據自己的業務做裁剪。只有知其所以然,才能開發出最符合自己業務的系統,才可能進一步優化。