天天看點

.NET簡談元件程式設計之(詳解NetRemoting結構)

在本人的上一篇文章中隻是簡單的介紹了一下.NETRemoting的一般概念和基本的使用。這篇文章我想通過自己的學習和了解将對.NETRemoting的整體的一個架構進行通俗的講解,其中最重要的就是信道(管道)處理模型思想,這裡面蘊含了很多的設計原理。[王清培版權所有,轉載請給出署名]

.NETRemoting遠端處理架構是一個半成品,是.NET給我們的擴充架構,要想用于商業項目必須進行一些安全、性能方面的控制。要想進行一定深度的擴充那就要必須了解它的整體結構,各個點之間的關系才能很好的控制它。

網上講解.NETRemoting的文章很多,但是通俗易懂的沒有幾篇,都是大概講解了一下整體模型或者從MSDN上COPY過來,未能對各模型之間的接口做詳細講解。了解.NETRemoting的朋友都知道,它的實作基本上都是通過接口與接口之間的串聯形成處理管道,這也讓我們想起來了設計模式中首要提及的面向對象設計思想“面向接口程式設計”。

本人在學習.NETRemoting的時候還是比較痛苦的,由于中文資料缺乏隻能通過搜尋網上零零散散的知識點再通過拼命的了解、換位思考、提高抽象層次,才終于參透為什麼那麼多接口之間的關系,在這裡小弟會一一講解那些諸如IClientChannelSink、IClientChannelSinkProvider、IMessageSink等等接口到底是什麼意思,提供程式與信道接收器之間又是啥關系,在信道處理模型中是怎麼利用多态來設計的。

.NETRemoting處理管道

在.NETRemoting中它的整體處理模型有點像ASP.NET中的HTTP處理管道模型,消息從最上面的入口進入然後一個一個的傳遞到各個處理子產品中,這樣就形成了一個抽象的處理管道。

圖1(用戶端信道處理):

這是用戶端信道處理模型的大概結構。任何用戶端對跨遠端處理都是通過代理進行的,代理分為兩部分一種是透明代理,一種是真實代理;

透明代理是通過真實代理動态生成的,透明代理的所有成員都是用戶端将要調用的遠端對象的一個虛拟鏡像。為什麼要說是虛拟的,是因為透明代理隻是友善用戶端調用的,當我們NEW一個遠端對象時,系統将為我們動态的生成一個繼承自你NEW的那個對象的代碼,然後動态生成記憶體透明代理對象。

透明代理包括了對真實代理的調用,真正的幕後主腦是RealProxy對象,它包括了對信道處理結構的引用。在上面的圖1中為什麼真實代理是用IMessageSink接口來啟動整個信道處理模型的。這裡也是最為難了解的入口點,下面我們将自己的抽象能力提升一個層次才能知道系統為什麼要這樣設計。[王清培版權所有,轉載請給出署名]

圖2(伺服器端信道處理):

.NETRemoting饒人的名詞解釋

不管是伺服器端還是用戶端都是通過信道處理模型來對雙方的消息進行一系列的處理。用戶端的信道處理和伺服器端的信道處理有點差別,這些差別是來自它們的更高層次的抽象。這句話可能根本不好了解,從我上面的兩幅圖中我想讀者多多少少能了解點意思來,在信道處理管道中都是通過每個信道接收器提供程式來建立有處理能的信道接收器。

我們先來解決一些會給我們了解帶來困擾的英文單詞:

Channel:在系統中它表示信道,也就是處理管道。

IChannel:表示一個抽象的處理管理,用來辨別一個管道的資訊,如管道名稱,管道級别之類的。

Sink:接收器,也就是管道中的消息處理環節,系統通常用這個單詞來組合,也會讓初學者容易參數混淆(看過這篇文章後你就不會在混淆了)。

Provider:一般表示為提供程式,在.NETRemoting裡面的提供程式這是某些環境的一個入口點,比如在系統中序列化環節,系統通過SoapClientFormatterSinkProvider提供程式來建立SoapClientFormatterSink信道接收器,這裡的提供程式是表示即将進入處理過程,這個處理過程表示某一個處理環節,在這個處理環節中可能包括很多用來接收要處理的資訊,所有叫做資訊接收器,也就是sink結尾的接口。

IClientChannelSinkProvider:用戶端信道接收器提供程式,也就是圖1中表示的每個對消息的處理環節部分。該接口有一個NEXT屬性用來連接配接下一個提供程式,這樣就串聯起一個抽象的處理管道。我們要記住提供程式這是建立處理環節的入口點,真正用來處理消息的是sink結尾的接口,這裡也就是IClientChannelSInk對象。

IClientChannelSink:用戶端消息接收器,用于在管道進行中對消息的傳遞進行個性化修改,我們可以在這個裡面進行一些消息傳遞的壓縮、編碼等等。

IServerChannelSinkProvider:伺服器端信道接收器提供程式,和用戶端是相同的功能,都是用來建立資訊接收器用的,隻不過這裡是伺服器端的資訊處理。

IServerChannelSink:伺服器端消息接收器,用來在伺服器端進行消息的處理,可以對傳輸過來的消息進行解壓縮、反編碼等等,與用戶端功能是對應的。

IMessageSink:入門的朋友可能會對這個接口參數誤解,這是啥接口啊?我不是有了Sink類型的接口了嗎?為什麼還要消息接口?其實這裡也很好了解,為什麼需要各種各樣的接口,這就是我上面所說的“将自己的抽象能力提升一個高度”才能了解。如果對接口程式設計不是很熟悉的朋友可能是不太好了解。每種接口表示某種抽象,我打個比方,如果有一個對象它既實作了IMan接口,又實作了IDog接口,它在與人交流的時候我們知道它是實作了人類接口的對象,是以我們認識它并且能與它進行交流,如果它與狗交流的話那麼狗狗也是認識它的因為它也實作了狗的接口。從這裡講,實作多個接口的對象具有很深的抽象層次,在某些場合它被視為一種類型,在另外一些場合它又被視為另一種類型。下面我們講到具體的接口實作時,就明白我所說的意思了。

.NETRemoting信道接收器

上面的名詞解釋的差不多了,我們來動動手吧;

按照上面的用戶端信道模型圖,我們先來實作一個IClientChannelSinkProvider接口,我們将利用這個接口來建立一個能處理消息的IClientChannelSink接口。

using System; 

using System.Collections.Generic; 

using System.Collections; 

using System.Text; 

using System.Runtime.Remoting; 

using System.Runtime.Serialization.Formatters; 

using System.Runtime.Remoting.Channels; 

using System.Runtime.Remoting.Lifetime; 

using System.Runtime.Remoting.Channels.Http; 

using System.Runtime.Remoting.Channels.Tcp; 

using System.Runtime.Remoting.Channels.Ipc; 

namespace MyClassLibrary.CustomProvider 

        public class ClientCustomProvider : IClientChannelSinkProvider 

        { 

IClientChannelSinkProvider 成員#region IClientChannelSinkProvider 成員 

                /// <summary> 

                /// 建立該提供程式的信道接收器,所有的信道接收器提供程式均要實作CreateSink方法,保證所有的信道接收器能串聯起來。 

                /// </summary> 

                /// <param name="channel">上下文發送接口</param> 

                /// <param name="url">對象的終結點</param> 

                /// <param name="remoteChannelData">信道資料</param> 

                /// <returns>IClientChannelSink用戶端信道接收器接口</returns> 

                public IClientChannelSink CreateSink(IChannelSender channel, string url, object remoteChannelData) 

                { 

                        return new ClientCustomSink(Next.CreateSink(channel, url, remoteChannelData)); 

                } 

                /// 擷取下一個信道接收器提供程式。通過這裡的NEXT屬性将所有的信道接收器連接配接起來。 

                public IClientChannelSinkProvider Next { get; set; } 

                #endregion 

        } 

}

這裡是我們建立用戶端接收器提供程式的代碼,在這裡我們建立個一個ClientCustomSink信道接收器對象,但是在構造函數中我們用NEXT屬性又建立了一個信道接收器作為參數傳入到對象,這樣做的目的就是能保證每一個消息接收器處理完成後能接着傳遞給下一個資訊接收器處理。

using System.Runtime.Remoting.Messaging; 

        public class ClientCustomSink : BaseChannelSinkWithProperties, IClientChannelSink 

                IClientChannelSink nextchannelsink; 

                public ClientCustomSink(IClientChannelSink nextchannel) 

                        nextchannelsink = nextchannel; 

IClientChannelSink 成員#region IClientChannelSink 成員 

                public void AsyncProcessRequest(IClientChannelSinkStack sinkStack, IMessage msg, ITransportHeaders headers, System.IO.Stream stream) 

                        nextchannelsink.AsyncProcessRequest(sinkStack, msg, headers, stream); 

                public void AsyncProcessResponse(IClientResponseChannelSinkStack sinkStack, object state, ITransportHeaders headers, System.IO.Stream stream) 

                        nextchannelsink.AsyncProcessResponse(sinkStack, state, headers, stream); 

                public System.IO.Stream GetRequestStream(IMessage msg, ITransportHeaders headers) 

                        msg.Properties.Add("自定義消息", "自定義消息值"); 

                        return nextchannelsink.GetRequestStream(msg, headers); 

                public IClientChannelSink NextChannelSink 

                        get { return nextchannelsink; } 

                public void ProcessMessage(IMessage msg, ITransportHeaders requestHeaders, System.IO.Stream requestStream, out ITransportHeaders responseHeaders, out System.IO.Stream responseStream) 

                        nextchannelsink.ProcessMessage(msg, requestHeaders, requestStream, out responseHeaders, out responseStream); 

                public IDictionary NextProperise 

                        get { return nextchannelsink.Properties; } 

有一這裡的代碼太長我就不加注釋了。這個對象主要實作了IClientChannelSink接口,也就是信道接收器接口,在這個接口面明前比Provider提供程式多了很多東西。這裡才是真正處理的地方,系統為了代碼的整潔性将提供程式與資訊接收器分離開,其實也可以将這兩個接口進行合并。在構造函數裡面我們用一個私有的IClientChannelSink儲存了下一個Sink。該對象還實作了BaseChannelSinkWithProperties抽象類,這個類是用來擷取下一個信道接收器的内部屬性用的。如果我們沒有實作這個抽象類就要自己實作NextProperise屬性。[王清培版權所有,轉載請給出署名]

其實在IClientChannelSink裡面比較重要的就是GetRequestStream和ProcessMessage兩個方法,一個是用來擷取即将要發送的Stream流,一個是進行發送的方法。為了便于大家的了解,請看圖3:

我上面的處理流程不一定就是GetRequestStream是第一步,但是它是ProcessMessage方法的上一步,是以我用1、2表示。當GetRequestStream到了最後一個Sink時,系統将進行最後的調用,也就是進行遠端發送了。

在GetRequestStream中我們加入了一些自己定義的資料,我們在通過伺服器端的IServerChannelSink擷取這消息。

        public class ServerCustomSink : BaseChannelSinkWithProperties, IServerChannelSink 

                private IServerChannelSink iserverchannelsink; 

                public ServerCustomSink(IServerChannelSink serverchannel) 

                        iserverchannelsink = serverchannel; 

IServerChannelSink 成員#region IServerChannelSink 成員 

                public void AsyncProcessResponse(IServerResponseChannelSinkStack sinkStack, object state, IMessage msg, ITransportHeaders headers, System.IO.Stream stream) 

                        if (iserverchannelsink != null) 

                                iserverchannelsink.AsyncProcessResponse(sinkStack, state, msg, headers, stream); 

                        throw new NotImplementedException(); 

                public System.IO.Stream GetResponseStream(IServerResponseChannelSinkStack sinkStack, object state, IMessage msg, ITransportHeaders headers) 

                                iserverchannelsink.GetResponseStream(sinkStack, state, msg, headers); 

                public IServerChannelSink NextChannelSink 

                        get { return iserverchannelsink; } 

                public ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders, 

                        System.IO.Stream requestStream, out IMessage responseMsg, out ITransportHeaders responseHeaders, out System.IO.Stream responseStream) 

                        sinkStack.Push(this, "ok"); 

                        return NextChannelSink.ProcessMessage(sinkStack, requestMsg, requestHeaders, requestStream, outresponseMsg, out responseHeaders, out responseStream); 

圖4:

總結:

.NETRemoting是一個非常成熟的CLR遠端處理架構,其實作的原理也很複雜,基本上每一部分都可以擴充,它的内部實作都是通過面向接口來的,接口的粒度也非常小。從IXXXChannelSinkProvider和IXXXChannelSink兩種接口就可以看出來,裡面的空間很大。

本篇文章隻是本人在學習.NETRemoting過程中的一點小小的感悟獻給那些還沒有搞清楚Remoting的基本架構的朋友。謝謝

 本文轉自 王清培 51CTO部落格,原文連結:http://blog.51cto.com/wangqingpei557/689406,如需轉載請自行聯系原作者

繼續閱讀