﻿# 如何讓 IIS6 / IIS7 中同站台不同應用程式間共用 Session 資料

在 Web Farm 環境下部署網站需要關注的細節可不少，在部署大型網站的時候 IIS 這部分到底要算 **IT** 的領域還是**開發人員**(Developer)的領域其實分不太清楚，像要在「同一個站台」區分「不同應用程式」且還要能讓 Session 彼此互通，這到底應該歸誰管呢？這可不是用「ASP.NET 開發伺服器」可以模擬出來的，而 IT 人員如果不會寫程式應該也不知該如何是好，這也是我認為 ASP.NET 開發人員應該多熟悉 IIS 的原因。

要在 Web Farm 環境下要達成 Session 互通有以下條件：

1.  確保 <machineKey> 要設定一致
2.  確保各站台在 IIS 中 metabase 定義的「應用程式路徑」必須一致
3.  使用者的 Session Cookie 的 **名稱/值** 必須一致 (不能跨越**不同的網域網址**或**不同的父網域網址**)

這三個條件是我從四處的文件與實際經驗累積而來的結果，而其中最不容易發現的是**「應用程式路徑」必須一致**這個條件。

例如在 IIS 中，預設站台的 ID 為 1，在站台根目錄這個預設用程式的「應用程式路徑」為：

```
/LM/W3SVC/1/ROOT
```

若你在 Web Farm 環境下的第二台伺服器，除了原本的預設站台外，再建立另一個新的站台，則該站台的 ID 為 2，其「應用程式路徑」為：

```
/LM/W3SVC/2/ROOT
```

那麼這兩個站台便無法共用 Session，即便 Session 儲存後端用 **SQL Server** 或 **ASP.NET 狀態服務** 且也設定好相同的 <machineKey> 也一樣無法互通 Session 資料。

基於這個規則，在架設 Web Farm 網站時就必須特別小心設定，否則可能查設定查到天荒地老也不知道為什麼會這樣，但在不太複雜的網站下，通常多台 Web Farm 主機下也各別只會有一個站台，所以也不一定會遇到這狀況。

那麼在「同一個站台」下「不同應用程式」時，其實就是「兩個應用程式」，我們可以利用 appcmd 指令列工具測試一下：

```
C:\Windows\System32\inetsrv>appcmd list appAPP "Default Web Site/" (applicationPool:DefaultAppPool)APP "Default Web Site/WebSite2" (applicationPool:DefaultAppPool)
```

以上述為例，在 **Default Web Site** 站台下另外新增了一個「應用程式」為 **/WebSite2**，其「應用程式路徑」分別為：

```
/LM/W3SVC/1/ROOT/LM/W3SVC/1/ROOT/WebSite2
```

所以這兩個**應用程式**基本上是無法互通 Session 的，但你可能會納悶在同一個站台下為何 Session 無法互通，因為在 .NET Framework 裡的 System.Web.SessionState 命名空間已經預先設定了取得 Session 資料的邏輯，所以這是 by design 的情況，程式碼並無法修改。

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/aef8ff3d5e33656da7ae-image.png)

 ![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/55105684a820011cdd69-image.png)

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/6d65c8337cd2c93cc3fb-image.png)

這些屬性無法透過「正規」的管道進行修正或調整，所以「表面上」似乎無解，除非你自行撰寫新的 Session 機制並將 IIS 中內建的 Session Module 抽換掉，但這實在太累人了，沒必要重新發明輪子。

還好在 .NET 有 **Reflection** (反映) 機制，透過自訂的 HttpModule 可以讓 SessionStateModule 模組被載入之前動態將 System.Web.HttpRuntime 型別中的 \_appDomainAppId 的值換成我們希望的值，這樣就可以讓不同應用程式之間共用 Session 資料了，程式碼如下：

```
FieldInfo runtimeInfo = typeof(HttpRuntime).GetField("_theRuntime",     BindingFlags.Static | BindingFlags.NonPublic);    HttpRuntime theRuntime = (HttpRuntime)runtimeInfo.GetValue(null);FieldInfo appDomainAppIdInfo = typeof(HttpRuntime).GetField("_appDomainAppId",     BindingFlags.Instance | BindingFlags.NonPublic);appDomainAppIdInfo.SetValue(theRuntime, "/LM/W3SVC/1/ROOT");
```

請注意：不同的站台會有不同的 Website ID 所以請不要照抄喔，一般來說預設站台的 ID 都是 1 所以才可以用上述的程式碼。

假設我們這個 HttpModule 的類別名稱為 SharedSessionModule 並放置在 App\_Code 動態編譯目錄下，我們就可以修改 web.config 將我們自訂的 SharedSessionModule 註冊到 IIS6 的 <httpModules> 區段，或 IIS7 的 <modules> 區段中。

**IIS6**

```
<httpModules>  <add name="SharedSessionModule" type="SharedSessionModule, App_Code"/></httpModules>
```

**IIS7**

```
<modules>    <remove name="Session" />    <add name="SharedSessionModule" type="SharedSessionModule, App_Code"/>    <add name="Session1" type="System.Web.SessionState.SessionStateModule,        System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> </modules>
```

注意: 在 IIS7 中由於 整合管線(Integrated Pipeline) 的關係 HttpModule 的載入順序有些應該注意的地方，你不能使用在 applicationHost.config 中已經定義過的模組名稱，否則將無法調整載入模組的順序，所以才必須「先移除 Session」再「載入 SharedSessionModule」然後再「重新載入 SessionStateModule 並命名為 Session1」才行。如果預設的 SessionStateModule 模組在 SharedSessionModule 之前先執行，在 SessionStateModule 模組完成初始化動作後 SharedSessionModule 再做任何修改就沒有作用了。

**相關連結**

-   [Sharing Session Across Applications](http://www.codeproject.com/KB/session/sharedsession.aspx)
-   [工作階段狀態模式](http://msdn.microsoft.com/zh-tw/library/ms178586.aspx) ( [Session-State Modes](http://msdn.microsoft.com/en-us/library/ms178586.aspx) )
-   [PRB：如果您使用 SqlServer 或 StateServer 工作階段模式，在 Web Farm 中的 Session 會遺失](https://web.archive.org/web/20100610012445/http://support.microsoft.com:80/kb/325056)
-   [PRB: Session State Is Lost in Web Farm If You Use SqlServer or StateServer Session Mode](https://web.archive.org/web/20070310211512/http://support.microsoft.com:80/kb/325056)
-   [How to create keys by using Visual C# .NET for use in Forms authentication](https://web.archive.org/web/20090214103845/http://support.microsoft.com:80/kb/312906)
-   [ASP.NET Memory - How much are you caching? + an example of .foreach](http://blogs.msdn.com/tess/archive/2006/01/26/asp-net-memory-how-much-are-you-caching-an-example-of-foreach.aspx)
-   [State Management and Caching](http://msdn.microsoft.com/en-us/library/aa531332.aspx)
-   [IIS 7.0: add Element for modules for system.webServer (IIS Settings Schema)](http://msdn.microsoft.com/en-us/library/ms690693.aspx)
-   [Introduction to IIS 7.0 Architecture](https://learn.microsoft.com/en-us/iis/get-started/introduction-to-iis/introduction-to-iis-architecture)
