分站点部署的小总结——多站点会话共享

随着IP访问量的增长,公司考虑对站点按频道进行分离。
将一个站点分离成N个子站点有诸多好处:

    1. Web服务器的分压:来自客户端的请求将分散到各个站点分别处理
    2. 有利于多服务器部署:每个站点都可以部署到独立的服务器上
    3. 降低系统的耦合度:分离站点后,每个站点可以进行独立的升级管理,而不必顾虑是否影响其他频道

可能还有其他的一些好处,但我还没有想到

将原来的一个站点分离成N个子站点,我是第一次做这事,其中存在的许多困难一开始根本想不到。首先要考虑的就是多站点单点登录的问题,用户从任何一个子站 点登录之后,进入其他的子站点,都能自动进行身份验证而不需要再登录。这是多站点部署所要解决的最首要的问题,由于公司现有系统验证登录用户身份的方式是 通过保存在会话数据中的用户信息,用户在登录之后,实例化一个自定义的会话对象,在这个对象里保存了经过加密的用户身份、权限等信息,要求必须在各个子站 点都能访问这个会话对象。因此必须实现各个站点间的会话数据共享。网上可以找到很多这方面的资料,但都不是我想要实现的,例如:

    1、采用IIS虚拟目录的方式,把多个站点放在同一个站点中,这种方法虽然能解决会话共享,但并不是真正的站点分离。
    2、采用SSO的方法,虽然能够实现多站点用户身份验证,但是由于现有系统的特殊性,也不能满足要求

会话数据保存在指定的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);

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

 

到目前为止,还有一些问题没有想明白:

    
数据库中保存的SessionID的后8位代表什么?与表ASPStateTempApplications中的AppName有什么关系?

posted on 2008-01-03 11:18  xiaoyz  阅读(471)  评论(1)    收藏  举报

导航