監(jiān)理公司管理系統(tǒng) | 工程企業(yè)管理系統(tǒng) | OA系統(tǒng) | ERP系統(tǒng) | 造價咨詢管理系統(tǒng) | 工程設(shè)計(jì)管理系統(tǒng) | 甲方項(xiàng)目管理系統(tǒng) | 簽約案例 | 客戶案例 | 在線試用
X 關(guān)閉
重慶OA行業(yè)資訊

當(dāng)前位置:工程項(xiàng)目OA系統(tǒng) > 泛普各地 > 重慶OA系統(tǒng) > 重慶OA行業(yè)資訊

可靠的XML Web Service

申請免費(fèi)試用、咨詢電話:400-8352-114

AMTeam.org

可靠的XML Web Service


Eric Schmidt

Microsoft Corporation,XML Core Services 組,項(xiàng)目經(jīng)理

2001 年 12 月 11 日

在 PDC 上,我談?wù)摿擞嘘P(guān)可靠的 XML Web Service(Web 服務(wù))的話題,這個話題源于過去一年來的多次交流。在有關(guān)建立 XML Web Service 的眾多常見問題中,可靠性問題是開發(fā)人員實(shí)現(xiàn)分散式 Web 服務(wù)所面臨的五個最重要的問題之一。如果分開來講,這個問題并不是太難,因此,本月我準(zhǔn)備談一談建立可靠的 XML Web Service 這一棘手的問題。

概述

Global XML Web Services Architecture(GXA [英文])最突出的一面就是可以使用可合成處理協(xié)議擴(kuò)展該體系結(jié)構(gòu)。這些協(xié)議主要通過 SOAP 標(biāo)頭實(shí)現(xiàn),可以提供包括安全性、加密、路由和可靠性的廣泛服務(wù)。當(dāng)您開始構(gòu)建基于 GXA 的應(yīng)用程序時,您將發(fā)現(xiàn) GXA 實(shí)質(zhì)上是一種消息處理體系結(jié)構(gòu),它通過基于標(biāo)準(zhǔn)的編碼技術(shù) (SOAP) 在系統(tǒng)和服務(wù)之間提供協(xié)同工作能力。到目前為止,大部分實(shí)現(xiàn)工作都集中在 SOAP 1.1 和 WSDL 兼容服務(wù)上,因此 Web 服務(wù)實(shí)現(xiàn)方案可以與多種語言和操作系統(tǒng)協(xié)同工作。

這是一個了不起的概念。任何兩個系統(tǒng)之間都能夠進(jìn)行交流,只要它們能夠分析 XML 并理解 SOAP 規(guī)范的規(guī)則。但是,簡單的消息交換并不能滿足復(fù)雜的業(yè)務(wù)應(yīng)用程序的需要。真正的應(yīng)用程序(不管其內(nèi)部域體系結(jié)構(gòu)如何)均需要標(biāo)準(zhǔn)化的服務(wù),例如處于 Web 服務(wù)消息處理層上的安全性、授權(quán)和可靠性。在 Global XML Web Services Architecture(具體地說就是 SOAP、SOAP 模塊和基礎(chǔ)結(jié)構(gòu)協(xié)議)的創(chuàng)建和實(shí)現(xiàn)背后有一個巨大的動力。隨著今年十月份四項(xiàng)新規(guī)范(WS-Routing、WS-Referral、WS-Licensing 和 WS-Security)的發(fā)布,我們已經(jīng)開始著手下一代 XML Web Service 實(shí)現(xiàn)工作。盡管發(fā)布了這么多的新規(guī)范,但仍有兩個領(lǐng)域尚無公共規(guī)范,即事務(wù)處理和可靠的消息處理,這主要是因?yàn)檫@些基礎(chǔ)結(jié)構(gòu)協(xié)議依賴于底層 SOAP 模塊。

本專欄主要從 GXA 環(huán)境的角度討論可靠性和可靠的消息處理的含義。而且我還要花一些時間探討通過在 .NET 框架中擴(kuò)展現(xiàn)有 Web 服務(wù)類來開發(fā)可靠性協(xié)議需要做些什么。本專欄有兩個主要目的:

讓讀者了解可靠性概念,為以后各種規(guī)范的實(shí)施做好準(zhǔn)備。請注意,本文不是規(guī)范,而只是一篇文章,旨在引發(fā)讀者思考下面要討論的問題。
說明 .NET 框架中 Web 服務(wù)和 SOAP 類強(qiáng)大的、基于標(biāo)準(zhǔn)的功能。

XML Web Service 的可靠性

我們把問題分開來講。我們前面講過,GXA 服務(wù)實(shí)現(xiàn)方案屬于消息處理服務(wù),它們需要在分散式環(huán)境中發(fā)送和接收基于標(biāo)準(zhǔn)的編碼消息。在 Web 服務(wù)實(shí)現(xiàn)方案中發(fā)送 SOAP 消息的主要傳輸協(xié)議是 HTTP,它易于實(shí)現(xiàn)和管理,但本身不可靠。我們無需深入探討 HTTP 不可靠的具體原因,但只要知道 HTTP 沒有基于標(biāo)準(zhǔn)的服務(wù)來保證終點(diǎn)或服務(wù)器能夠接收到請求就足夠了。盡管內(nèi)置的網(wǎng)絡(luò)層設(shè)備可以在發(fā)生一般災(zāi)難性故障(例如未找到資源)時產(chǎn)生錯誤,但是卻沒有機(jī)制可以確??蛻舳四軌蛞钥煽康姆绞浇邮照埱蠡蝽憫?yīng)。

通常是通過簡單的重新發(fā)送操作處理 HTTP 故障,但在業(yè)務(wù)處理環(huán)境中這既不利于提高效率也沒有效果。它導(dǎo)致不必要的通信量,并增加了重復(fù)交易的風(fēng)險。

目前市場上有多種能夠更有效解決此問題的消息處理技術(shù),包括傳輸協(xié)議(如 HTTPR)、企業(yè)基礎(chǔ)結(jié)構(gòu)(如 MSMQ 和 MQ Series)以及業(yè)務(wù)處理協(xié)議(如 ebXML)。盡管每種技術(shù)針對特定的實(shí)現(xiàn)方案各有優(yōu)點(diǎn),但都不能以可在所有傳輸協(xié)議上跨域應(yīng)用的可擴(kuò)展方式解決可靠性問題;而且,在消息交換和處理方面的功能層次上也不盡相同。

面對所有這些問題,我決定總結(jié)一個需求列表,看一看在 Web 服務(wù)環(huán)境中實(shí)現(xiàn)可靠性原型需要做哪些工作。

以下是我自己創(chuàng)建的可靠性層的主要需求列表:

基于標(biāo)準(zhǔn)并應(yīng)用于消息協(xié)議層

確認(rèn)發(fā)送

有序發(fā)送

對稱對話

加快異步處理

基于標(biāo)準(zhǔn)

該協(xié)議必須由現(xiàn)有的基于標(biāo)準(zhǔn)的技術(shù)組成。而且,該協(xié)議還應(yīng)該對 SOAP 1.1 規(guī)范進(jìn)行擴(kuò)展,然后應(yīng)用到消息編碼層而不是傳輸層。這樣,消息才能夠在所有可用的機(jī)制(從 HTTP 到某些專用套接字實(shí)現(xiàn)方案)上進(jìn)行傳輸。

確認(rèn)發(fā)送

為了有章可循,該協(xié)議必須采用某種發(fā)送確認(rèn)機(jī)制,也就是說,使用該協(xié)議發(fā)送的消息應(yīng)當(dāng)從處理器接收一個且只有一個關(guān)于該消息狀態(tài)的確認(rèn)信息。

有序發(fā)送

有序發(fā)送引入了對話概念,即客戶端和服務(wù)器可以交換消息和確認(rèn)(確認(rèn)也是可唯一標(biāo)識的對話的一部分)。收到消息后,將檢查消息以進(jìn)行排序,確保處理器收到一組有序的消息。

對稱對話

建立在對話機(jī)制之上的協(xié)議,還必須確保消息和確認(rèn)的對稱性。必須確保每條消息只被處理一次,并且只生成一個確認(rèn)。

加快異步處理

這是需求列表中最重要的一點(diǎn),所以留在最后說明。HTTP 是基于同步請求響應(yīng)模型的,適用于處理任務(wù)量小或運(yùn)行時間短的簡單應(yīng)用程序。Web 服務(wù)實(shí)現(xiàn)方案有一個不太高明的小秘密,即用戶不需要從處理的角度了解服務(wù)是如何在后端實(shí)現(xiàn)的。也就是說,Web 服務(wù)實(shí)現(xiàn)方案處理一個請求可能只需要三秒鐘,也可能會花上三個小時。這導(dǎo)致消息處理體系結(jié)構(gòu)效率低下,且無法擴(kuò)縮。我們需要的是一個能夠加快異步消息處理體系結(jié)構(gòu)的處理模型。但是要注意,異步模型實(shí)現(xiàn)方案要比緊耦合的請求響應(yīng)實(shí)現(xiàn)方案復(fù)雜得多,因?yàn)樗枰嗟幕A(chǔ)結(jié)構(gòu)。

我來解釋一下。在使用標(biāo)準(zhǔn) HTTP 的同步消息處理模型中,客戶端在向服務(wù)器發(fā)送請求后、從服務(wù)器接收到響應(yīng)之前一直保持停滯狀態(tài)。在這段時間內(nèi),可能會發(fā)生許多災(zāi)難性事件:

連接可能會因外部原因而斷開,從而丟失請求或響應(yīng)。

服務(wù)器可能會因脫機(jī)或過載而超時。

服務(wù)器進(jìn)程可能取決于下行服務(wù),而這種服務(wù)的響應(yīng)時間無法控制。

同步模型


圖 1:同步模型

不管問題是與網(wǎng)絡(luò)有關(guān)還是與應(yīng)用程序有關(guān),要確??煽啃远夹枰獙?shí)現(xiàn)某種附加協(xié)議驅(qū)動的基礎(chǔ)結(jié)構(gòu)。在本次討論中,我想著重講述消息是如何在傳輸層上進(jìn)行處理的。傳輸層是獨(dú)立于應(yīng)用程序的關(guān)鍵部分,使用它可以實(shí)現(xiàn)可靠性層,并將消息的最終處理過程分離出來。更具體一點(diǎn)來說,每一條消息(不管針對哪種應(yīng)用程序)都需要從網(wǎng)絡(luò)層上讀取,并分配至適當(dāng)?shù)膽?yīng)用程序資源。我們可以在這里添加一個發(fā)送可靠性確認(rèn)和執(zhí)行持續(xù)存儲的附加協(xié)議,這樣應(yīng)用程序資源就可以選擇處理該消息的時間和方式。而且,這個新協(xié)議可以幫助我們分離或加快異步處理模型。下面將解釋如何完成此過程。

在下面的異步模型中,某一請求被發(fā)送至 SOAP 服務(wù)器。服務(wù)器從網(wǎng)絡(luò)層上讀取該消息流,并立即向客戶端返回 HTTP 202 響應(yīng)。此進(jìn)程僅就向服務(wù)器發(fā)送消息的時間而言是同步的,這樣可以減少與連接有關(guān)的問題。到達(dá)服務(wù)器后,消息將被傳送到可靠性層,在這里進(jìn)行檢查以驗(yàn)證消息是否過期、重復(fù)和有序。然后,消息被持續(xù)存儲(在關(guān)系數(shù)據(jù)庫中),并向客戶端發(fā)送有關(guān)其狀態(tài)的確認(rèn)。最后,消息將被分配至正確的應(yīng)用程序功能。

異步模型


圖 2:異步模型

在 HTTP 環(huán)境中,您可以控制向客戶端發(fā)送響應(yīng)的時間。通過控制向客戶端發(fā)送響應(yīng)的時間,您可以將下行處理影響通信可靠性的風(fēng)險降到最低。在 SOAP 中,這是通過單向消息傳遞實(shí)現(xiàn)的。它指示底層 SOAP 處理器立刻向客戶端發(fā)送 HTTP 202 響應(yīng),通CRM/zhike/ target=_blank class=infotextkey>知客戶端已收到消息,并已成功地將消息分配給正確的資源進(jìn)行處理。之后,處理器向客戶端發(fā)送有關(guān)該消息狀態(tài)的響應(yīng)。本文稍后將對這種模型的優(yōu)點(diǎn)進(jìn)行詳細(xì)介紹。

建立可靠性層

明確了上述要求之后,我們來討論如何使用 .NET 框架為 Web 服務(wù)實(shí)現(xiàn)方案建立可靠性協(xié)議。根據(jù)上述要求,我建立了一個小型 API,以便提供可用的實(shí)現(xiàn)方案。

協(xié)議:ericRP

第一個問題是定義如何分解可靠性協(xié)議 (ericRP)。以下是該協(xié)議的關(guān)鍵之處:

該協(xié)議是用于教學(xué)的原型。

該協(xié)議主要是通過擴(kuò)展 SOAP 消息處理層在 SOAP 處理層上執(zhí)行的。

SOAP 標(biāo)頭用于對處理層所需信息進(jìn)行編碼。

該協(xié)議要求實(shí)現(xiàn)方案具有某種方式的持續(xù)存儲,以便記錄消息。本實(shí)現(xiàn)方案使用的是 Microsoft SQL Server 2000。

注意:不管采取哪種方式在 SOAP 環(huán)境中實(shí)現(xiàn)可靠性層,除了 SOAP 分析器之外,都還需要其他基礎(chǔ)結(jié)構(gòu)。

該協(xié)議支持對話概念,也就是說可以對多條消息進(jìn)行排序,從而保證有序的發(fā)送。

該協(xié)議的全部實(shí)現(xiàn)方案都由 ericRP 名稱空間限定。

ericRP 基于兩方對話方案,即兩個服務(wù)可以通過 XML Web Services 體系結(jié)構(gòu)(HTTP、SOAP 和 WSDL)進(jìn)行對話。

客戶端負(fù)責(zé)消息的所有更正。(本文后面有詳細(xì)論述)

服務(wù)器只負(fù)責(zé)基于特定標(biāo)準(zhǔn)發(fā)送確認(rèn)。

服務(wù)器不記錄收到的過期消息。

服務(wù)器不記錄收到的無序消息。

服務(wù)器不記錄收到的重復(fù)消息。

處理 API

在這個原型中,我建立了六個主要的類和一個小型數(shù)據(jù)庫。我將類稱為處理 API。Web 服務(wù)客戶端和服務(wù)器將使用這些類監(jiān)控和更正使用 ericRP 可靠性協(xié)議的消息。所有的類都屬于 ericRP 名稱空間:

Client.ConversationManager:由客戶端使用,創(chuàng)建 Web 服務(wù)消息關(guān)聯(lián)和消息監(jiān)控的對話環(huán)境。

Client.RPClientTrace:由 Web 服務(wù)客戶端使用,這些客戶端的方法對出站消息執(zhí)行 ericRP 可靠性協(xié)議。

Server.ConversationManager:由 Web 服務(wù)服務(wù)器使用,記錄并處理入站消息。

Server.RPServerTrace:由 Web 服務(wù)服務(wù)器使用,這些服務(wù)器的方法對入站消息執(zhí)行 ericRP 可靠性協(xié)議。

ReliabilityInfo:具有雙重作用。它可以由 Client.ConversationManager 使用,為記錄提供可靠性信息;也可以由 Web 服務(wù)客戶端代理使用,為出站消息創(chuàng)建必要的 SOAP 標(biāo)頭信息。

Acknowledgment:由 Server.ConversationManager 使用,向客戶端發(fā)送確認(rèn)。

ericRP 的工作原理

在查看代碼之前,我想先從用戶的角度說明該協(xié)議的工作原理。例如,我有一個簡單的 Web 服務(wù)代理類,通過它可以向 Web 服務(wù)發(fā)送訂單消息。打算使用 API 的客戶端需要執(zhí)行以下操作:

首先,創(chuàng)建 Client.ConversationManager 類的實(shí)例并開始一個新對話。例如:

private void begin()
{
rpClient = new ericRP.Client.ConversationManager();

rpClient.MessageSent += _
   new ericRP.Client.ConversationManager.MessageSentEventHandler(process);

rpClient.ConversationStarted += new _
   ericRP.Client.ConversationManager.ConversationStartedHandler(constarted);
  
rpClient.BeginConversation();
}

rpClient 變量在類級別內(nèi)有效,稍后會用到。我還設(shè)置了一些事件處理程序。

下一步,使用訂單代理并配合 ReliabilityInfo 類,發(fā)送一條可靠的信息。先創(chuàng)建 PurchaseOrderProxy 的實(shí)例,就象通常為 Web 服務(wù)客戶端所做的操作一樣。再創(chuàng)建 ReliabiltiyInfo 類的實(shí)例,將 ConversationManager 傳送給構(gòu)造函數(shù),然后設(shè)置可靠性屬性。需要特別注意的屬性是 MaxRetry、ExpireDate 和 AckURL。MaxRetry 和 ExpireDate 用于限制消息的活動,防止它無限制地發(fā)送;Web 服務(wù)將在向客戶端發(fā)送接收確認(rèn)時使用 AckURL。設(shè)置完這些屬性后,即可設(shè)置代理的 ReliableHeader 屬性并調(diào)用所需的方法。

private void sendMessage()
{
ClientProxies.PurchaseOrderProxy po = new ClientProxies.PurchaseOrderProxy();
        
   ericRP.ReliabilityInfo rInfo = new ericRP.ReliabilityInfo(rpClient);
   rInfo.Status = ReliabilityInfo.MessageStatus.New;
   rInfo.SendDate = System.DateTime.Now;
   rInfo.ExpireDate = System.DateTime.Now.AddHours(4);
   rInfo.MaxRetry = 5;
   rInfo.AckURL = "http://localhost:8082/ericRPAck/POAck.asmx";
        
   po.ReliableHeader = rInfo;
   po.SubmitMessage("非常希望他們得到此訂單!");
}

這是為了說明該功能而編寫的一段客戶端測試程序的屏幕快照。注意,我們一共發(fā)送了五條消息。第三條消息在到達(dá)目的地之前已過期,按照 ericRP 協(xié)議,這條消息將被丟棄,服務(wù)器不對其進(jìn)行處理。第四條消息是無序消息,因?yàn)榉?wù)器并沒有收到有效的第三條消息。在重新發(fā)送第三條消息之前,任何后續(xù)消息都是無序的。如果重新查詢 Client.ConversationManager,您將發(fā)現(xiàn)第五條消息也是無序的。

圖 3:客戶端測試程序

標(biāo)頭的作用

在查看代碼之前,我們需要了解一下 SOAP 主題,即標(biāo)頭。SOAP 1.1 規(guī)范中談?wù)撟钌俚膬?nèi)容之一就是 SOAP 標(biāo)頭。標(biāo)頭提供了一種擴(kuò)展消息處理體系結(jié)構(gòu)的簡單方法。SOAP 1.1 規(guī)范中提到:標(biāo)頭在實(shí)現(xiàn)與消息主體沒有特定關(guān)系的處理規(guī)則(例如驗(yàn)證和事務(wù)管理)時非常有用。對任何類型的消息來說,SOAP 標(biāo)頭都是以獨(dú)立方式對可靠性信息進(jìn)行編碼的完美解決方案。規(guī)范中還概述了實(shí)施和處理這些標(biāo)頭的標(biāo)準(zhǔn)和規(guī)則。

下面我們來看看如何實(shí)現(xiàn)包含可靠性信息的 SOAP 標(biāo)頭。首先要為標(biāo)頭確定架構(gòu)。這很重要,因?yàn)樗亲罱K用戶在支持該標(biāo)頭的 Web 服務(wù)的 WSDL 文件中看到的實(shí)際效果。對于此實(shí)現(xiàn)方案,我直接將處理 API 映射到 SOAP 標(biāo)頭。

我來解釋一下。在我的處理 API 中有一個名為 ReliabilityInfo 的類。對此類進(jìn)行實(shí)例化時,它將在運(yùn)行時變成動態(tài)對象。也就是說,您可以從可靠性的角度設(shè)置確定如何處理出站消息的屬性。此對象還可以在運(yùn)行時序列化為 SOAP 標(biāo)頭。下面是該類及其成員的一個快照。為了清楚起見,我刪除了具體的實(shí)現(xiàn)和私有成員。

[XmlRootAttribute(ElementName="ReliableHeader", _
   Namespace="http://ericRP/ReliableHeader/2001/", IsNullable=false)]
   public class ReliabilityInfo : SoapHeader
   {     
      public string Destination{}
      public string ConversationId{}
      public int MessageId{}
      public MessageStatus Status{}
      public DateTime SendDate{}
      public DateTime ExpireDate{}
      public string AckURL{}
public enum MessageStatus{}

[XmlIgnore]
      public int MaxRetry{}
      [XmlIgnore]
      public string Text{}
   }

序列化為出站 SOAP 消息時,標(biāo)頭如下所示:

<soap:Header>
<ReliableHeader xmlns="http://ericRP/ReliableHeader/2001/">
<ConversationId>b9e029e1-af0f-42cb-83b0-7888f9e3ffc4</ConversationId>
<MessageId>1</MessageId>
<Status>新</Status>
<SendDate>2001-11-06T14:59:02.1021226-08:00</SendDate>
<ExpireDate>2001-11-06T18:59:02.1021226-08:00</ExpireDate>
<AckURL>http://localhost:8082/ericRPAck/POAck.asmx</AckURL>
</ReliableHeader>
</soap:Header>

這是通過 .NET 框架中兩個非常重要的類實(shí)現(xiàn)的?;?XML 序列化塊使用 System.Xml.Serialization 名稱空間類來進(jìn)行處理。這兩個類一個是 XmlRootAttribute 屬性類,我使用它告訴序列化程序?qū)⑽臋n碎片的根稱為 ReliableHeader,并將它與名稱空間相關(guān)聯(lián)。此后,所有公共成員也同樣會被序列化,除非您告訴序列化程序忽略該成員。我使用的另一個重要名稱空間是 System.Web.Services.Protocols。具體地說,即 SoapHeader 類。從該類進(jìn)行繼承時,它會自動將序列化的 XML 加入到 SOAP 消息中。本文稍后將論述如何將標(biāo)頭加入消息。

這非常強(qiáng)大,因?yàn)槲也粌H可以使用類型明確的、編譯好的對象作為標(biāo)頭的基礎(chǔ),而且在這些類的成員內(nèi)部還有特定的實(shí)現(xiàn)。

綜述
好啦,上面只是對可靠性進(jìn)行了簡單的論述,并闡述了我自己的規(guī)范。下面讓我們看一看它的代碼。

擴(kuò)展 Web 服務(wù)客戶端代理

第一步是重新設(shè)置現(xiàn)有的 .NET Web 服務(wù)客戶端代理。記住,此協(xié)議可以在任何 SOAP 處理引擎中實(shí)現(xiàn)。我選擇了 .NET 框架,因?yàn)樗子谑褂?、基于?biāo)準(zhǔn)且可擴(kuò)展。

選取一個現(xiàn)有的 Web 服務(wù)客戶端代理(例如 PurchaseOrderProxy),然后添加以下代碼:

該類中必須有名為 ReliableHeader 的公共成員,并且其類型必須為 ericRP.ReliabilityInfo。此成員將在運(yùn)行時通過調(diào)用客戶端進(jìn)行設(shè)置。稍后將使用此成員為出站消息提供標(biāo)頭信息,并提供在跟蹤過程中應(yīng)用了該標(biāo)頭的消息的狀態(tài)信息。例如:
public class PurchaseOrderProxy :
      System.Web.Services.Protocols.SoapHttpClientProtocol
   {
      public ReliabilityInfo ReliableHeader;
     
      //為了清楚起見,此處省略了其他代碼
   }

在 Web 服務(wù)客戶端代理中調(diào)用的方法必須使用以下屬性進(jìn)行批注:
[SoapHeader("ReliableHeader", Required=true)]

在序列化過程中,此屬性將在運(yùn)行時把適當(dāng)?shù)臉?biāo)頭值加入到出站 SOAP 消息中。此處的 SubmitMessage 方法使用 SoapHeader 屬性進(jìn)行標(biāo)記。注意,ReliableHeader 是在步驟 1 中實(shí)現(xiàn)的成員,而且是必須的。也就是說,如果不在運(yùn)行時設(shè)置此成員,將產(chǎn)生異常。例如:

[SoapHeader("ReliableHeader", Required=true)]
public void SubmitMessage(object message)
{
this.Invoke("SubmitMessage", new object[] {message});     
}
  
在 Web 服務(wù)客戶端代理中調(diào)用的方法必須包含以下屬性:
[ericRP.Client.RPClientTrace.TraceExtension()]

此屬性表示該方法支持自定義 SOAP 擴(kuò)展。在消息被序列化之前和之后、且在消息被發(fā)送至底層傳輸機(jī)制之前,這種 SOAP 擴(kuò)展將在運(yùn)行時被調(diào)用。我通常是在消息從客戶端計(jì)算機(jī)發(fā)送出去之前,對它進(jìn)行一些簡單的跟蹤和記錄。稍后再查看此擴(kuò)展的實(shí)現(xiàn)情況。例如:

[ericRP.Client.RPClientTrace.TraceExtension()]
[SoapHeaderAttribute("ReliableHeader", Required=true)]
public void SubmitMessage(object message)
{
this.Invoke("SubmitMessage", new object[] {message});     
}

該類必須從 ericRP.Client.RPClientTrace.IClientTrace 實(shí)現(xiàn)。這種接口實(shí)現(xiàn)方案提供了基本的跟蹤功能,以檢查調(diào)用程序是否支持特定的跟蹤協(xié)議。在后面的跟蹤功能中可以看到此代碼。例如:
public class PurchaseOrderProxy :
   System.Web.Services.Protocols.SoapHttpClientProtocol,
   ericRP.Client.RPClientTrace.IClientTrace
{
public ReliabilityInfo ReliableHeader;
   }

最后,該類必須實(shí)現(xiàn) IClientTrace.GetReliabilityInfo 函數(shù),該函數(shù)是 IClientTrace 接口要求的唯一函數(shù)。這是一個簡單的機(jī)制,客戶端可以使用它在運(yùn)行時將消息的狀態(tài)信息發(fā)送到跟蹤服務(wù)。例如:

public class PurchaseOrderProxy :
   System.Web.Services.Protocols.SoapHttpClientProtocol,
   ericRP.Client.RPClientTrace.IClientTrace
{
public ReliabilityInfo ReliableHeader;

ericRP.ReliabilityInfo ericRP.Client.RPClientTrace.IClientTrace.GetReliabilityInfo()
{     
return ReliableHeader;
}
   }

看起來代碼可能很多,但實(shí)際上卻很簡單。事實(shí)上,如果時間再多一些,我可以創(chuàng)建從 SoapHttpClientProtocol 繼承的新類并明確加以實(shí)現(xiàn),但這種方式對于服務(wù)器端來說會更有趣。

擴(kuò)展 Web 服務(wù)服務(wù)器存根

下一步,我們來擴(kuò)展現(xiàn)有的 Web 服務(wù)服務(wù)器存根。選取一個現(xiàn)有的 .NET Web 服務(wù)類(例如 ProcessPurchaseOrder),然后添加以下代碼:

該類中必須有一個類型為 ericRP.ReliabilityInfo 的公共成員 ReliableHeader。稍后將使用此成員為入站消息提供反序列化標(biāo)頭信息,并提供在跟蹤過程中應(yīng)用了該標(biāo)頭的消息的狀態(tài)信息。例如:

public class ProcessPurchaseOrder :
      System.Web.Services.WebService
   {
      public ReliabilityInfo ReliableHeader;
   }

在 Web 服務(wù)存根中調(diào)用的方法必須使用以下屬性進(jìn)行批注:

[SoapHeader("ReliableHeader", Required=true)]

在反序列化過程中,此屬性將在運(yùn)行時對入站 SOAP 消息中適當(dāng)?shù)臉?biāo)頭值進(jìn)行反序列化。此處的 SubmitMessage 方法使用 SoapHeader 屬性進(jìn)行標(biāo)記。例如:

[WebMethod]
[SoapHeader("ReliableHeader", Required=true)]
public void SubmitMessage(object message)
{
//為了清楚起見,此處省略了一些代碼  
}

在 Web 服務(wù)存根中調(diào)用的方法必須使用以下屬性進(jìn)行批注:
[ericRP.Server.RPServerTrace.TraceExtension()]

此屬性表示該方法支持自定義 SOAP 擴(kuò)展。稍后再查看此擴(kuò)展的實(shí)現(xiàn)情況。

注意:此擴(kuò)展與客戶端擴(kuò)展不同。
例如:

[SoapHeader("ReliableHeader", Required=true)]
[ericRP.Server.RPServerTrace.TraceExtension()]
public void SubmitMessage(object message)
{
//為了清楚起見,此處省略了一些代碼  
}

在 Web 服務(wù)存根中調(diào)用的方法必須使用以下屬性進(jìn)行批注:
[SoapDocumentMethod(OneWay=true)]

此屬性表示被調(diào)用的函數(shù)不返回值。更具體地說,即一旦消息被反序列化,此屬性就會強(qiáng)制 Web 服務(wù)向客戶端返回 HTTP 202 響應(yīng)。對于分離消息的最終處理來說,這不失為一個有效的機(jī)制,否則,客戶端就不得不同步地等待服務(wù)返回響應(yīng)。例如:

[WebMethod]
[SoapDocumentMethod(OneWay=true)]
[SoapHeader("ReliableHeader", Required=true)]
[ericRP.Server.RPServerTrace.TraceExtension()]
public void SubmitMessage(object message)
{
//為了清楚起見,此處省略了一些代碼  
}

該類必須從 ericRP.Server.RPServerTrace.IServerTrace 實(shí)現(xiàn)。這種接口實(shí)現(xiàn)方案提供了基本的跟蹤功能,以檢查調(diào)用程序是否支持特定的跟蹤協(xié)議。在后面的跟蹤功能中可以看到此代碼。例如:
public class ProcessPurchaseOrder :
System.Web.Services.WebService, ericRP.Server.RPServerTrace.IServerTrace
   {
      public ReliabilityInfo ReliableHeader;
   }

最后,該類必須實(shí)現(xiàn) IServerTrace.GetReliabilityInfo 函數(shù)。這是 IServerTrace 接口要求的唯一函數(shù)。例如:
public class ProcessPurchaseOrder :
System.Web.Services.WebService, ericRP.Server.RPServerTrace.IServerTrace
   {
      public ReliabilityInfo ReliableHeader;

ericRP.ReliabilityInfo ericRP.Server.RPServerTrace.IServerTrace.GetReliabilityInfo()
      {  
         return ReliableHeader;
      }
   }

好啦,現(xiàn)有的 Web 服務(wù)客戶端和服務(wù)器已準(zhǔn)備就緒。下面我們來看看 SoapExtension 是如何工作的。

查看 RPClientTrance 和 RPServerTrace

為了實(shí)現(xiàn)可靠性處理,我決定使用 SoapExtension。SoapExtension 是一個可繼承的基類,使用它可以跟蹤 SOAP 消息的出站序列化和入站反序列化。正是在這個跟蹤過程中,對消息進(jìn)行記錄并檢查其狀態(tài)。記得前面講過,Web 服務(wù)客戶端代理方法實(shí)現(xiàn)
[ericRP.Client.RPClientTrace.TraceExtension()]。當(dāng)調(diào)用該方法時,此屬性將在 SOAP 消息出站序列化時調(diào)用以下代碼。

需要特別指出的主要函數(shù)是 ProcessMessage。發(fā)送出站消息時,ProcessMessage 將提供有關(guān)該消息序列化之前和之后的全部狀態(tài)信息。這時,將檢查誰在調(diào)用并將 Client 屬性的類級別成員與當(dāng)前消息分離。然后檢查消息是否處于 AfterSerialize 狀態(tài)。如果已經(jīng)序列化,則可以在消息被發(fā)送至服務(wù)器之前進(jìn)行記錄。通過名為 ProcessOutgoingMessageText 的自定義函數(shù),首先進(jìn)行一些流交換以免破壞底層消息流。然后檢查客戶端是否支持 IClientTrace 接口。如果客戶端支持該接口,則表明它們也支持可靠性協(xié)議。通過接口檢查功能,可以調(diào)用 GetReliabilityInfo 以便將當(dāng)前消息返回可應(yīng)用于該消息的 ConversationManager,然后設(shè)置一些屬性并調(diào)用 LogMessage。LogMessage 將當(dāng)前消息信息寫入本地?cái)?shù)據(jù)庫,然后發(fā)送事件,通知客戶端已記錄該消息。

public class RPClientTrace : SoapExtension
{     
 public override void ProcessMessage(SoapMessage message)
 {
  SoapClientMessage tmpMsg = (SoapClientMessage)message;
  _client = tmpMsg.Client;

  if (message.Stage == SoapMessageStage.AfterSerialize)
  {
    ProcessOutgoingMessageText(message);
  }
 }

  public void ProcessOutgoingMessageText(SoapMessage message)
  {
   newStream.Position = 0;
   TextReader reader = new StreamReader(newStream);
   StringBuilder strMessage = new StringBuilder();
   strMessage.Append(reader.ReadToEnd());
   newStream.Position = 0;
   Copy(newStream, oldStream);

   if(_client is Client.RPClientTrace.IClientTrace)
   {
    try
    {
Client.RPClientTrace.IClientTrace _ptrClient = _
   (Client.RPClientTrace.IClientTrace)_client;
   ReliabilityInfo rInfo = _ptrClient.GetReliabilityInfo();
      rInfo.Text = strMessage.ToString();
      rInfo.Destination = message.Url;
   rInfo.Manager.LogMessage(rInfo);
     }
     catch(Exception e)
     {
      throw e;
     }
    }
   }
}

服務(wù)器端的情況有點(diǎn)復(fù)雜,因?yàn)榉?wù)器需要為入站消息執(zhí)行額外的操作。被調(diào)用的 Web 方法用于實(shí)現(xiàn) [ericRP.Server.RPServerTrace.TraceExtension()]。以下代碼將在入站消息反序列化之前和之后被調(diào)用:

public class RPServerTrace : SoapExtension
{
public override void ProcessMessage(SoapMessage message)
{
 try
 {
  switch (message.Stage)
  {
   case SoapMessageStage.BeforeDeserialize:
        ReadIncomingMessageText(message);
    break;
   case SoapMessageStage.AfterDeserialize:
     SoapServerMessage tmpMsg = (SoapServerMessage)message;
     _server = tmpMsg.Server;
     if(_server is Server.RPServerTrace.IServerTrace)
     {
          Server.RPServerTrace.IServerTrace _ptrServer = _
             (Server.RPServerTrace.IServerTrace)_server;
     ReliabilityInfo rInfo = _ptrServer.GetReliabilityInfo();
   ericRP.ReliabilityInfo tempInfo = (ericRP.ReliabilityInfo)message.Headers[0];
   tempInfo.Text = _tempMessage;
   Server.ConversationManager manager = new Server.ConversationManager();
   manager.ProcessMessage(tempInfo);

     }
   break;
   }
}

進(jìn)行流交換和接口檢查后,將在服務(wù)器對話管理器上調(diào)用 ProcessInboundMessage。ProcessInboundMessage 用于檢查核心消息的狀態(tài)。消息限制程序即在此使用。首先檢查消息是否過期,然后檢查其是否重復(fù),最后檢查其是否有序。如果滿足所有三個條件,則將記錄該消息并向客戶端發(fā)送確認(rèn);如果不能滿足任一條件,則將更改消息的狀態(tài)并向客戶端發(fā)送確認(rèn)。

public void ProcessInboundMessage(ericRP.ReliabilityInfo rInfo)
{        
    try
    {
   if(IsExpired(rInfo))
   {
   rInfo.Status = ericRP.ReliabilityInfo.MessageStatus.Expired;
   SendAck(rInfo);
   }
   else if(IsDulplicate(rInfo))
   {
   rInfo.Status = ericRP.ReliabilityInfo.MessageStatus.Duplicate;
   SendAck(rInfo);
   }
   else if(IsNotOrdered(rInfo))
   {
   rInfo.Status = ericRP.ReliabilityInfo.MessageStatus.NotOrdered;
   SendAck(rInfo);
   }
   else
{
   rInfo.Status = ericRP.ReliabilityInfo.MessageStatus.Success;
   LogMessage(rInfo);
   SendAck(rInfo);
   }
    }
    catch(Exception e)
    {
   throw e;
    }
  }
}

要使本示例在生產(chǎn)環(huán)境中可行,還需要做大量的實(shí)施工作。更重要的是,API 應(yīng)當(dāng)基于一個以后將會發(fā)布的公共標(biāo)準(zhǔn)。本文的主要目的就是引發(fā)讀者思考問題,了解一下 .NET 框架中的主要 Web 服務(wù)類,我想這兩個目的都已經(jīng)達(dá)到。如果您正在尋求可靠異步消息處理的成熟可用的實(shí)現(xiàn)方案,建議您看一看 Microsoft BizTalk? Server 2000。

最后,您可以使用類似的跟蹤功能執(zhí)行所有類型的操作,例如加密、客戶分配和通知。記住,這項(xiàng)附加的功能增加了 Web 服務(wù)所需的處理基礎(chǔ)結(jié)構(gòu)。

展望

無論是在規(guī)范還是在實(shí)現(xiàn)方面,XML Web Service 的未來都是光明的。Microsoft 將以協(xié)作的、標(biāo)準(zhǔn)驅(qū)動的方式工作,確保 XML Web Service 成為編寫松散耦合的分散式應(yīng)用程序的最佳體系結(jié)構(gòu)??煽康南⑻幚砗褪聞?wù)規(guī)范將在以后發(fā)布。盡管還有大量的實(shí)現(xiàn)工作要做,但您現(xiàn)在就可以開始使用 SOAP 和 WSDL 構(gòu)建您的框架。與公共規(guī)范進(jìn)程越接近,以后的改動工作就越容易進(jìn)行。SOAP 規(guī)范就是這種概念的一個顯著例子。世界各地的開發(fā)人員都可以跟蹤 SOAP 規(guī)范的發(fā)展進(jìn)程,因此可以毫不費(fèi)力地對自己的實(shí)現(xiàn)方案進(jìn)行相應(yīng)地調(diào)整。

希望我能有一個水晶球,為您準(zhǔn)確地描繪未來 XML Web Service 的基礎(chǔ)結(jié)構(gòu);希望我們目前依賴的所有公用服務(wù),例如安全性、事務(wù)、存儲、查詢、路由、進(jìn)程協(xié)調(diào)等等,將來都能夠直接映射至 Web 服務(wù)體系結(jié)構(gòu);而且 GXA 實(shí)現(xiàn)方案可以滲透到核心開發(fā)語言、應(yīng)用程序框架、業(yè)務(wù)處理框架和企業(yè)基礎(chǔ)結(jié)構(gòu)中。到那時,我們就能夠真正地以無縫方式將任意兩種服務(wù)耦合在一起。但愿 ericRP 不會真的成為服務(wù)背后的可靠性層。<微笑>

發(fā)布:2007-03-25 10:27    編輯:泛普軟件 · xiaona    [打印此頁]    [關(guān)閉]
相關(guān)文章: