多站点会话共享

现在以应对各种需求.需要将一个站点分离成N个子站点,这比想像中多了些阻碍.在我做过一个多站点分布后.就发现了会话不能共享的问题.下面谈谈解决方案:
 一.
     会话数据保存在指定的SQLServer数据库中,会话数据通过SessionID进行查找,理论上只需要使多个站点共享同一会话数据库即可共享会话数据,确定了思路便好办。多个站点共享统一会话数据库在.NET下是很容易的事情,只需要在WEB.CONFIG里配置sessionState节点指定会话存储模式以及相同的会话数据库即可。事情没有这么简单,不同的站点其SessionID是不同的,就算SessionID相同了,保存在会话数据库中的实际SessionID的值也不只是网站生成的SessionID,而是在SessionID后加上了站点的AppName,因此即使各个站点共享了会话数据库也不能共享会话数据。打开会话数据表ASPStateTempSessions中的记录,可以发现如下几个数据

ASPStateTempSessions各个字段的意义如下:

表1 ASPStateTempSessions表

类 型

描 述

SessionId

char(88)

索引字段,它表示会话ID

Created

DateTime

指出会话被创建的时间。默认值为当前时间

Expires

DateTime

指出会话将到期的时间。该值一般等于会话状态的创建时间加上Timeout中指定的分钟数。注意,Created指会话的创建时间,而Expires把分钟数加到第一个数据项被添加到会话状态的时间

LockDate

DateTime

指出会话被锁定以添加最后一个数据项的时间。该值表示为当前的UTC(Universal Time Coordinate)时间

LockDateLocal

DateTime

与LockDate一样,但是它只表示系统的本地时间。ASP.NET 1.x不支持该列

LockCookie

int

指出该会话被锁定的次数——即,访问次数

Timeout

int

指出会话的超时时间(以分为单位)

Locked

bit

指出会话当前没有被锁定

SessionItemShort

VarBinary(7000)

可以取null的字段。它表示指定会话中的值。这些字节的布局等同于StateServer提供程序所述的布局。如果对字典进行序列化需要7000多字节,则使用SessionItemLong

SessionItemLong

Image

可以取null的字段,表示一个超过7000字节的会话状态的序列化版本

Flags

Int

指示SessionStateActions枚举类型的行动标记(初始化数据项)。ASP.NET 1.x不支持该列




其中的SessionId包括两个部分:网站生成的24位SessionID及8位AppName(这个AppName是怎么来的呢?)对于不同的站点,其AppName不同,在能够在不同站点下使24位SessionID相同的情况下,要保证经过组合加上AppName后的SessionID相同,可以通过修改存储过程TempGetAppID,使其得到的SessionID与AppName无关,修改TempGetAppID如下:
 1
 2    CREATE PROCEDURE dbo.TempGetAppID
 3    @appName    tAppName,
 4    @appId      int OUTPUT
 5    AS
 6    SET @appName = LOWER(@appName)
 7    SET @appId = NULL
 8
 9    SELECT @appId = AppId
10    FROM [JSEC_SessionDB].dbo.ASPStateTempApplications
11   -- WHERE AppName = @appName   //屏蔽该行
12
13    IF @appId IS NULL BEGIN
14        BEGIN TRAN        
15
16        SELECT @appId = AppId
17        FROM [JSEC_SessionDB].dbo.ASPStateTempApplications WITH (TABLOCKX)
18        WHERE AppName = @appName
19        
20        IF @appId IS NULL
21        BEGIN
22            EXEC GetHashCode @appName@appId OUTPUT
23            
24            INSERT [JSEC_SessionDB].dbo.ASPStateTempApplications
25            VALUES
26            (@appId@appName)
27            
28            IF @@ERROR = 2627 
29            BEGIN
30                DECLARE @dupApp tAppName
31            
32                SELECT @dupApp = RTRIM(AppName)
33                FROM [JSEC_SessionDB].dbo.ASPStateTempApplications 
34                WHERE AppId = @appId
35                
36                RAISERROR('SQL session state fatal error: hash-code collision between applications ''%s'' and ''%s''. Please rename the 1st application to resolve the problem.'
37                            181@appName@dupApp)
38            END
39        END
40
41        COMMIT
42    END
43
44    RETURN 0
45GO
46

经过以上修改之后,下面要实现多个站点共用同一个SessionID,对ASP.NET的会话模型System.Web.SessionState.SessionIDManager.GetSessionID(HttpContext context)方法进行反编译,分析其源代码:

 1 public string GetSessionID(HttpContext context)
 2 {
 3     string id = null;
 4     this.CheckInitializeRequestCalled(context);
 5     if (this.UseCookieless(context))
 6     {
 7         return (string) context.Items["AspCookielessSession"];
 8     }
 9     HttpCookie cookie = context.Request.Cookies[Config.CookieName];
10     if ((cookie != null&& (cookie.Value != null))
11     {
12         id = this.Decode(cookie.Value);
13         if ((id != null&& !this.ValidateInternal(id, false))
14         {
15             id = null;
16         }
17     }
18     return id;
19 }
20 
21  
22 

可以看到,SessionID是通过客户端的Cookie进行保存的,因此,只要使各个站点共用同一个Cookie文件保存SessionID即可达到目的。如何使不同的站点共用同一个Cookie呢?只要设置cookie.Domain为相同的域即可。在页面的PageLoad事件中加上以下代码:

1         HttpCookie cookie = HttpContext.Current.Request.Cookies["ASP.NET_SessionId"];  //"ASP.NET_SessionId"是ASP.NET会话模型中默认的SessionId变量名称
2         cookie.Domain = ".websitename.com";
3         cookie.Expires = DateTime.Now.AddDays(365); //设置Cookie保存的天数,不可少于1天
4         HttpContext.Current.Response.Cookies.Add(cookie);


经过以上步骤之后,终于真正的实现了多站点会话共享。 

 

二.

和上面前面一样.在WEB.CONFIG里配置sessionState节点指定会话存储模式以及相同的会话数据库.由上面第一种方法分析可以知道.AppName不一样.上面是让它忽略AppName.下面就是让他AppName一样.这样也能达到同样效果.具体做法是:

在每个子站点必须设置相同路径主目录(比如.有三台服务器.WEB程序IIS主目录都应指向同一位置.如都在D:\WebRoot下)

这样.AppName会得一个相同的值.

注意:如果你是在已经建立好的IIS站点上.采用这种模式.建议选删除原来的WEB站点.再新建成每台都一样的物理路径的IIS站点.我的理解是.一但建立就生成了AppName.以后修改它是不会变的(不知道是不是这个原因,但我就是这样才解决的.删除其它已建立好的.重新建立的),所以保证真正的AppName一样.得严格按照上面的做法.

 

posted @ 2009-04-01 11:12  LeonSky  阅读(1384)  评论(1编辑  收藏  举报