如何讓 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 ServerASP.NET 狀態服務 且也設定好相同的 <machineKey> 也一樣無法互通 Session 資料。

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

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

C:\Windows\System32\inetsrv>appcmd list app
APP "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 的情況,程式碼並無法修改。

 

這些屬性無法透過「正規」的管道進行修正或調整,所以「表面上」似乎無解,除非你自行撰寫新的 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, "SharedAppDomainAppId");

假設我們這個 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 再做任何修改就沒有作用了。

相關連結

  

此文章由 will 發表於 2010/1/24 下午 02:02:10

永久連結 | 評論 (6) | 此文章的RSSRSS comment feed |

分類: Web | ASP.NET | IIS

標籤: , , , ,

收藏:

相關文章

評論

三月 6. 2010 21:15

Gower Lin

最近一個專案剛好遇到此問題, 環境為 Windows Server 2003 + SP2 (Windows Update 到最新), Visual Studio 2008, ASP.NET Web 應用程式專案.....

試了一下, 這個方式好失效了, Code Project Femiani 的模組也是無效, 而且會照成 IIS 死掉, 必須 IISRESET. :-(

看來還是得 "造個輪子" 否則難以保證哪次 Service Pack 或 Hotfix 後, 又不能相容了

Gower Lin tw

三月 6. 2010 22:14

Will 保哥

Gower Lin: 你有成功過嗎?

Will 保哥 tw

三月 7. 2010 08:14

Gower Lin

沒有, 但是以單步執行 Debug, 發現他的 appDomainAppId 的確有被修改

Gower Lin tw

三月 7. 2010 08:37

Gower Lin

後來在對岸找到一篇有關 ISessionIDManager 介紹, 看來寫這個必較簡單...

www.cnblogs.com/worksguo/articles/1113991.html

Gower Lin tw

三月 7. 2010 09:27

Will 保哥

Gower Lin: 我自己嘗試是可以成功的,而且不會讓 IIS 掛掉喔,你可能是哪裡弄錯了,你可以嘗試一下調整 HttpModule 的載入順序!

ISessionIDManager 是用來做 Session Partitioning 用的,用途是將 Session 分散儲存到不同的伺服器,不能用來解決「同站台不同應用程式」之間共用 Session 資料的問題。

Will 保哥 tw

三月 9. 2010 23:20

Gower

請問保哥, 你是使用 sessionState mode 是使用 InProc 還是 StateServer ?

另外, 我如果用 Visual Studio (F5) 除錯, 第一次正常, 再 Rebuild 方案, 就會造成 w3wp crashed

Gower

新增評論


(將顯示您的Gravatar圖示)  

  Country flag

[b][/b] - [i][/i] - [u][/u]- [quote][/quote]



線上預覽

三月 10. 2010 05:15