明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

ASP.NET 中 Session 完成原理淺析 [1] 會(huì)話的創(chuàng)建流程

[摘要]HTTP 協(xié)議之所以能夠獲得如此大的成功,其設(shè)計(jì)實(shí)現(xiàn)的簡(jiǎn)潔性和無(wú)狀態(tài)連接的高效率是很重要的原因。而為了在無(wú)狀態(tài)的 HTTP 請(qǐng)求和有狀態(tài)的客戶端操作之間達(dá)到平衡,產(chǎn)生了服務(wù)器端會(huì)話 (Session...
HTTP 協(xié)議之所以能夠獲得如此大的成功,其設(shè)計(jì)實(shí)現(xiàn)的簡(jiǎn)潔性和無(wú)狀態(tài)連接的高效率是很重要的原因。而為了在無(wú)狀態(tài)的 HTTP 請(qǐng)求和有狀態(tài)的客戶端操作之間達(dá)到平衡,產(chǎn)生了服務(wù)器端會(huì)話 (Session) 的概念?蛻舳嗽谶B接到服務(wù)器后,就由 Web 服務(wù)器產(chǎn)生并維護(hù)一個(gè)客戶端的會(huì)話;當(dāng)客戶端通過無(wú)狀態(tài) HTTP 協(xié)議再次連接到服務(wù)器時(shí),服務(wù)器根據(jù)客戶端提交的某種憑據(jù),如 Cookie 或 URL 參數(shù),將客戶關(guān)聯(lián)到某個(gè)會(huì)話上。這種思路在各種開發(fā)語(yǔ)言和開發(fā)環(huán)境中大量得到應(yīng)用。
在 ASP.NET 中,Web 應(yīng)用程序和會(huì)話狀態(tài)被分別進(jìn)行維護(hù),通過 HttpApplication 和 HttpSessionState 分離 Web 應(yīng)用程序與會(huì)話的功能。應(yīng)用程序?qū)舆壿嬙?Global.asax 文件中實(shí)現(xiàn),運(yùn)行時(shí)編譯成 System.Web.HttpApplication 的實(shí)例;會(huì)話則作為單獨(dú)的 System.Web.SessionState.HttpSessionState 實(shí)例,由服務(wù)器統(tǒng)一為每個(gè)用戶會(huì)話維護(hù),通過 ASP.NET 頁(yè)面編譯成的 System.Web.UI.Page 對(duì)象子類的 Session 屬性訪問。關(guān)于 ASP.NET 中不同層次關(guān)系可參考我以前的一篇文章《.NET 1.1中預(yù)編譯ASP.NET頁(yè)面實(shí)現(xiàn)原理淺析 [1] 自動(dòng)預(yù)編譯機(jī)制淺析》,以下簡(jiǎn)稱【文1】。

ASP.NET 在處理客戶端請(qǐng)求時(shí),首先將根據(jù)客戶端環(huán)境,生成一個(gè) System.Web.HttpContext 對(duì)象,并將此對(duì)象作為執(zhí)行上下文傳遞給后面的頁(yè)面執(zhí)行代碼。
在【文1】的分析中我們可以看到,HttpRuntime 在處理頁(yè)面請(qǐng)求之前,根據(jù) HttpWorkerRequest 中給出的環(huán)境,構(gòu)造 HttpContext 對(duì)象,并以次對(duì)象作為參數(shù)從應(yīng)用程序池中獲取可用應(yīng)用程序。簡(jiǎn)要代碼如下:
以下內(nèi)容為程序代碼:

private void HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr)
{
// 構(gòu)造 HTTP 調(diào)用上下文對(duì)象
HttpContext ctxt = new HttpContext(wr, 0);

//...

// 獲取當(dāng)前 Web 應(yīng)用程序?qū)嵗?br> IHttpHandler handler = HttpApplicationFactory.GetApplicationInstance(ctxt);

// 調(diào)用 handler 實(shí)際處理頁(yè)面請(qǐng)求
}




HttpApplicationFactory 工廠內(nèi)部維護(hù)了一個(gè)可用的應(yīng)用程序?qū)嵗彌_池,用戶降低應(yīng)用程序?qū)ο髽?gòu)造的負(fù)荷。
如果池中沒有可用的應(yīng)用程序?qū)ο髮?shí)例,此對(duì)象工廠最終會(huì)調(diào)用 System.Web.HttpRuntime.CreateNonPublicInstance 方法構(gòu)造新的應(yīng)用程序?qū)嵗,并調(diào)用其 InitInternal 方法初始化。詳細(xì)步驟分析見【文1】
以下內(nèi)容為程序代碼:

internal static IHttpHandler HttpApplicationFactory.GetApplicationInstance(HttpContext ctxt)
{
// 處理定制應(yīng)用程序
//...

// 處理調(diào)試請(qǐng)求
//...

// 判斷是否需要初始化當(dāng)前 HttpApplicationFactory 實(shí)例
//...

// 獲取 Web 應(yīng)用程序?qū)嵗?br> return HttpApplicationFactory._theApplicationFactory.GetNormalApplicationInstance(ctxt);
}

private HttpApplication HttpApplicationFactory.GetNormalApplicationInstance(HttpContext context)
{
HttpApplication app = null;

// 嘗試從已施放的 Web 應(yīng)用程序?qū)嵗?duì)列中獲取
//...

if(app == null)
{
// 構(gòu)造新的 Web 應(yīng)用程序?qū)嵗?br> app = (HttpApplication)System.Web.HttpRuntime.CreateNonPublicInstance(this._theApplicationType);

// 初始化 Web 應(yīng)用程序?qū)嵗?br> app.InitInternal(context, this._state, this._eventHandlerMethods);
}

return app;
}



這里的 System.Web.HttpApplication.InitInternal 函數(shù)完成對(duì)應(yīng)用程序?qū)ο蟮某跏蓟ぷ鳎ㄕ{(diào)用 HttpApplication.InitModules 函數(shù)初始化 HTTP 模塊(后面將詳細(xì)介紹),并將作為參數(shù)傳入的 HttpContext 實(shí)例保存到 HttpApplication._context 字段中。而此 HTTP 上下文對(duì)象將被后面用于獲取會(huì)話對(duì)象。
以下內(nèi)容為程序代碼:

public class HttpApplication : ...
{
private HttpContext _context;
private HttpSessionState _session;

public HttpSessionState Session
{
get
{
HttpSessionState state = null;
if (this._session != null)
{
state = this._session;
}
else if (this._context != null)
{
state = this._context.Session;
}
if (state == null)
{
throw new HttpException(HttpRuntime.FormatResourceString("Session_not_available"[img]/images/wink.gif[/img]);
}
return state;
}
}
}



而在 ASP.NET 頁(yè)面中獲取會(huì)話的方法也是類似,都是通過 HttpContext 來(lái)完成的。
以下內(nèi)容為程序代碼:

public class Page : ...
{
private HttpSessionState _session;
private bool _sessionRetrieved;

public virtual HttpSessionState Session
{
get
{
if (!this._sessionRetrieved)
{
this._sessionRetrieved = true;
try
{
this._session = this.Context.Session;
}
catch (Exception)
{
}
}
if (this._session == null)
{
throw new HttpException(HttpRuntime.FormatResourceString("Session_not_enabled"[img]/images/wink.gif[/img]);
}
return this._session;
}
}
}



在 HttpContext 中,實(shí)際上是通過一個(gè)哈希表保存諸如會(huì)話對(duì)象之類信息的
以下內(nèi)容為程序代碼:

public sealed class HttpContext : ...
{
private Hashtable _items;

public IDictionary Items
{
get
{
if (this._items == null)
{
this._items = new Hashtable();
}
return this._items;
}
}

public HttpSessionState Session
{
get
{
return ((HttpSessionState) this.Items["AspSession"]);
}
}
}



而 HttpContext.Session 所訪問的又是哪兒來(lái)的呢?這就又需要回到我們前面提及的 HttpApplication.InitModules 函數(shù)。

在 .NET 安裝目錄 Config 子目錄下的 machine.config 定義了全局性的配置信息,而 HttpApplication 就是使用其中 system.web 一節(jié)的配置信息進(jìn)行初始化的。
以下內(nèi)容為程序代碼:

<system.web>
<httpModules>
<add name="OutputCache" type="System.Web.Caching.OutputCacheModule" />
<add name="Session" type="System.Web.SessionState.SessionStateModule" />
<add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" />
<add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
<add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule" />
<add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />
<add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule" />
<add name="ErrorHandlerModule" type="System.Web.Mobile.ErrorHandlerModule, System.Web.Mobile, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</httpModules>
</system.web>



httpModules 節(jié)點(diǎn)指定了 HttpApplication 需要初始化的模塊列表,而在前面提到的 HttpApplication.InitModules 函數(shù)正式根據(jù)此列表進(jìn)行初始化的
以下內(nèi)容為程序代碼:

private void HttpApplication.InitModules()
{
HttpModulesConfiguration cfgModules = ((HttpModulesConfiguration) HttpContext.GetAppConfig("system.web/httpModules"[img]/images/wink.gif[/img]);

if (cfgModules == null)
{
throw new HttpException(HttpRuntime.FormatResourceString("Missing_modules_config"[img]/images/wink.gif[/img]);
}
_moduleCollection = cfgModules.CreateModules();

for(int i = 0; i < _moduleCollection.Count; i++)
{
_moduleCollection[i].Init(this);
}

GlobalizationConfig cfgGlobal = ((GlobalizationConfig) HttpContext.GetAppConfig("system.web/globalization"[img]/images/wink.gif[/img]);
if (cfgGlobal != null)
{
_appLevelCulture = cfgGlobal.Culture;
_appLevelUICulture = cfgGlobal.UICulture;
}
}



Session 節(jié)點(diǎn)對(duì)于的 System.Web.SessionState.SessionStateModule 對(duì)象將被 HttpModulesConfiguration.CreateModules 方法構(gòu)造,并調(diào)用其 Init 函數(shù)初始化。SessionStateModule 類實(shí)際上就是負(fù)責(zé)管理并創(chuàng)建會(huì)話,用戶完全可以自行創(chuàng)建一個(gè)實(shí)現(xiàn) IHttpModule 接口的類,實(shí)現(xiàn)會(huì)話的控制,如實(shí)現(xiàn)支持集群的狀態(tài)同步等等。
SessionStateModule.Init 方法主要負(fù)責(zé) machine.config 文件中的 sessionState 配置,調(diào)用 SessionStateModule.InitModuleFromConfig 方法建立相應(yīng)的會(huì)話管理器。
以下內(nèi)容為程序代碼:

<system.web>
<sessionState mode="InProc"
stateConnectionString="tcpip=127.0.0.1:42424"
stateNetworkTimeout="10"
sqlConnectionString="data source=127.0.0.1;Integrated Security=SSPI"
cookieless="false"
timeout="20" />
</system.web>



sessionState 的使用方法請(qǐng)參加 MSDN 中相關(guān)介紹和 INFO: ASP.NET State Management Overview。關(guān)于不同 mode 的會(huì)話管理的話題我們后面再討論,先繼續(xù)來(lái)看會(huì)話的建立過程。

在從 machine.config 文件中讀取配置信息后,InitModuleFromConfig 方法會(huì)向 HttpApplication 實(shí)例注冊(cè)幾個(gè)會(huì)話管理事件處理函數(shù),負(fù)責(zé)在應(yīng)用程序合適的情況下維護(hù)會(huì)話狀態(tài)。
以下內(nèi)容為程序代碼:

private void SessionStateModule.InitModuleFromConfig(HttpApplication app,
SessionStateSectionHandler.Config config, bool configInit)
{
// 處理不使用 Cookie 的情況
//...

app.AddOnAcquireRequestStateAsync(new BeginEventHandler(this.BeginAcquireState),
new EndEventHandler(this.EndAcquireState));
app.ReleaseRequestState += new EventHandler(this.OnReleaseState);
app.EndRequest += new EventHandler(this.OnEndRequest);

// 創(chuàng)建會(huì)話管理器
//...
}



BeginAcquireState 和 EndAcquireState 作為一個(gè)異步處理器注冊(cè)到 HttpApplication._acquireRequestStateEventHandlerAsync 字段上;OnReleaseState 則負(fù)責(zé)在合適的時(shí)候清理會(huì)話狀態(tài);OnEndRequest 則是 OnReleaseState 的一個(gè)包裝,負(fù)責(zé)較為復(fù)雜的請(qǐng)求結(jié)束處理。前面提到的 HttpApplication.InitInternal 函數(shù),在完成了初始化工作后,會(huì)將上述這些事件處理器,加入到一個(gè)執(zhí)行隊(duì)列中,由應(yīng)用程序在合適的時(shí)候,使用流水線機(jī)制進(jìn)行調(diào)用,最大化處理效率。有關(guān) ASP.NET 中流水線事件模型的相關(guān)介紹,請(qǐng)參考 HTTP PIPELINES
Securely Implement Request Processing, Filtering, and Content Redirection with HTTP Pipelines in ASP.NET 一文中 The Pipeline Event Model 小節(jié),有機(jī)會(huì)我在寫文章詳細(xì)分析。

知道了會(huì)話建立的調(diào)用流程再來(lái)看會(huì)話的實(shí)現(xiàn)就比較簡(jiǎn)單了,SessionStateModule.BeginAcquireState 被 HttpApplication 實(shí)例在合適的時(shí)候調(diào)用,處理各種會(huì)話的復(fù)雜情況后,使用 SessionStateModule.CompleteAcquireState 函數(shù)完成實(shí)際的會(huì)話建立工作,并將封裝會(huì)話的 HttpSessionState 對(duì)象以 "AspSession" 為 key 加入到 HttpContext 的哈希表中,也就是前面提到的 HttpContext.Context 的由來(lái)。而 SessionStateModule.OnReleaseState 則從 HttpContext 中刪除 "AspSession" 為 key 的 HttpSessionState 對(duì)象,并對(duì)會(huì)話管理器進(jìn)行同步工作。

至此,ASP.NET 中的會(huì)話建立流程大概就分析完畢了,下一小節(jié)將進(jìn)一步展開分析多種不同會(huì)話管理器的實(shí)現(xiàn)原理與應(yīng)用。

to be continue...