WCF分散式開發步步為贏(11):WCF流處理(Streaming)機制

iDotNetSpace發表於2009-06-01
   WSE3.0框架提供了資料優化傳輸機制,WSE3.0構建Web服務安全(4):MTOM訊息傳輸優化和檔案上傳、下載 疑問裡進行了介紹。WCF同樣也提供了流操作來支援大資料物件的傳輸和處理優化機制,今天我們WCF分散式開發步步為贏系列的(4):使用流操作(Streaming Operations)優化傳輸。本節會詳細介紹流操作的相關概念、程式設計實現過程,以及實際開發過程中需要主要的一些問題。本節結構:【1】流處理的概念【2】流處理的特點【3】示例程式碼分析【4】總結。最後上傳本文的示例程式碼。

    Streaming,本文翻譯為流處理(張逸兄翻譯的Programming WCF Services一書裡把這個機制翻譯為“流操作”,不存在爭議。我選擇“流處理”一詞的意思,只是想把這個Streaming詞形象準確化,動名詞,流化處理、流處理,翻譯為流操作,初學者會誤會認為這個是一個服務操作。因為Streaming只是WCF內建的一個機制。而不是操作。)

   我們首先來理解什麼是Streaming流處理。

【1】Streaming流處理的概念:

    通常情況,客戶端和服務端進行互動,傳遞訊息,都是放到接收端的快取裡,待接收完畢後再進行處理。無論接收端是客戶端還是服務端都是如此。

【1.1】要解決的問題:

     當客戶端呼叫服務時,要阻塞客戶單程式,直到訊息傳送完畢,服務端才開始處理資料,然後是返回處理完畢的結果給客戶端,客戶端接收完畢,才能解除阻塞。這樣帶來的問題是當訊息傳遞的時間很短,相對處理時間可以忽略不計,不會影響系統服務的效率。但是要是訊息資料很大,比如是圖片或者多媒體物件。每次傳輸時間相對較大,這樣接收端的等待時間過久,勢必每次阻塞都會很長,程式無法繼續執行。因而導致效率低下。

【1.2】Streaming流處理:

     Streaming流處理就是WCF提供的主要針對大量訊息資料處理的一種優化機制。WCF允許接收端通過通道接受訊息的同時,啟動對訊息資料的處理,這樣的過程稱為流傳輸模型。

【2】Streaming流處理的特點:

      顯然對於處理大量的訊息資料而言,流處理機制改善了系統的吞吐量和響應效率。

【2.1】流處理操作定義:

    WCF的流處理機制需要使用.NET FrameWork定義的Stream類(它是FileStream, NetworkStream, MemoryStream 的父類)。流處理適用一下場景:

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gt[ServiceContract]
interface IMyContract
{
   [OperationContract]
   Stream StreamReply1( );

   [OperationContract]
   
void StreamReply2(out Stream stream);

   [OperationContract]
   
void StreamRequest(Stream stream);

   [OperationContract(IsOneWay 
= true)]
   
void OneWayStream(Stream stream);
}

  它可以做為返回資料、引數、輸出引數的型別。當然也可以作為單調服務的操作引數。這裡使用的引數必須是可序列化的,例如MemoryStream。而FileStream不支援序列化因而不能作為引數或者返回資料的型別。

【2.2】流處理與繫結協議:

    流處理機制在特定的繫結協議中才能使用,目前是BasicHttpBinding, NetTcpBinding, 和NetNamedPipeBinding 支援流處理模型。但是在預設情況下,WCF禁止流處理模式。

   流傳輸模式使用使用TransferMode進行配置,TransferMode為列舉型別,其定義如下:

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gt
    
public enum TransferMode
    {
        
// Summary:
        
//     The request and response messages are both buffered.
        Buffered = 0,
        
//
        
// Summary:
        
//     The request and response messages are both streamed.
        Streamed = 1,
        
//
        
// Summary:
        
//     The request message is streamed and the response message is buffered.
        StreamedRequest = 2,
        
//
        
// Summary:
        
//     The request message is buffered and the response message is streamed.
        StreamedResponse = 3,
    }

  只有Streamed模式支援2.1中列舉的流處理模式場景。除了直接在服務上配置屬性以外,我們還可以再服務的配置檔案裡定義流傳輸模式。程式碼如下:

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gt      <basicHttpBinding>
        
<binding name="basicHttpBinding"   receiveTimeout="10:10:10"  transferMode="Streamed" maxReceivedMessageSize="200000">
        
binding>
      
basicHttpBinding>
      
<netTcpBinding>
        
<binding name="netTcpBinding"  receiveTimeout="10:10:10" transferMode="Streamed" maxReceivedMessageSize="200000">
        
binding>
      
netTcpBinding>

   此為託管宿主的配置檔案,特定的繫結協議,可以配置其傳輸模式。

【2.3】注意:

    流處理在使用http協議時,其預設訊息長度是64K,如果希望增加資料長度,需要在配置檔案裡重新設定。如: maxReceivedMessageSize="200000",具體程式碼如下:

 

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gt<basicHttpBinding>
        
<binding name="basicHttpBinding"   receiveTimeout="10:10:10"  transferMode="Streamed" maxReceivedMessageSize="200000">
        
binding>
      
basicHttpBinding>

【3】示例程式碼分析:

    這裡測試的流處理機制,使用的是處理圖片的上傳於下載,分別使用Stream和其子類MemoryStream作為引數或者返回訊息資料的型別。基本程式碼演示的是流處理三種模式場景:

【3.1】服務端:

    服務契約分別定義了下載資料和上傳資料,下載資料使用的型別MemoryStream,上傳資料引數型別是是Stream。具體程式碼如下:

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gt //1.服務契約
    [ServiceContract( Namespace = "http://www.cnblogs.com/frank_xl/")]
    
public interface IWCFService
    {
        
//操作契約,獲取資料流
        [OperationContract]
        MemoryStream DownLoadStreamData(
string fileName);

        
//操作契約,輸出資料流
        [OperationContract]
        
void DownLoadStreamDataOut(out MemoryStream stream, string fileName);

        
//操作契約,上載資料流,單向操作的訊息轉換為資料流
        [OperationContract(IsOneWay=true)]
        
void UpLoadStreamData(Stream stream);
    }
    
//2.服務類,繼承介面。實現服務契約定義的操作
    public class WCFService : IWCFService
    {
        
//1實現介面定義的方法,下載檔案資料流
        public MemoryStream DownLoadStreamData(string fileName)
        {
           
// Stream stream = 
            byte[] file = new byte[200000];
            String filePath 
= AppDomain.CurrentDomain.BaseDirectory + @"\" + fileName;
            file 
= File.ReadAllBytes(filePath);
            MemoryStream memoryStream 
= new MemoryStream(file);
            
return memoryStream;
        }
        
//2實現介面定義的方法,下載檔案資料流
        public void DownLoadStreamDataOut(out MemoryStream stream, string fileName)
        {
            
// Stream stream = 
            byte[] file = new byte[200000];
            String filePath 
= AppDomain.CurrentDomain.BaseDirectory + @"\" + fileName;
            file 
= File.ReadAllBytes(filePath);
            MemoryStream memoryStream 
= new MemoryStream(file);
            stream 
= memoryStream;
            
        }
        
//3實現介面定義的方法,上傳檔案資料流
        public void UpLoadStreamData(Stream stream)
        {
            
// codes here to deal with the stream Stream stream = 
            Console.WriteLine("The Stream length is {0}",stream.Length);

        }
    }

【3.2】託管宿主:

    我們分別使用basicHttpBinding和netTcpBinding定義了兩個服務終結點,這裡不要忘記設定最大接受訊息資料大小maxReceivedMessageSize="200000",如果設定較小會導致接受資料超過設定的錯誤。具體程式碼如下:

 

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gt  <system.serviceModel>
    
<services>
      
<service behaviorConfiguration="WCFService.WCFServiceBehavior"
        name
="WCFService.WCFService">
        
<endpoint 
          address
="http://localhost:8002/WCFService" 
          binding
="basicHttpBinding" 
          contract
="WCFService.IWCFService">
        
endpoint>
        
<endpoint
         address
="net.tcp://localhost:8004/WCFService"
         binding
="netTcpBinding"
         contract
="WCFService.IWCFService">
        
endpoint>
        
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        
<endpoint address="mex"  binding="mexTcpBinding" contract="IMetadataExchange" />
        
<host>
          
<baseAddresses>
            
<add baseAddress="http://localhost:8001/"/>
            
<add baseAddress="net.tcp://localhost:8003/"/>
          
baseAddresses>
        
host>
      
service>
    
services>
    
<behaviors>
      
<serviceBehaviors>
        
<behavior name="WCFService.WCFServiceBehavior">
          
<serviceMetadata httpGetEnabled="true" />
          
<serviceDebug includeExceptionDetailInFaults="false" />
        
behavior>
      
serviceBehaviors>
    
behaviors>
    
<bindings>
      
<basicHttpBinding>
        
<binding name="basicHttpBinding"   receiveTimeout="10:10:10"  transferMode="Streamed" maxReceivedMessageSize="200000">
        
binding>
      
basicHttpBinding>
      
<netTcpBinding>
        
<binding name="netTcpBinding"  receiveTimeout="10:10:10" transferMode="Streamed" maxReceivedMessageSize="200000">
        
binding>
      
netTcpBinding>
    
bindings>
  
system.serviceModel>

【3.3】客戶端:

   客戶端分別測試資料的上傳和下載,使用不同的方法。這裡的測試目前

【4】總結:

    本文介紹了WCF流處理模型,但是實現程式碼出現問題。

(1)作為WCF的流處理機制,確實為我們的大規模訊息資料傳輸提供了理想的機制,提高了系統的效率和響應速度。

(2)Stream作為.net類庫的內部型別,在.net平臺上使用來說較為方便,但是與異構平臺的互動勢必受到諸多限制,也違背了WCF跨平臺的初衷。

(3) 我在除錯這個示例程式碼的過程中遇到了幾個錯誤,基本都整理出來放到WCF分散式開發常見錯誤裡了。使用netTcpBinding繫結進行資料傳輸的時候,一個很有價值的錯誤就是:the socket connection was aborted. this could be caused by an error processing your message or a receive timeout being exceeded by the remote host ...這個錯誤我google國內和國外的一些資料,但是解決的辦法都是更換協議。其實是一種很無奈的措施,目前我還沒有找出更好的解決辦法就是基於netTcpBinding。表面的原因是服務處理超時。但是具體的錯誤資訊沒有這樣簡單。我更換協議以後其他的流服務操作呼叫出了問題。所以這個只能針對特定的操作有幫助。我把這個錯誤收集起來供大家參考。也希望發揮大家的作用把這個問題解決。

   也許這個錯誤應該反映給WCF的開發小組,無論國內還是國外的技術論壇,包括MSDN都有人遇到這樣的問題,而沒有一個最佳的解決方案。這裡我對流處理示例程式碼分別打包,目前都有問題。

<1>流處理機制示例1裡程式碼測試上傳檔案,成功,下載檔案錯誤。

/Files/frank_xl/WCFServiceStreamingFrankXuLei.rar

<2>這裡使用的是位元組陣列,測試下載檔案,下載檔案是成功的,上傳檔案失敗。/Files/frank_xl/WCFServiceStreamingByteArrayFrankXuLei.rar

 兩個失敗的原因都是一樣,套接字中斷,連線超時。WCF分散式開發常見錯誤解決(10):套接字連線中斷,The socket connection was aborted ,我進行了整理,也查詢了國外的論壇,沒有找到理想的解決辦法。我已經嘗試了修改接受時間的限制,但是不起作用。我會繼續關注這個問題,也希望有興趣的朋友補充。MSDN論壇上有人提供瞭解決的方法,但是不理想,更換協議。希望微軟WCF的開發、測試小組早日注意這個問題。

 參考文章:

1.《Programming WCF Services》;

2.WSE3.0構建Web服務安全(4):MTOM訊息傳輸優化和檔案上傳、下載


 

老徐的部落格

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12639172/viewspace-604819/,如需轉載,請註明出處,否則將追究法律責任。

相關文章