Windows Azure - 使用Windows Azure Storage解決Sessios問題
2012/6/16 更新 目前官方已經提出新的Cache來解決Session了,請參考這篇和這篇
2012/5/28 更新 根據官方說法,如果使用Table當Sesson,碰上一個Role裡面有多個Web Site時,可會出問題,所以官方建議使用Caching或是SQL Server,是比較建議的做法。
有玩過Windows Azure的人,應該都會知道Web Role可以是多個的 ( 只要口袋夠深 ),就好比下面這張圖 (在微軟的邊做邊學找到的)一樣,我們可以在HTTP的請求進來的時候,利用六台的Web Role來進行服務,這也就是Load Balance的機制。
其實Windows Azure這點很棒的,因為我們可以輕鬆地去平衡大量的需求,當流量多的時候,我們就多開幾台Web Role,當人少的時候,我們就少開幾台Web Role!而且我們不需要做任何的事情!
當然,話是這樣說,現實總是比較殘酷一點的,Load Balance機制有一個最大的特點,舉例來說,我們現在有Web RoleA和Web RoleB兩台提供服務,當進入Default.aspx( 首頁 )的時候,可能會在Web RoleA,而點下Login.aspx ( 登入頁面 )的時候,可能是由Web RoleB來提供服務,超酷的吧!!
但問題來了,如果今天在Web RoleB登入,並在Web RoleB利用Session來記錄登入資訊,當下一頁跑到Web RoleA的時候,我相信,客戶就會打電話進來罵了,然後我們就會被老闆罵了…。
會發生這個原因其實很簡單,因為Session通常都記錄在記憶體裡面,而我們登入的資訊記錄在Web RoleB的記憶體上,當下一頁切換到Web RoleA的時候,當然就會找不到內容。
話說回來,我相信如果如果比較大的公司,有導入Load Balance機制的程式開發人員,一定都會針對這方面來,避免使用Session寫程式,或是將資訊寫到資料庫,或是利用Cookie等等的方法,但隨著時代進步,超方便的Windows Azure上市,讓中小企業也可以享受到Load Balance機制,而這時如果舊有的程式,沒有針對Load Balance機制去做設計,然後就發佈到雲端上,就會發生上面所講的問題,被老闆罵了。
( 如果只放一台Web Role,當然就不會有這種問題了。 )
沒圖沒真相
所謂沒圖沒真相,所以小弟我稍微寫了一個超簡單的程式來驗證一下。
小弟我寫了兩頁A.aspx和B.aspx。
反正是自己機器上面模擬,就給他開個四台吧。
A.aspx的頁面也超級簡單,就放一個Label和一個超連結。
`<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="A.aspx.cs" Inherits="AzureAspNetSession.A" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label> <a href=B.aspx>Go B Page</a> </div> </form> </body> </html>`
A.aspx.cs的程式碼也超簡單,但這邊我使用了RoleEnvironment.CurrentRoleInstance.Id來識別目前這台Web Role是哪一台,另外,就是將session存了一個A PageSession的字串。
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using Microsoft.WindowsAzure.ServiceRuntime; namespace AzureAspNetSession { public partial class A : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Label1.Text = RoleEnvironment.CurrentRoleInstance.Id; Session.Clear(); Session["Test"] = "A PageSession"; } } }
B.aspx
`<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="B.aspx.cs" Inherits="AzureAspNetSession.B" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label> <asp:Label ID="Label2" runat="server" Text="Label"></asp:Label> </div> </form> </body> </html>`
B.aspx.cs的程式碼也很簡單,如果Session不是空,就Show出來,如果是空,就Show出Session is null,當然,也要把目前是哪一台Web Role Show出來。
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using Microsoft.WindowsAzure.ServiceRuntime; namespace AzureAspNetSession { public partial class B : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Label1.Text = RoleEnvironment.CurrentRoleInstance.Id; if(Session["Test"] != null) { Label2.Text = Session["Test"].ToString(); }else { Label2.Text = "Session is null"; } } } }
然後執行看看,我們可以看到,目前是連到第二台Web Role,然後我們按下 Go B Page的超連結。
到B.aspx,Session卻是Null!,那是因為我們現在連到第一台。
所以,這個實驗可以發現,如果連到別台,Session是真的可能會抓不到,所以建議,如果寫這方面程式的時候,盡量不要用Session。
現實是殘酷的
沒錯,現實是殘酷的,當老闆要求幾天內將程式搬到Windows Azure上去,而且要開兩台以上;但是程式裡面已經一堆Session的時候,該怎麼辦!? ( 實際上可能還會考慮到更多問題,反正這只是一個梗,大家就不要太計較了XDD ),其實還有幾種方法,我們這邊就先使用最簡單的方法吧!也就是將Session存到Windows Azure Storage裡面去。
動手寫程式
我們延續之前的範例,首先,我們要先在Web.Config裡面的System.web下,加上這一串設定。
<?xml version="1.0" encoding="utf-8"?> <!-- 如需如何設定 ASP.NET 應用程式的詳細資訊,請造訪 http://go.microsoft.com/fwlink/?LinkId=169433 --> <configuration> <system.web> <compilation debug="true" targetFramework="4.0" /> <!-- 加入以下 --> <sessionState mode="Custom" customProvider="TableStorageSessionStateProvider"> <providers> <clear/> <add name="TableStorageSessionStateProvider" type="Microsoft.Samples.ServiceHosting.AspProviders.TableStorageSessionStateProvider"/> </providers> </sessionState> <!-- 到此為止 --> </system.web> </configuration>
接下來,很不幸的,我們必須去下載一個dll,大家可以從這邊下載,這是官網提供的一個範例,內容就是在講如何使用Windows Azure Storage來存Session,下載完後,將之解壓縮,我們就可以看到下有一個C#\AspProviders\bin\Debug\AspProviders.dll檔案,這個檔案就是關鍵,然後我們就可以回到我們的專案,將這個檔案參考進來,參考進來後會如下圖。
接下來,點一下AspProviders,然後看一下"複製到本機"是否為True。
接下來,我們要設定連線字串,這裡我們使用的是Local的Storage,如果要使用Cloud上的,可以自己在這邊設定,如果不會修改,可以參考這篇。
設定完後,就可以開始執行看看了,一樣我們先從A.aspx開始。
當我們切換到B.aspx時,可以發現,現在在另外一台Web Role上,但是他卻抓的Session了。
我們可以從伺服器總管查看Blob和Table,發現,他現在將Session存在這邊了。
這樣就完成嚕!!
後記
目前官方並沒有正式提供Library來實作這塊,官方比較建議利用Azure Caching來配合Session或是SQL Server,但不管怎樣,Table至少是最便宜的方法,至於好或不好,就見仁見智嚕。
參考資料
- http://www.dotblogs.com.tw/robinson/archive/2010/11/19/19570.aspx
- http://blogs.blackmarble.co.uk/blogs/sspencer/post/2011/09/22/session-state-in-windows-azure.aspx
- http://niallbest.com/using-windows-azure-tables-to-persist-session-data/
- http://code.msdn.microsoft.com/windowsazure/Windows-Azure-ASPNET-03d5dc14/view/SourceCode