ASP.NET Session詳解及Session莫名丟失的原因及解決辦法

來源:互聯網
上載者:User

 

Session模型簡介

Session是什麼呢?簡單來說就是伺服器給用戶端的一個編號。當一台WWW伺服器運行時,可能有若干個使用者瀏覽正在運正在這台伺服器上的網站。當每個使用者首次與這台WWW伺服器建立串連時,他就與這個伺服器建立了一個Session,同時伺服器會自動為其分配一個SessionID,用以標識這個使用者的唯一身份。這個SessionID是由WWW伺服器隨機產生的一個由24個字元組成的字串,我們會在下面的實驗中見到它的實際樣子。

這個唯一的SessionID是有很大的實際意義的。當一個使用者提交了表單時,瀏覽器會將使用者的SessionID自動附加在HTTP頭資訊中,(這是瀏覽器的自動功能,使用者不會察覺到),當伺服器處理完這個表單後,將結果返回給SessionID所對應的使用者。試想,如果沒有SessionID,當有兩個使用者同時進行註冊時,伺服器怎樣才能知道到底是哪個使用者提交了哪個表單呢。當然,SessionID還有很多其他的作用,我們會在後面提及到。

除了SessionID,在每個Session中還包含很多其他資訊。但是對於編寫ASP或ASP.NET的程式與來說,最有用的還是可以通過訪問ASP/ASP.NET的內建Session對象,為每個使用者儲存各自的資訊。例如我們想瞭解一下訪問我們網站的使用者瀏覽了幾個頁面,我們可能在使用者可能訪問到每個的頁面中加入:

<%

If Session("PageViewed") = ""Then

 Session("PageViewed") = 1

Else

 Session("PageViewed") = Session("PageViewed") + 1

End If

%>

通過以下這句話可以讓使用者得知自己瀏覽了幾個頁面:

<%

Response.Write("You have viewed " & Session("PageViewed") & " pages")

%>

可能有些有些讀者會問:這個看似像是數組的Session(“..”)是哪裡來的?需要我定義嗎?實際上,這個Session對象是具有ASP解釋能力的的WWW伺服器的內建對象。也就是說ASP的系統中已經給你定義好了這個對象,你只需要使用就行了。其中Session(“..”)中的..就好像變數名稱,Session(“..”)=$$中的$$就是變數的值了。你只需要寫上句話,在這個使用者的每個頁面中都可以訪問..變數中的值了。

其實ASP一共內建了7個對象,有Session、Application、Cookie、Response、Request、Server等。在其他的伺服器端指令碼語言如JSP、PHP等中也有其類似的對象,只是叫法或者使用方法上不太一樣。

ASP Session的功能的缺陷

目前ASP的開發人員都正在使用Session這一強大的功能,但是在他們使用的過程中卻發現了ASP Session有以下缺陷:

進程依賴性:ASP Session狀態存於IIS的進程中,也就是inetinfo.exe這個程式。所以當inetinfo.exe進程崩潰時,這些資訊也就丟失。另外,重起或者關閉IIS服務都會造成資訊的丟失。

Session狀態使用範圍的局限性:當一個使用者從一個網站訪問到另外一個網站時,這些Session資訊並不會隨之遷移過去。例如:新浪網站的WWW伺服器可能不止一個,一個使用者登入之後要去各個頻道瀏覽,但是每個頻道都在不同的伺服器上,如果想在這些WWW伺服器共用Session資訊怎麼辦呢?

Cookie的依賴性:實際上用戶端的Session資訊是儲存與Cookie中的,如果用戶端完全禁用掉了Cookie功能,他也就不能享受到了Session提供的功能了。

鑒於ASP Session的以上缺陷,微軟的設計者們在設計開發 ASP.NET Session時進行了相應的改進,完全克服了以上缺陷,使得ASP.NET Session成為了一個更加強大的功能。

Web.config檔案簡介

有的ASP.NET程式員說:Web.config檔案?我從來沒有聽說過啊,可是我寫的程式不是也能很正常的運轉嗎?是的,你說得沒錯,沒有Web.config檔案程式是可以正常啟動並執行。但是,如果你做了一個大型的網站,需要對整個網站做一些整體配置,例如整個網站的頁面使用何種語言編寫的、網站的安全認證模式、Session資訊儲存方式等,這時你就需要使用Web.config檔案了。雖然Web.config檔案中的某些選項是可以通過IIS配置的,但是如果在Web.config中也有相應的設定就會覆蓋掉IIS中的配置。而且,Web.config檔案的最大的便利之處就是可以在ASP.NET頁面中通過調用System.web名字空間訪問Web.config中的設定。

Web.config有兩種,分別是伺服器設定檔和Web應用程式設定檔,他們都名為Web.config。在這個設定檔中會儲存當前IIS伺服器中網頁的使用哪種語言編寫的、應用程式安全認證模式、Session資訊儲存方式的一系列資訊。這些資訊是使用XML文法儲存的,如果想對其編輯,使用文字編輯器就行了。

其中伺服器設定檔會對IIS伺服器下所有的網站中的所有應用程式起作用。在.NET Framework 1.0中,伺服器的Web.config檔案是存在:\WinNT\Microsoft.NET\Framework\v1.0.3705中的。

而Web應用程式設定檔Web.config則儲存在各個Web應用程式中。例如:當前網站的根目錄\Inetpub\wwwroot,而當前的Web應用程式為MyApplication,則Web應用程式根目錄就應為:\Inetpub\wwwroot\MyApplication。如果你的網站有且只有一個Web應用程式,一般說來應用程式的根目錄就是\Inetpub\wwwroot。如果想添加一個Web應用程式,在IIS中添加一個具有應用程式起始點的虛擬目錄就行了。這個目錄下的檔案及目錄將被視為一個Web應用程式。但是,這樣通過IIS添加Web應用程式是不會為你產生Web.config檔案的。如果想建立一個帶有Web.config檔案的Web應用程式,需要使用Visual
Studio.NET,建立一個Web應用程式項目。

Web應用程式的設定檔Web.config是可選的,可有可無。如果沒有,每個Web應用程式會使用伺服器的Web.config設定檔。如果有,則會覆蓋伺服器Web.config設定檔中相應的值。

在ASP.NET中,Web.config修改儲存後會自動立刻成效,不用再像ASP中的設定檔修改後需要重新啟動Web應用程式才會生效了。

Web.config檔案中的Session配置資訊

開啟某個應用程式的設定檔Web.config後,我們會發現以下這段:

<sessionState

mode="InProc"

stateConnectionString="tcpip=127.0.0.1:42424"

sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes"

cookieless="false"

timeout="20"

/>

這一段就是配置應用程式是如何儲存Session資訊的了。我們以下的各種操作主要是針對這一段配置展開。讓我們先看看這一段配置中所包含的內容的意思。sessionState節點的文法是這樣的:

<sessionState mode="Off|InProc|StateServer|SQLServer"

cookieless="true|false"

timeout="number of minutes"

stateConnectionString="tcpip=server:port"

sqlConnectionString="sql connection string"

stateNetworkTimeout="number of seconds"

/>

必須有的屬性是

屬性 選項 描述

mode 設定將Session資訊儲存到哪裡

Off 設定為不使用Session功能

InProc 設定為將Session儲存在進程內,就是ASP中的儲存方式,這是預設值。

StateServer 設定為將Session儲存在獨立的狀態服務中。

SQLServer 設定將Session儲存在SQL Server中。

可選的屬性是:

屬性 選項 描述

cookieless 設定用戶端的Session資訊儲存到哪裡

ture 使用Cookieless模式

false 使用Cookie模式,這是預設值。

timeout 設定經過多少分鐘後伺服器自動放棄Session資訊。預設為20分鐘

stateConnectionString 設定將Session資訊儲存在狀態服務中時使用的伺服器名稱和連接埠號碼,例如:"tcpip=127.0.0.1:42424”。當mode的值是StateServer是,這個屬性是必需的。

sqlConnectionString 設定與SQL Server串連時的連接字串。例如"data source=localhost;Integrated Security=SSPI;Initial Catalog=northwind"。當mode的值是SQLServer時,這個屬性是必需的。

stateNetworkTimeout 設定當使用StateServer模式儲存Session狀態時,經過多少秒空閑後,斷開Web伺服器與儲存狀態資訊的伺服器的TCP/IP串連的。預設值是10秒鐘。

ASP.NET中用戶端Session狀態的儲存

在我們上面的Session模型簡介中,大家可以發現Session狀態應該儲存在兩個地方,分別是用戶端和伺服器端。用戶端只負責儲存相應網站的SessionID,而其他的Session資訊則儲存在伺服器端。在ASP中,用戶端的SessionID實際是以Cookie的形式儲存的。如果使用者在瀏覽器的設定中選擇了禁用Cookie,那末他也就無法享受Session的便利之處了,甚至造成不能訪問某些網站。為瞭解決以上問題,在ASP.NET中用戶端的Session資訊儲存方式分為:Cookie和Cookieless兩種。

ASP.NET中,預設狀態下,在用戶端還是使用Cookie儲存Session資訊的。如果我們想在用戶端使用Cookieless的方式儲存Session資訊的方法如下:

找到當前Web應用程式的根目錄,開啟Web.Config檔案,找到如下段落:

<sessionState

mode="InProc"

stateConnectionString="tcpip=127.0.0.1:42424"

sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes"

cookieless="false"

timeout="20"

/>

這段話中的cookieless="false"改為:cookieless="true",這樣,用戶端的Session資訊就不再使用Cookie儲存了,而是將其通過URL儲存。關閉當前的IE,開啟一個新IE,重新訪問剛才的Web應用程式,就會看到類似下面的樣子:

其中,http://localhost/MyTestApplication/(ulqsek45heu3ic2a5zgdl245)/default.aspx中黑體標出的就是用戶端的Session ID。注意,這段資訊是由IIS自動加上的,不會影響以前正常的串連。

ASP.NET中伺服器端Session狀態的儲存

準備工作

為了您能更好的體驗到實驗現象,您可以建立一個叫做SessionState.aspx的頁面,然後把以下這些代碼添加到<body></body>中。

<scriptrunat="server">

Sub Session_Add(sender As Object, e As EventArgs)

  Session("MySession") = text1.Value

  span1.InnerHtml = "Session data updated! <P>Your session contains: <font color=red>" & \

Session("MySession").ToString() & "</font>"

End Sub

Sub CheckSession(sender As Object, eAs EventArgs)

  If (Session("MySession")Is Nothing) Then

  span1.InnerHtml = "NOTHING, SESSION DATA LOST!"

  Else

  span1.InnerHtml = "Your session contains: <font color=red>" & \

Session("MySession").ToString() & "</font>"

End If

End Sub

</script>

<formrunat="server"id="Form2">

  <inputid="text1"type="text"runat="server"name="text1">

  <inputtype="submit"runat="server"OnServerClick="Session_Add"

  value="Add to Session State" id="Submit1"name="Submit1">

  <inputtype="submit"runat="server"OnServerClick="CheckSession"

  value="View Session State" id="Submit2"name="Submit2">

</form>

<hrsize="1">

<fontsize="6"><spanid="span1"runat="server" /></font>

這個SessionState.aspx的頁面可以用來測試在當前的伺服器上是否丟失了Session資訊。

將伺服器Session資訊儲存在進程中

讓我們來回到Web.config檔案的剛才那段段落中:

<sessionState

mode="InProc"

stateConnectionString="tcpip=127.0.0.1:42424"

sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes"

cookieless="false"

timeout="20"

/>

當mode的值是InProc時,說明伺服器正在使用這種模式。

這種方式和以前ASP中的模式一樣,就是伺服器將Session資訊儲存在IIS進程中。當IIS關閉、重起後,這些資訊都會丟失。但是這種模式也有自己最大好處,就是效能最高。應為所有的Session資訊都儲存在了IIS的進程中,所以IIS能夠很快的訪問到這些資訊,這種模式的效能比進程外儲存Session資訊或是在SQL Server中儲存Session資訊都要快上很多。這種模式也是ASP.NET的預設。

好了,現在讓我們做個實驗。開啟剛才的SessionState.aspx頁面,隨便輸入一些字元,使其儲存在Session中。然後,讓我們讓IIS重起。注意,並不是使當前的網站停止再開始,而是在IIS中原生機器名的節點上點擊滑鼠右鍵,選擇重新啟動IIS。(想當初使用NT4時,重新啟動IIS必須要重新啟動電腦才行,微軟真是@#$%^&)返回到SessionState.aspx頁面中,檢查剛才的Session資訊,發現資訊已經丟失了。

將伺服器Session資訊儲存在進程外

首先,讓我們來開啟管理工具->服務,找到名為:ASP.NET State Service的服務,啟動它。實際上,這個服務就是啟動一個要儲存Session資訊的進程。啟動這個服務後,你可以從Windows工作管理員->進程中看到一個名為aspnet_state.exe的進程,這個就是我們儲存Session資訊的進程。

然後,回到Web.config檔案中上述的段落中,將mode的值改為StateServer。儲存檔案後的重新開啟一個IE,開啟SessionState.aspx頁面,儲存一些資訊到Session中。這時,讓我們重起IIS,再回到SessionState.aspx頁面中查看剛才的Session資訊,發現沒有丟失。

實際上,這種將Session資訊儲存在進程外的方式不光指可以將資訊儲存在原生進程外,還可以將Session資訊儲存在其他的伺服器的進程中。這時,不光需要將mode的值改為StateServer,還需要在stateConnectionString中配置相應的參數。例如你的計算你是192.168.0.1,你想把Session儲存在IP為192.168.0.2的電腦的進程中,就需要設定成這樣:stateConnectionString="tcpip=192.168.0.2:42424"。當然,不要忘記在192.168.0.2的電腦中裝上.NET
Framework,並且啟動ASP.NET State Services服務。

將伺服器Session資訊儲存在SQL Server中

首先,還是讓我們來做一些準備工作。啟動SQL Server和SQL Server代理服務。在SQL Server中執行一個叫做InstallSqlState.sql的指令檔。這個指令檔將在SQL Server中建立一個用來專門儲存Session資訊的資料庫,及一個維護Session資訊資料庫的SQL Server代理作業。我們可以在以下路徑中找到那個檔案:

[system drive]\winnt\Microsoft.NET\Framework\[version]\

然後開啟查詢分析器,串連到SQL Server伺服器,開啟剛才的那個檔案並且執行。稍等片刻,資料庫及作業就建立好了。這時,你可以開啟企業管理器,看到新增了一個叫ASPState的資料庫。但是這個資料庫中只是些預存程序,沒有使用者表。實際上Session資訊是儲存在了tempdb資料庫的ASPStateTempSessions表中的,另外一個ASPStateTempApplications表格儲存體了ASP中Application對象資訊。這兩個表也是剛才的那個指令碼建立的。另外查看管理->SQL Server代理->作業,發現也多了一個叫做ASPState_Job_DeleteExpiredSessions的作業,這個作業實際上就是每分鐘去ASPStateTempSessions表中刪除到期的Session資訊的。

接著,我們返回到Web.config檔案,修改mode的值改為SQLServer。注意,還要同時修改sqlConnectionString的值,格式為:

sqlConnectionString="data source=localhost; Integrated Security=SSPI;"

其中data source是指SQL Server伺服器的IP地址,如果SQL Server與IIS是一台機子,寫127.0.0.1就行了。Integrated Security=SSPI的意思是使用Windows整合身分識別驗證,這樣,訪問資料庫將以ASP.NET的身份進行,通過如此配置,能夠獲得比使用userid=sa;password=口令的SQL Server驗證方式更好的安全性。當然,如果SQL Server運行於另一台電腦上,你可能會需要通過Active Directory域的方式來維護兩邊驗證的一致性。

同樣,讓我們做個實驗。向SessionState.aspx中添加Session資訊,這時發現Session資訊已經存在SQL Server中了,即使你重起電腦,剛才的Session資訊也不會丟失。現在,你已經完全看見了Session資訊到底是什麼樣子的了,而且又是儲存在SQL Server中的,能幹什麼就看你的發揮了。

總結

通過這篇文章,你可以看到在Session的管理和維護上,ASP.NET比ASP有了很大的進步,我們可以更加隨意的挑選適合的方法了。對於企業級的應用來說,這無疑對於伺服器的同步、伺服器的穩定性、可靠性都是有利的。相信在強大的微軟支援下,新一代的電子商務平台將會搭建的更好!

---------------------------------------------
附:Asp.net 預設配置下,Session莫名丟失的原因及解決辦法

由於Asp.net程式是預設配置,所以Web.Config檔案中關於Session的設定如下:
<sessionState mode='InProc' stateConnectionString='tcpip=127.0.0.1:42424' sqlConnectionString='data source=127.0.0.1;Trusted_Connection=yes' cookieless='true' timeout='60'/>

我們會發現sessionState標籤中有個屬性mode,它可以有3種取值:InProc、StateServer、SQLServer(大小寫敏感)。預設情況下是InProc,也就是將Session儲存在進程內(IIS5是aspnet_wp.exe,而IIS6是W3wp.exe),這個進程不穩定,在某些事件發生時,進程會重起,所以造成了儲存在該進程內的Session丟失。

哪些情況下該進程會重起呢?微軟的一篇文章告訴了我們:
1、設定檔中processModel標籤的memoryLimit屬性
2、Global.asax或者Web.config檔案被更改
3、Bin檔案夾中的Web程式(DLL)被修改
4、殺毒軟體掃描了一些.config檔案。
更多的資訊請參考PRB: Session variables are lost intermittently in ASP.NET applications

解決辦法:

前面說到的sessionState標籤中mode屬性可以有三個取值,除了InProc之外,還可以為StateServer、SQLServer。這兩種存Session的方法都是進程外的,所以當aspnet_wp.exe重起的時候,不會影響到Session。

現在請將mode設定為StateServer。StateServer是原生一個服務,可以在系統服務裡看到服務名為ASP.NET State Service的服務,預設情況是不啟動的。當我們設定mode為StateServer之後,請手工將該服務啟動。

這樣,我們就能利用原生StateService來儲存Session了,除非電腦重啟或者StateService崩掉,否則Session是不會丟的(因Session逾時被丟棄是正常的)。

除此之外,我們還可以將Session通過其他電腦的StateService來儲存。具體的修改是這樣的。同樣還在sessionState標籤中,有個stateConnectionString='tcpip=127.0.0.1:42424'屬性,其中有個ip地址,預設為本機(127.0.0.1),你可以將其改成你所知的運行了StateService服務的電腦IP,這樣就可以實現位於不同電腦上的Asp.net程式互連Session了。

如果你有更高的要求,需要在服務期重啟時Session也不丟失,可以考慮將mode設定成SQLServer,同樣需要修改sqlConnectionString屬性。關於使用SQLServer儲存Session的操作,請訪問這裡。

在使用StateServer或者SQLServer儲存Session時,所有需要儲存到Session的對象除了基礎資料型別 (Elementary Data Type)(預設的資料類型,如int、string等)外,都必須序列化。只需將[Serializable]標籤放到要序列化的類前就可以了。
如:
[Serializable]
public class MyClass
{
     ......
}
具體的序列化相關的知識請參這裡。

至此,問題解決。

參考文章:
ASP.NET Session State FAQ
ASP.NET Session State
[ASP.NET] Session 詳解
PRB: Session Data Is Lost When You Use ASP.NET InProc Session State Mode
PRB: Session Data Is Lost When You Use ASP.NET InProc Session State Mode
ASP.NET HTTP 運行時
.NET 中的對象序列化

-------------------------------------------------------

附:

可能的原因1:

win2003 server下的IIS6預設設定下對每個運行在預設應用池中的工作者進程都會經過20多個小時後自動回收該進程,造成儲存在該進程中的session丟失。

因為Session,Application等資料預設儲存在運行該Web應用程式的工作者進程中,如果回收工作者進程,則會造成丟失。

解決辦法:

修改配置,設定為不定時自動回收該工作者進程,比如設定為當超出佔用現有實體記憶體60%後自動回收

該進程。通過使用預設應用程式集區,可以確保多個應用程式間互相隔離,保證由於一個應用程式的崩潰不會影響另外的Web應用程式。還可以使一個獨立的應用程式運行在一個指定的使用者帳號特權之下。如果使用StateServer方式或者Sql Server資料庫方式來儲存Session,則不受該設定的影響。

可能的原因2:

系統要運行在Server Load Balancer的 Web 場環境中,而系統設定檔web.config中的Session狀態卻設定為InProc(即在本機存放區工作階段狀態),導至在使用者訪問量大時,Session常經逾時的情況。引起這個現象的原因主要是因為使用者通過Server Load BalancerIP來訪問WEB應用系統,某段時候在某台伺服器儲存了Session的工作階段狀態,但在其它的WEB前端伺服器中卻沒有儲存Session的工作階段狀態,而隨著並發量的增大,Server Load Balancer會當作路由隨時訪問閒置伺服器,結果閒置伺服器並沒有之前儲存的Session工作階段狀態。

解決辦法:
1.當您在Server Load Balancer的 Web 場環境中運行 ASP.NET Web 應用程式時,一定要使用 SqlServer 或 StateServer 工作階段狀態模式,在項目中我們基於效能考慮並沒有選擇SqlServer模式來儲存Session狀態,而是選擇一台SessionStateServer 伺服器來使用者的Session工作階段狀態。我們要在系統設定檔web.config中設定如下:
<sessionState mode="StateServer" cookieless="false" timeout="240" stateConnectionString="tcpip=192.168.0.1:42424" stateNetworkTimeout="14400" />

還要添加一項
<machineKey validationKey="78AE3850338BFADCE59D8DDF58C9E4518E7510149C46142D7AAD7F1AD49D95D4" decryptionKey="5FC88DFC24EA123C" validation="SHA1"/>

2. 我們同時還要在SessionStateServer 伺服器中啟動ASP.NET State Service服務,具體設定:控制台>>管理工具>>服務>>ASP.NET State Service,把它設為自動啟動即可。

3. 每台前端WEB服務的Microsoft“Internet 資訊服務”(IIS)設定
要在 Web 場中的不同 Web 服務器間維護工作階段狀態,Microsoft“Internet 資訊服務”(IIS) 設定資料庫中 Web 網站的應用程式路徑(例如,\LM\W3SVC\2)與 Web 場中所有 Web 服務器必須相同。大小寫也必須相同,因為應用程式路徑是區分大小寫。在一台 Web 服務器上,承載 ASP.NET 應用程式的 Web 網站的執行個體 ID 可能是 2(其中應用程式路徑是 \LM\W3SVC\2)。在另一台 Web 服務器上,Web 網站的執行個體 ID 可能是 3(其中應用程式路徑是
\LM\W3SVC\3)。因此,Web 場中的 Web 服務器之間的應用程式路徑是不同的。我們必須使Web 場Web 網站的執行個體 ID 相同即可。你可以在IIS中把某一個WEB配置資訊儲存為一個檔案,其他Web 服務器的IIS配置可以來自這一個檔案。您如果想知道具體的設定請訪問Microsoft Support網站:

補充一些相關資料:
PRB: Session Variables Are Lost If You Use FRAMESET in Internet Explorer 6.0
http://support.microsoft.com/kb/323752/EN-US/#

PRB: Session Data Is Lost When You Use ASP.NET InProc Session State Mode
http://support.microsoft.com/?id=324772

PRB:如果您使用 SqlServer 或 StateServer 會話模式 Web 場中會丟失工作階段狀態
http://support.microsoft.com/default.aspx?scid=kb;zh-cn;325056

ASP.NET Session State FAQ
http://www.eggheadcafe.com/articles/20021016.asp

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.