程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> IIS 7.0使用WAS擴展HTTP之外的WCF服務

IIS 7.0使用WAS擴展HTTP之外的WCF服務

編輯:關於.NET

本文以 Windows Server 2008 的預發布版為基礎。文中包含的所有信息均有 可能變更。

本文討論:

IIS 6.0 和 IIS 7.0 的體系結構和進程模型

IIS 6.0 如何承載 Web 服務

用 IIS 7.0 承載可靠的 WCF 服務

Windows Process Activation Service (WAS) 工作原理

支持非 HTTP 協議

本文使用了以下技術:

IIS,WAS

目錄

IIS 6.0 體系結構和進程模型

IIS 7.0 和 WAS

深入了解 WAS

配置和多協議尋址

偵聽器如何分辨偵聽

通過非 HTTP 協議執行基於消息的激活

工作進程初始化

將數據從偵聽器傳送到工作進程

在 WAS 中承載 WCF 服務

WAS 承載的服務的生存期管理

自動設置 WAS 承載的服務

擴展 WAS

過去幾年裡我們聽說的所有關於面向服務的應用程序,已經促使產生了用於 設計、構建和部署面向服務的連接系統的真實框架、運行時和其他實用的工具。 Windows® Communication Foundation (WCF) 就是一個很好的例子,它允許 您使用托管代碼創建服務和服務使用者。

WCF 的一個優勢是讓您可以在任何 Windows 進程中承載基於 WCF 的服務, 這些進程包括控制台應用程序、Windows 窗體應用程序或 Windows Presentation Foundation (WPF) UI 應用程序。您甚至可以在長時間運行的 Windows NT® 服務(這些在後台運行的服務代表已配置的標識運作)中自承 載 WCF 服務。具有基於 HTTP 的終結點的 WCF 服務也可以承載於 IIS 內部, 這與由 ASP.NET 和 ASMX 實現的傳統 Web 服務非常相似。

如果您希望通過 IIS 7.0 提供可靠的 WCF 服務,那麼必須要了解 IIS 的一 項新功能 — Windows Process Activation Service (WAS)。WAS 是 IIS 7.0 的一個基本組件,它使不需安裝整個 IIS 軟件包就能承載 HTTP 以外的 WCF 服務得以實現。

在我們深入討論 WAS 並向您展示如何在自己的應用程序中使用它之前,先讓 我們看一下 IIS 6.0 承載的工作原理,並了解為什麼 IIS 7.0 中的新進程更加 優秀。

IIS 6.0 體系結構和進程模型

要了解 Windows Vista® 和 Windows Server® 2008 的新進程激活 機制,需要先熟悉 IIS 6.0 體系結構和進程模型。Windows Server 2003 和 IIS 6.0 的體系結構分為兩個基本部分:一個偵聽器進程和一組工作進程。IIS 6.0 偵聽器進程由 w3svc 服務實現,該服務是一個長時間運行的 Windows NT 服務,由 Windows 服務控制管理器 (SCM) 激活。該偵聽器進程等待通過 HTTP 到達的消息,再將消息分派至適當的工作進程 (w3wp.exe),該工作進程承載將 最終處理請求的應用程序代碼。

當請求到達網絡時,由內核模式的 HTTP 堆棧 (http.sys) 處理該請求,之 後該請求被傳送至偵聽器進程。然後,w3svc 進程會查看請求的 URI,並利用它 將請求映射到一個位於特定 IIS 應用程序池(該池承載於該工作進程 (w3wp) 的一個實例內)內部的特定 IIS 應用程序,如圖 1 所示。請求 URI 與該應用 程序間的映射以存儲在 IIS 元數據庫中的配置信息為基礎,可分別在 metabase.xml 和 mbschema.xml 中找到,這兩者都存儲在 %windir%\system32\inetsrv 文件夾中。一旦 w3svc 確定了目標應用程序,它就可以在一 些內部數據結構中查詢,以解析目標工作進程和應用程序池。

圖 1IIS 6.0 基礎體系結構

根據目標 IIS 服務器的狀態,此時可能會發生以下情形之一。如果由於已為 應用程序池接收了上一個請求而使工作進程和應用程序池已在運行,那就不需要 激活。所以,該請求僅被分派到等待中的工作進程。如果沒有工作進程處理當前 請求,偵聽器進程必須在分派請求前創建一個新的 w3wp 實例。

IIS 6.0 工作進程是一種輕型可執行文件。當它開始對一個激活請求做出響 應時,首先會加載一個簡單的非托管填充程序 DLL (w3wphost.dll) 來支持 w3svc 與工作進程通信。該填充程序還負責加載 aspnet_isapi.dll,該文件會 實現 ASP.NET 的托管組件和 IIS 的非托管組件之間的接口。

Aspnet_isapi.dll 負責將公共語言運行時 (CLR) 載入工作進程並創建一個 默認的應用程序域,ASP.NET 的托管宿主組件將位於此域中。這些托管宿主組件 負責按需創建附加的應用程序域(每個 IIS 應用程序對應一個域),並基於請 求的 URL 將請求傳送給它們。

IIS 7.0 和 WAS

如果您理解 IIS 6.0 進程模型,就能理解 IIS 7.0 和 WAS。IIS 6.0 的所 有主要體系結構組件(偵聽器、工作進程和應用程序管理器)在 WAS 中也存在 。不同的是,IIS 7.0 和 WAS 實現支持非 HTTP 情形,這可能非常適合基於服 務的應用程序體系結構。

可管理性、可靠性和可伸縮性與運行於 Windows Server 2003 的 IIS 6.0 中的 WAS 完全相同。但在 IIS 6.0 中僅對基於 HTTP 的應用程序有效的可解決 按需激活、進程運行狀況監控、企業級可管理性、快速故障保護等問題的所有 IIS 出色功能,現在對非基於 HTTP 的應用程序和服務同樣適用。

深入了解 WAS

在 IIS 6.0 中,w3svc 服務實際上承擔著雙重任務。它在 http.sys 中注冊 ,並且是傳入的 HTTP 通信的直接接收者,因此它充當 HTTP 偵聽器。同時,它 還擁有進程激活組件,負責正確啟動 w3wp 的新實例以及分派請求。在 IIS 7.0 中,這兩種責任被重構給了不同的 Windows NT 服務。w3svc 進程保留了其作為 HTTP 偵聽器的角色,但負責配置和進程激活的組件被作為因子之一計入 WAS, WAS 包括三個部分:配置管理器、進程管理器、非托管偵聽器適配器接口。

配置管理器從 applicationhost.config(IIS 7.0 中針對元數據庫的替換文 件)中讀取應用程序和應用程序池配置。進程管理器將應用程序池映射到現有的 工作進程,並負責生成 w3wp 的新實例,以承載新的應用程序池來響應激活請求 。非托管偵聽器適配器接口定義外部偵聽器如何將接收到的激活請求傳送給 WAS 。

w3svc 服務負責與內核級 http.sys 通信,並通過偵聽器適配器接口將 HTTP 激活請求傳送給 WAS。

WCF 利用偵聽器適配器接口,傳送通過支持的非 HTTP 協議(即 TCP、命名 管道和 MSMQ)收到的激活請求。通過非 HTTP 協議實際接收請求的 WCF 管道承 載於 SMSvcHost.exe 內部,SMSvcHost.exe 承載以下四個長時間運行的 Windows NT 服務:NetTcpPortSharing、NetTcpActivator、NetPipeActivator 和 NetMsmqActivator(參見圖 2)。

圖 2IIS 7.0 和 WAS 基礎體系結構

NetTcpPortSharing 是 WCF TCP 端口共享服務。它會實現一個集中的 TCP 偵聽器,使得多個進程可以在同一 TCP 端口上偵聽。即使未安裝 IIS 7.0 也可 使用該服務。

NetTcpActivator 是 WCF TCP 激活服務。它會將 TCP 激活請求傳送到 WAS 。

NetPipeActivator 是 WCF 命名的管道激活服務,會將命名的管道激活請求 傳送到 WAS。

NetMsmqActivator 是 WCF MSMQ 激活服務;它會將 MSMQ 激活請求傳送到 WAS。

雖然這些服務全都位於同一二進制文件中,但它們是不同的 Windows NT 服 務,可以單獨停止和啟動,以減少攻擊面和系統開銷。它們都是偵聽器服務的示 例,行為方式也都相似。基於此原因,我們將著重探討 TCP 激活,它也能夠用 作命名管道和 MSMQ 的示例。唯一的區別在於兩種情況下使用的網絡資源的特定 類型。

配置和多協議尋址

要使某個應用程序通過 WAS 激活,首先需要對該應用程序進行配置。WAS 中 承載的每個應用程序都有一個相應的配置項,位於%windir%\system32\inetsrv\config\applicationhost.config。此信息包括將承載該應用程 序的應用程序池、可唯一標識該應用程序的 URL 片段等項目。我們稍後會提供 相應的示例。與 IIS 6.0 非常相似,每個應用程序都關聯到一個應用程序池和 一個站點。每個站點都有一個其支持的網絡協議的地址綁定集。例如,管理員可 以對默認站點進行配置,將其綁定到 HTTP 端口 80 和 TCP 端口 7777。該站點 可能承載位於不同應用程序池的兩個應用程序(分別在 /Foo 和 /Bar 上進行偵 聽)。在此配置中,應用程序 /Foo 將偵聽 http://myserver.com/Foo 和 net.tcp://myserver.com:2323/Foo,而 /Bar 將偵聽 http://myserver.com/Bar 和 net.tcp://myserver.com:2323/Bar。不管請求的 接收方式如何(由於每個應用程序池都獲得其各自的工作進程,因此接收方式不 同),所有發送到 /Foo 的請求都將轉到工作進程 1,而所有發送到 /Bar 的請 求都將轉到工作進程 2。一般來說,應用程序站點控制與該應用程序相關聯的一 組網絡地址,而它的應用程序池則控制將承載它的工作進程實例。注意,只有當 您不使用 Web 園時,每個應用程序池對應一個工作進程的規則才有效。在 Web 園情況下,每個應用程序池都映射到它們自己的一組工作進程,進程數最多可為 應用程序池配置中指定的工作進程的最大數量。

偵聽器如何分辨偵聽

偵聽器需要接收消息。為此,它需要打開一個套接字(或一個管道句柄,或 啟動一個 MSMQ 讀取等)。然而,為了接收正確的消息,它需要從 WAS 取得必 要的尋址信息。該過程在偵聽器啟動時完成。協議的偵聽器適配器在 WAS 偵聽 器適配器接口調用一個函數,並實質上說明:“我現在正在 net.tcp 協議 進行偵聽,請使用我傳給您的這組回調函數,告訴我需要知道些什麼。” 作為響應,WAS 將利用它所擁有的任何配置,為設置為通過相關協議接受消息的 應用程序進行回調。對於上面的例子,TCP 偵聽器將被告知有兩個應用程序 (*:7777/Foo 和 *:7777/Bar)被配置為使用 TCP。WAS 還會為每個應用程序分 配一個唯一的偵聽器通道 ID,用來將請求關聯到它們的目標應用程序。

偵聽器進程使用 WAS 提供的配置信息建立路由表,再使用該路由表,在偵聽 器通道 ID 到達時,將傳入請求映射到偵聽器通道 ID。該映射的機制是偵聽器 支持的底層協議的實現細節。重要的是每個偵聽器服務都必須能夠看到傳入消息 (比如“啊,這個發送給偵聽器通道 x”)並相應地將請求分派到 WAS。碰巧 WCF 會使用 URI 指明通過 TCP/MSMQ/命名管道到達的消息的目的地 ,但其他協議很可能會以任何適合自己的方式實現此映射。

一旦偵聽器服務連接到 WAS 並收到配置信息,它就可以打開自己的網絡資源 並開始偵聽消息。對於 TCP,這會導致 NetTcpActivator 觸發打開套接字並異 步調用 Socket.Accept,此時偵聽器實際上會進入休眠狀態,直到有消息到達。

通過非 HTTP 協議執行基於消息的激活

偵聽器進程到達等待中的套接字時會被喚醒,並開始讀取字節。此時的目的 只是為了讀取足夠的信息,以確定即將到達的消息的最終目的地,然後當偵聽器 回調至 WAS 並生成工作進程時暫停。對於 TCP,目標地址是通過從支持 SOAP 消息的幀協議讀取信息來確定的。一旦偵聽器有了目標 URI,它就會使用該 URI 作為至其內部路由表的索引,將 URI 解析為相應的偵聽器通道 ID,由 WAS 分 配到該地址。

接著,偵聽器服務請求 WAS 激活一個工作進程,通過在偵聽器適配器接口調 用非托管 WebhostOpenListenerChannelInstance 方法來處理掛起的請求。除其 他參數外,WebhostOpenListenerChannelInstance 還會接受偵聽器通道 ID 以 及數據 blob(以字節數組形式表示),該數據 Blob 將用來在激活進程中初始 化管道的接收端。該 Blob 對 WAS 沒有影響,它僅是方便允許每個協議偵聽器 實現能初始化新激活的組件。

當 WAS 收到對 WebhostOpenListenerChannelInstance 的調用時,WAS 的進 程管理器部分會生成工作進程的新實例。接著,該工作進程需要先對自己進行一 些初始化,然後才能處理偵聽器收到的消息。在這段時間內,偵聽器可能會繼續 從網絡接收數據;然而它只能在收到工作進程初始化完成的通知後,才能將消息 分派至該工作進程。

工作進程初始化

工作進程內部有幾個處於活動狀態的重要組件,如圖 3 所示。工作進程的體 系結構如圖 4 所示。

Figure3工作進程中的重要組件

組件 說明 進程宿主 將 CLR 載入工作進程並初始化默認應用程序域。 應用程序管理器 創建唯一的應用程序域以響應應用程序級別的激活請求,並管 理它們的生存期。 進程協議處理程序 實現特定於協議的進程初始化邏輯。 應用程序域協議處理程序 駐留在激活的應用程序域並執行特定於協議的應用程序域初始 化。

圖 4工作進程初始化體系結構

由於 IIS 不實現任何托管代碼組件,因此將 CLR 載入工作進程的工作落在 ASP.NET 進程宿主上。WAS 通過調用由 aspnet_isapi.dll 公開的 API,在工作 進程內部創建進程宿主。WAS 向此函數傳遞一個回調接口,使托管進程宿主可以 用它來與 WAS 進行後續通信。然後,新創建的進程宿主會返回給 WAS,使 WAS 和工作進程之間可以實現雙向通信。

WAS 使用它從工作進程中獲取的進程宿主的實例來啟動特定於協議的初始化 例程。進程宿主上的激活系統 StartProcessProtocolListenerChannel 傳遞給 它一個協議關鍵字(例如“net.tcp”)和一個回調接口,特定於協 議的處理程序可以使用它們與 WAS 通信。

當進程宿主收到要在工作進程內部啟動新的進程協議偵聽器通道的請求時, 它做的第一件事就是在配置中查找協議關鍵字。要求 WAS 用戶在啟動偵聽器之 前進行注冊。例如,WCF 為 net.tcp 注冊了一個進程協議處理程序和一個應用 程序域協議處理程序。圖 5 所示的配置數據可以在%windir%\Microsoft.NET\Framework\v2.0.50727\CONFIG下的 web.config 主文件中找到。

Figure5net.tcp 協議處理程序的配置

<system.web>
 <protocols>
  <add name="net.tcp"
   processHandlerType=
    "System.ServiceModel.WasHosting.TcpProcessProtocolHandler,
    System.ServiceModel.WasHosting, Version=3.0.0.0, Culture=neutral,
    PublicKeyToken=b77a5c561934e089"
   appDomainHandlerType=
    "System.ServiceModel.WasHosting.TcpAppDomainProtocolHandler,
    System.ServiceModel.WasHosting, Version=3.0.0.0, Culture=neutral,
    PublicKeyToken=b77a5c561934e089" validate="false" />
 </protocols>
</system.web>

一旦進程宿主解析了配置中的協議關鍵字,它就會在默認的應用程序域中創 建一個已配置進程協議處理程序類型的新實例,並用來調用 StartListenerChannel。此方法需要兩個單獨的回調接口。一個允許與 WAS 通 信(它實際上就是傳遞給進程宿主的同一個 WAS 回調對象)。另一個允許與進 程宿主直接通信(參見圖 6)。

圖 6注冊協議處理程序

進程協議處理程序 (PPH) 對於特定協議的承載模型有很大的控制力。例如, PPH 可以在初始化例程期間的某一時刻停下來,並在默認的應用程序域中處理所 有後續請求。但是,WCF 選擇保留 IIS 6.0 應用程序池模型,在每個應用程序 各自的域內進行激活,這樣可以單獨監視和回收每個應用程序。這樣,WCF PPH 需要與 ASP.NET 應用程序管理器合作,此管理器負責管理應用程序以及應用程 序域的生存期。

進程宿主通過它傳遞給 StartListenerChannel 的 IAdphManager 接口,為 PPH 提供對應用程序管理器功能的訪問。為確定激活請求的目標應用程序,PPH 回調到 WAS 並請求偵聽器最初調用 WebhostOpenListenerChannelInstance 時 提供的數據 Blob。處理返回的字節數組後,WCF PPH 將可以訪問唯一標識目標 應用程序(例如,/w3svc/1/Foo)的應用程序鍵;因為按慣例,WCF 偵聽器會在 它發送給偵聽通道的數據 Blob 中包括應用程序鍵。WAS 對於 Blob 中傳遞的數 據沒有任何限制 — 從 WAS 的角度來說,這不過是一個不透明的字節數組 。它隨即可將應用程序鍵、協議關鍵字和偵聽器回調接口傳遞給 IAdphManager.StartAppDomainProtocolListenerChannel,以激活應用程序的應 用程序域。

通常情況下,應用程序管理器會將應用程序鍵用作所有應用程序域的表索引 。當它收到對 StartAppDomainProtocolListener 的調用時,首先會查看此表, 以檢查該應用程序是否已由以前通過不同協議傳送的請求所激活。如果沒有,則 使用協議關鍵字查找特定於協議的應用程序域協議處理程序類型,查找的方式與 進程宿主使用此關鍵字來確定進程協議處理程序類型的方式相同。一旦解析了應 用程序域協議處理程序類型,應用程序管理器就在目標應用程序域中創建它的新 實例。最後,應用程序管理器調用 AppDomainProtocolHandler.StartListenerChannel,再次傳遞 WAS 回調接口, 這樣,在必要時應用程序域就可以與 WAS 通信。

當應用程序域協議處理程序被實例化之後,工作進程的內部即如圖 7 所示。 此時,WAS 認為目標應用程序已完全激活,在應用程序關閉之前不再干預進程。

圖 7應用程序域協議處理程序初始化

將數據從偵聽器傳送到工作進程

激活工作進程和應用程序域是實現把消息實際傳送到應用程序這一更大目標 的第一步。由於 WAS 是激活服務而不是消息傳送服務,因此它不參與這項活動 。相反,每個協議實現都能夠以適合自己的任意方式實現偵聽器和工作進程之間 的通信。此體系結構允許多種特定於協議的優化,允許每個實現利用底層協議的 特性。例如,WCF MSMQ 激活服務僅傳遞數據 Blob 中 MSMQ 隊列的名稱,允許 已激活的應用程序從數據源直接讀取,完全繞過偵聽器。

在 WAS 中承載 WCF 服務

現在,讓我們動手實際將 WCF 服務承載於 WAS。首先,我們將設置一個基於 HTTP 的服務,這與我們在 IIS 6.0 中的操作沒有太大區別。然後,我們將添加 對 TCP、命名管道和 MSMQ 的支持,並詳細說明可用配置選項(參見圖 8 獲取 該簡單服務的源代碼)。

Figure8示例 WCF 服務

[ServiceContract(Name = "WasDemoServiceContract",
        Namespace = "urn:msdnmag")]
public interface IDemoService
{
 [OperationContract(Name="Ping", Action="Ping",
           ReplyAction="PingReply")]
 string Ping(string data);
}
[ServiceBehavior(
 Name="WasDemoService", Namespace="urn:msdnmag",
 InstanceContextMode=InstanceContextMode.PerCall,
 ConcurrencyMode=ConcurrencyMode.Single)]
public class DemoService : IDemoService
{
 public string Ping(string data)
 {
  return "pinged with: " + data;
 }
}

第一步是在 WAS 中創建新應用程序 — 您可以使用 IIS 7.0 管理工具 ,或使用 Visual Studio® 來創建。虛擬目錄必須包含三項:服務(在 /bin 目錄中編譯的或如 /App_Code 文件夾中的源代碼)、.svc 服務終結點和 配置文件。

.svc 文件會在 URI 和服務實現(與 .asmx 文件十分相似)之間建立一個連 接。.svc 文件內的 @ServiceHost 指令會指示 WCF 管道為 Service 屬性中指 定的類型創建一個 ServiceHost 實例:

<% @ServiceHost Service="DemoService" % >

WAS 的配置信息存儲在 web.config 文件中。整個 WCF 配置在 system.serviceModel 節發生。這裡要說明的是,當承載於 WAS 時不能指定基 址,因為該地址由網站和虛擬目錄系統決定(參見圖 9 以了解基本配置文件) 。已配置的服務行為會啟用元數據發布,您可以將 svcutil.exe 或 Visual Studio 中的“Add Service Reference”(添加服務參考)對話框等 工具直接指向 .svc 文件,以生成客戶端代理。

Figure9示例服務的基本配置

<configuration>
 <system.serviceModel>
  <services>
   <service name="DemoService" behaviorConfiguration="Behavior">
    <endpoint address="" binding="wsHttpBinding"
     bindingNamespace="urn:msdnmag" contract="IDemoService" />
   </service>
  </services>
  ...
  <behaviors>
   <serviceBehaviors>
    <behavior name="Behavior">
     <serviceMetadata httpGetEnabled="true" />
    </behavior>
   </serviceBehaviors>
  </behaviors>
 </system.serviceModel>
</configuration>

使用 HTTP 傳輸的 WCF 終結點會遍歷公用 IIS 處理管道。如果您過去使用 過 ASP.NET Web 服務,則可能看上去會非常熟悉,但其內在工作原理卻有略有 不同。對於基於 HTTP 的終結點,實際上有兩種操作模式:ASP.NET 兼容性和無 兼容性。ASP.NET 兼容性在默認情況下關閉,這就是說 HttpContext.Current 為空,終結點的實際激活並不在 HttpHandler 中發生,而是在 HttpModule 中 發生,它將請求傳遞到 ASP.NET PostAuthenticateRequest 管道事件的 ServiceHost。此處有一些含義;首先,如果您習慣用 ASP.NET 可擴展性模型, 您將只接收到管道通知,直至 PostAuthenticateRequest。從此時起,處理將直 接跳到 EndRequest 事件。此外,WCF 服務將無法訪問任何 ASP.NET 宿主功能 ,比如會話狀態。這種設置是有意為之的。由於 WCF 服務宿主擁有與所有這些 功能(會話、身份驗證等)對應的傳輸獨立的等效項,您不應嘗試將您的服務綁 定到一個特定承載環境中。

如果您的確需要 ASP.NET 兼容性(例如,為了在服務和承載於同一應用程序 域中的 ASP.NET 應用程序間共享會話狀態),您可以使用一個配置開關啟用它 :

<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />

此外,您必須專門為 ASP.NET 兼容性啟用您的服務:

[AspNetCompatibilityRequirements(
  RequirementsMode=AspNetCompatibilityRequirementsMode.Required)]
class DemoService : IDemoService { ... }

這兩個組合開關改變了服務請求的內在處理的工作方式。使用這兩個開關後 ,請求就會通過 HttpHandler 被分派到 WCF 運行時,整個的處理管道行為與 ASP.NET Web 服務類似。此外,當前的 HttpContext 仍然可用,可以像往常一 樣使用它。在 ASP.NET 兼容性模式下運行的 WCF 服務只支持 HTTP 終結點。

現在您已經了解如何在 WAS 中承載基於 HTTP 的服務,接下來讓我們將非 HTTP 終結點添加到該服務。如前所述,協議偵聽器負責打開實際傳輸,並將連 接分派至運行服務的應用程序域。這些協議偵聽器都是 Windows NT 服務。打開 services.msc 時,您將發現一個 Net.tcp 偵聽器適配器,以及用於命名管道與 MSMQ 的偵聽器。這些適配器與偵聽器連同為 TCP 共享服務的端口,必須啟用並 運行才能實現非 HTTP 的成功激活。

偵聽器適配器的宿主名稱和端口配置可以在 WAS 配置文件 applicationHost.config 中找到。您可以使用 appcmd.exe 命令行工具手動編 輯這些設置,也可以使用 IIS 7.0 配置 GUI。這些稱作綁定的設置在站點級別 完成(參見圖 10)。(注意,Windows Vista 和 Windows Server 2008 附帶的 WAS 版本有所不同。在 Windows Vista 中,綁定不是預配置的,而且當前沒有 GUI 以圖形方式添加它們。之後您將看到必需的 appcmd.exe 命令。在 Windows Server 2008 中,綁定是默認配置的。)

圖 10IIS 6.0 基礎體系結構

圖 10IIS 6.0 基礎體系結構

<bindings>
 <binding protocol="https" bindingInformation="*:443:" />
 <binding protocol="http" bindingInformation="*:80:" />
 <binding protocol="net.tcp" bindingInformation="808:*" />
 <binding protocol="net.pipe" bindingInformation="*" />
 <binding protocol="net.msmq" bindingInformation="localhost" />
 <binding protocol="msmq.formatname" bindingInformation="localhost" />
</bindings>

在 bindingInformation 屬性中,每個協議都與特定於協議的配置設置相關 聯。例如,TCP 綁定的 bindingInformation 指定端口和偵聽器接受連接所在的 宿主名稱(* 表示所有宿主名稱均有效)。

最後一步是在承載服務的應用程序中啟用選擇的協議。截止 Windows Server 2008 Beta 3,都沒有適用的 GUI;您必須手動編輯 applicationHost.config。 稍後我們將探討如何使用工具或以編程方式執行這些操作。搜索配置文件中的應 用程序元素,然後添加一個 enabledProtocols 屬性,如下所示:

<application path="/WasDemo"
  enabledProtocols="http,net.tcp,net.pipe,net.msmq">
 <virtualDirectory path="/" physicalPath="C:\MSDN\WasDemo\web" />

接著,您只需要為自己的 WCF 服務配置添加額外終結點,使它們可以通過以 上協議被訪問:

<endpoint address="" binding="netTcpBinding"
 bindingNamespace="urn:msdnmag" contract="IDemoService" />
<endpoint address="" binding="netNamedPipeBinding"
 bindingNamespace="urn:msdnmag" contract="IDemoService" />

當根據元數據生成客戶端代理時,配置文件將為每個終結點包含一個客戶端 元素。在客戶端代碼中,您可以通過將終結點名稱傳遞至代理構造函數來指定使 用哪個傳輸,如下所示:

WasDemoServiceContractClient proxyTcp =
 new WasDemoServiceContractClient(
  "NetTcpBinding_WasDemoServiceContract");
Console.WriteLine(proxyTcp.Ping("Hello"));

向服務添加 MSMQ 終結點還需要一些其他步驟。首先,您必須使用 Message Queuing MMC 管理單元(或通過使用任何可用的編程方法)創建一個隊列。另外 ,您必須在此隊列中設置訪問控制列表 (ACL),以允許工作進程(默認是 NETWORK SERVICE)從隊列讀取和速查。

值得注意的是,MSMQ 終結點激活僅能在隊列與 .svc 文件(去掉機器名稱) 同名的情況下才能正確運作。這就是說,如果服務終結點是 /server/app/service.svc,隊列名稱就必須是 app/service.svc。

在服務配置中,必須指定服務應偵聽的隊列的名稱:

<endpoint address="net.msmq://localhost/private/wasdemo/service.svc"  binding="netMsmqBinding" bindingNamespace="urn:msdnmag"
 contract="IServiceMsmq" />

非 HTTP 終結點不傳遞 IIS 處理管道,將直接路由至 WCF 運行時。這意味 著您不能使用 HttpModule 對請求進行預處理或後處理。此外, HttpApplication 類 (global.asax) 的 Application_Start 和 Application_End 不會觸發。所以,如果您希望為此類服務運行啟動或清理代碼 ,則必須使用 ServiceHost 類的事件。在下一部分我們將詳細介紹其工作方式 。

WAS 承載的服務的生存期管理

當您自承載一個服務(例如,在 Windows NT 服務中)時,該服務的生存期 是確定的。它將在調用 Open 後運行,在調用 Close 後關閉(暫時忽略災難性 故障)。此外,Windows NT 服務通常在引導時啟動。這與在 WAS 中承載是不同 的。

首先,WAS 的確要求激活(如您所知,“WAS”中的 “A”就代表激活)。這就是說,承載服務的應用程序域僅在請求消 息進入時才能創建。歷經一段可配置的空閒時間後,應用程序域再次關閉。有幾 個原因可解釋為何 WAS 或 ASP.NET 運行時可以決定回收應用程序域甚至整個工 作進程。

首先,WAS 中的應用程序池設置指定工作進程的定期回收,默認每 29 小時 回收一次(請注意這個質數是刻意選擇的,以最大程度減少實際回收發生在一天 當中的同一時間的可能性)。

應用程序域可以回收還有其他幾個原因。影響該應用程序域的 machine.config、web.config、或 applicationHost.config 的配置設置可能已 經更改。/bin 或 /App_Code 目錄或其內容也可能已經被修改,重新編譯(對於 .aspx、.asmx、.svc 等)的次數超出了 machine.config 或 web.config 中的 <compilation numRecompilesBeforeAppRestart /> 設置所指定的限制( 默認值設為 15),或者虛擬目錄的物理路徑已被修改。最後,代碼訪問安全 (CAS) 策略可能已被修改,或者應用程序子目錄可能已被刪除。當發生這些情況 時,您會丟失所有內存中狀態,包括會話或實例和靜態變量。這實質上意味著現 成的 WAS 承載並不是真正適合會話服務或單例服務。它更適合無狀態單調用服 務。

WCF ServiceHost 類型包含 Opening 和 Closing 這兩個事件。它們是在服 務啟動和關閉時執行代碼的唯一正確方式。問題是,您必須在創建 ServiceHost 的新實例和在其上調用 Open 之間接通這些事件的處理程序。如上所述,當在 .svc 文件中使用 @ServiceHost 指令時,這無法實現。這種情況下的一個可行 方案是承載一個自定義服務宿主工廠。它能給您更多控制,允許您處理上述事件 。

為此,必須從一個名為 ServiceHostFactoryBase 的類派生,並實現 CreateServiceHost 方法。該方法從承載環境接收服務類型名稱和基址,返回 ServiceHostBase 的一個實例。現在您要負責創建恰當的 ServiceHost,並按照 需要對它進行配置,然後將其返回到 WCF 運行時。完成這些操作後,您就能以 編程方式訪問 WCF 的可擴展性模型,然後接通事件處理程序(參見圖 11)。

Figure11使用 CreateServiceHost

public class HostFactory : ServiceHostFactoryBase
{
 public override ServiceHostBase CreateServiceHost(
  string constructorString, Uri[] baseAddresses)
 {
  Type service = Type.GetType(constructorString);
  ServiceHost host = new ServiceHost(service, baseAddresses);
  // hook up event handlers
  host.Opening += OnOpening;
  host.Closing += OnClosing;
  return host;
 }
}

要在業已存在的 .svc 終結點和自定義工廠之間建立連接,必須將 Factory 屬性添加到 @ServiceHost 指令:

<%@ServiceHost Service="Service" Factory="HostFactory" %>

自動設置 WAS 承載的服務

我們已經討論過,設置 WAS 承載的 WCF 服務包含幾個必需的步驟:

設置物理目錄(如果需要,還可設置 ACL)。

在 WAS 中設置應用程序和虛擬目錄。

配置啟用的協議。

創建隊列並設置 ACL(如果使用 MSMQ)。

WAS 為配置提供了多種不同的接口,包括 Windows Management Instrumentation (WMI)、命令行工具 (appcmd.exe) 和全新的托管 API。WMI 和 appcmd.exe 非常適用於編寫腳本和批處理文件,新的托管 API 則讓您能輕 松訪問更復雜的部署方案(如 MSI 包)的配置。請參見圖 12 獲得一些 appcmd.exe 示例。

Figure12使用 appcmd.exe 設置 WAS 應用程序

REM adding bindings to the default web site
appcmd.exe set site "Default Web Site" –
  +bindings.[protocol='net.tcp',bindingInformation='808:*']
appcmd.exe set site "Default Web Site" –
  +bindings.[protocol='net.pipe',bindingInformation='*']
appcmd.exe set site "Default Web Site" –
  +bindings.[protocol='net.msmq',bindingInformation='localhost']
REM add a new application
appcmd add app /site.name:"Default Web Site" /path:/WasDemo
  /physicalpath:c:\etc\WasDemo
REM enabled protocols for application
appcmd.exe set app "Default Web Site/WasDemo"
  /enabledProtocols:http,net.pipe,net.tcp,net.msmq

托管的配置 API 可在新的 Microsoft.Web.Administration.dll 程序集中找 到,以 ServerManager 類為中心。您會在其中找到一個完整的對象模型,涵蓋 應用程序池、工作進程、站點、應用程序和配置文件。使用該 API 設置應用程 序只需要使用幾行代碼,如圖 13 所示。另請參見圖 14,了解一些可為 NETWORK SERVICE 帳戶設置隊列和最少 ACL 的幫助器方法。

Figure14創建隊列並設置 ACL

private static void SetupMsmq(string queuename)
{
 // create queue – in this case a transactional queue is created
 MessageQueue queue = MessageQueue.Create(queuename, true);
 queue.Label = queuename;
 // set ACL
 queue.SetPermissions(GetAccountName(
  WellKnownSidType.NetworkServiceSid),
  MessageQueueAccessRights.ReceiveMessage);
}
private static string GetAccountName(WellKnownSidType wellKnownSid)
{
 NTAccount account = (NTAccount) new SecurityIdentifier (wellKnownSid,
  null).Translate(typeof(NTAccount));
 return account.Value;
}

Figure13使用托管的配置 API

private static void SetupWasApp(string virtualPath, string physicalPath,
 string enabledProtocols)
{
 ServerManager manager = new ServerManager();
 // creating the application
 manager.Sites[0].Applications.Add(virtualPath, physicalPath);
 manager.CommitChanges();
 // setting up the enabled protocols
 Application wasApp = manager.Sites[0].Applications[virtualPath];
 wasApp.EnabledProtocols = enabledProtocols;
 manager.CommitChanges();
}

擴展 WAS

WAS 是可擴展的,這意味著您可以編寫自己的對自定義 WCF 傳輸信道的支持 。Windows SDK 包含基於用戶數據報協議 (UDP) 的 WAS 激活擴展的示例,但是 擴展 WAS 以添加您自己的功能絕非易事。這涉及到實現自定義進程和應用程序 域協議處理程序,以及創建用於偵聽的代碼以接收各個目標傳輸機制的相關消息 。要充分說明這個問題,需要專門深入探討該主題的文章。

但是,有一點非常重要,必須提到。WAS 中的最大請求執行限制數根據運行 的操作系統的不同而具有不同的值。對於 Windows Server 2008,它是沒有限制 的,但對於 Windows Vista Ultimate 以及 Business 來說,連接限制數為 10 。Windows Vista 的家庭版,即 Windows Vista Home Basic 和 Home Premium 以及 Windows Vista Starter,請求執行限制數都是 3。在規劃實現時應考慮到 這一點。

Dominick Baier是德國 thinktecture (thinktecture.com) 的安全咨詢師。 此外,他還是 DevelopMentor (develop.com) 安全和 WCF 課程負責人和開發人 員安全 MVP,同時也是《Developing More Secure Microsoft ASP.NET 2.0 Applications》(Microsoft Press, 2006) 一書的作者。您可以在 leastprivilege.com 訪問他的博客。

Christian Weyer是 thinktecture 的共同創始人之一,也是一位首席架構師 ,曾用 Java、COM、DCOM、COM+、Web Services、WCF 和其他技術進行分布式應 用程序的建模和實現。您可以通過 thinktecture.com/staff/christian 與他聯 系。

Steve Maine是 Microsoft Connected Framework 團隊的一位高級項目經理 。他目前致力於研究 .NET Framework 3.5 中即將發布的 WCF 的多項專注於 Web 的編程模型功能。他的博客地址:hyperthink.net/blog。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved