.net,sessionState的Session共享问题解决方案
最近项目因为要负载均衡所以就使用了sessionState的Session共享,但是却发现多台服务器中有个别服务器的Session没有共享,于是就有了这篇文章,下面开始说说。
这个基本上就分两种情况:一种就是在多台服务器上,在IIS中建立的网站的标识符一样(要想都一样,需要在建网站的时候输入同样的描述符,就我知道的方 法,只有在第一次建这个网站的时候,输入的描述符要一样,建好后再改描述符是没用的,它会保留以前的标识符,另外这种情况有一个特例,就是默认网站,虽然 在iis中可以看到默认网站的标识符都是1,但是它不属于第一种情况,它属于第二情况)。
另外一种情况是,网站的标识符不一样,比如在两台服务器上不同名的网站,或者在同一台服务器上的两个或多个网站(在同一IIS下这种情况根本就不可能会有相同标识符的网站),还有就是默认网站的情况。
这个我在网上有看到别人说,说到要起相同网站名的文章有一篇,但那个说的不对,它只是强调起相同的名字,其实真正是要IIS中网站的标识符一样,不是它们的网站名(也就是描述符)一样,同一IIS下也能有两个网站拥有相同的标识符。这点一定要注意。
另外要说的是,如果使用cookie来存储sessionid的话,这个一定要确保这些共享session的网站在网页浏览器中可以使用相同的域名来访问 到,比如a.test.com,b.test.com,c.test.com等等的.test.com域的网站。这些网站所在的计算机或者说服务器不非得 真的处在这样的一个域或者网站有这样的一个域名什么的,只要能让浏览器所在的计算机通过这些域名访问到这些服务器就行,我自己的测试环境就是家里的两台计 算机的一个小内网,也没有域,只是修改了使用浏览器做测试的机器上的C:\WINDOWS\system32\drivers\etc\host,让浏览 器可以使用那些域名访问到另外的服务器即可。这个也说明在客户端的浏览器使用cookie的一个机制,只要是地址栏里输入的顶级域一样,比如访问 a.test.com,b.test.com什么的,只要都是test.com的,它就会使用那些cookie了。所以说要确保这些服务器都在同一个域名 下面,这样这些服务器才能得到一样的sessionid,因为asp.net会把sessionid存储在叫ASP.NET_SessionId的 cookie中,只有访问同样的域名的网站才会发送这个COOKIE,不然访问其他域名的网站,比如a.test1.com,浏览器不会发送这个 cookie,即使使用同样的stateserver,不能得到相同的sessionid,也无法共享session。
先说一下stateserver的web.config等的配置,这些不论是上面说的哪两种情况都是一样的,在web.config中需要添加以下两个节点:
|
1
2
3
4
|
<!--web.config中sessionState节点的配置方案--><sessionState cookieName="DotNesession" mode="StateServer" stateConnectionString="tcpip=127.0.0.1:42424" cookieless="false" timeout="30" /><httpRuntime targetFramework="4.5" /><machineKey validationKey="D4033EDA0C4490B94331CAD18C43A72146BC0083FBB0D5ABE876A43EE271904EB2DAE181096561A451F7ADF61ED9EDBE40C0B920ED45682F96A8EA2788B96913" decryptionKey="DDE7A8EF5E6B8C31DA73F8D942FF28B0790BF0F2446202BBA0F80F39A5375BAA" validation="SHA1" decryption="AES" /> |
这个machineKey的值可以是随意的,但一定要配成一样的,因为需要用它来给session进行解密。另外要说的就是如果stateserver配为远程的服务器的话,则需要修改stateserver服务器的注册表的[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\aspnet_state\Parameters]中的AllowRemoteConnection,把它改成1,默认是不允许远程连接的,值为0,另外在这里也可以改Port 端口号,默认的stateserver需要用的asp.net状态服务需要监听42424端口。然后在stateserver上启动asp.net状态服务,另外要是先启动了这个服务,再修改的注册表,则需要再重启一下这个服务。
最后说一下以上两种情况分别需要写不同的处理程序,其实处理第二种情况的程序也能处理第一种情况,不过第一种情况的程序比第二种的简单。在这里先说一下分 类的标准,这是根据,在如果没有写处理程序的情况下,stateserver会把这些要共享session的网站产生的session放置到 appdomin的情况来区分的。
第一种情况,因为网站的标识ID是相同的,当然除了默认网站那个特例,因为aps.net状态服务应该是根据网站的标识id来决定放置session的 appdomain的id, 所以放置这些网站的appdomain的id也是相同的,因此这些网站产生的session会被放在同一个appdomain中,所以处理程序很简单,只 要确保ASP.NET_SessionId这个cookie能在这些网站中共享就行了。网上给出来的通用做 法,就是在自定义实现IHttpModule的自定义类中,在request结束的事件中,改写ASP.NET_SessionId的cookie的 domain为这些网站的主域名即可,按上例来说,即.test.com。代码如下:

public class Test:IHttpModule
{
#region IHttpModule 成员 void IHttpModule.Dispose()
{
//throw new Exception("The method or operation is not implemented.");
} void IHttpModule.Init(HttpApplication context)
{
context.EndRequest += new EventHandler(this.EndRequest);
} #endregion private void EndRequest(object sender, EventArgs args)
{
HttpApplication application = sender as HttpApplication;
for (int i = 0; i < application.Response.Cookies.Count; i++ )
{
if( application.Response.Cookies[i].Name == "ASP.NET_SessionId")
application.Response.Cookies[i].Domain = ".test.com";
}
}
}

在EndRequest当中,或在此类中的其他相应方法中,切记不要没有for循环而使用以下代码来赋值
application.Response.Cookies["ASP.NET_SessionId"].domain = ".test.com"
甚至是不用循环而直接在方法中直接使用像string str = application.Response.Cookies["ASP.NET_SessionId"].value这样的代码。不然这样会产生一种这样 的效果,每刷新两次浏览器,session就会发生更新,这样就导致程序出现问题。内部的原因我也不甚了解,但从表面来看,是因为 application.Response.Cookies这个集合只有在网站最开始打开的时候,可以通过循环访问到ASP.NET_SessionId 这个cookie,然后再刷新网页,这个循环就不会访问到ASP.NET_SessionId这个cookie了,但是使用上面的代码,则仍可以直接访问 到ASP.NET_SessionId这个cookie。应该是就因为在这种情况下修改了这个cookie,导致cookie或什么发生了变化等原因,最 终让下一个提交请求被认为是新的请求,而产生了新的session。
第二种情况,根据第一种情况中的描述,现在因为网站的标识id不同(默认网站情况除外),所以每个网站放置session的appdomain的id也不 同,因此这些session被放在了不同的appdomain中,所以像上面那样的代码,虽然可以确保将想共享的sessionid传至服务器,但是由于 session被放在了不同的appdomain中,所以实际上是产生了两个相同sessionid的session被分别放在了属于不同网站的两个 appdomain中, 这样肯定也是不能共享session的了,这个解决起来就麻烦点,需要通过反射来调用一个asp.net没有公开的类 OutOfProcSessionStateStore,看名字也知道它代表的是进程外session存储了,来修改它一个静态成员s_uribase, 此成员代表state外部存储需要访问的appdomain的一个内部id,只要在创建session前,设置一个相同的appdomain的id(当然 看程序这个id其实好像可以随意设了,应该不局限于只使用网站的根域名),这样就能确保取session和放置session都到同一个 appdomain中。大致的代码如下:

public class CookieTest:IHttpModule
{
#region IHttpModule 成员 void IHttpModule.Dispose()
{
//throw new Exception("The method or operation is not implemented.");
} void IHttpModule.Init(HttpApplication context)
{
//throw new Exception("The method or operation is not implemented.");
Type stateServerSessionProvider = typeof(HttpSessionState).Assembly.GetType("System.Web.SessionState.OutOfProcSessionStateStore");
FieldInfo uriField = stateServerSessionProvider.GetField("s_uribase", BindingFlags.Static | BindingFlags.NonPublic); if (uriField == null)
throw new ArgumentException("UriField was not found"); uriField.SetValue(null, ".test.com");
context.EndRequest += new EventHandler(this.EndRequest); } private void EndRequest(object sender, EventArgs args)
{
HttpApplication application = sender as HttpApplication; for (int i = 0; i < application.Response.Cookies.Count; i++)
{ application.Response.Cookies[i].Domain = ".test.com";
}
}
}

最后在web.config中注册这个http模块:
<httpModules>
<add name="test" type="WebApplication5.Test,WebApplication5"/>
</httpModules>
这样就可以在相同顶级域名的网站间共享session状态了.
还可以在Global.asax.cs中加入下面代码
public override void Init()
{
base.Init();
foreach (string moduleName in this.Modules)
{
string appName = "APPNAME";
IHttpModule module = this.Modules[moduleName];
SessionStateModule ssm = module as SessionStateModule;
if (ssm != null)
{
FieldInfo storeInfo = typeof(SessionStateModule).GetField("_store", BindingFlags.Instance | BindingFlags.NonPublic);
SessionStateStoreProviderBase store = (SessionStateStoreProviderBase)storeInfo.GetValue(ssm);
if (store == null)//In IIS7 Integrated mode, module.Init() is called later
{
FieldInfo runtimeInfo = typeof(HttpRuntime).GetField("_theRuntime", BindingFlags.Static | BindingFlags.NonPublic);
HttpRuntime theRuntime = (HttpRuntime)runtimeInfo.GetValue(null);
FieldInfo appNameInfo = typeof(HttpRuntime).GetField("_appDomainAppId", BindingFlags.Instance | BindingFlags.NonPublic);
appNameInfo.SetValue(theRuntime, appName);
}
else
{
Type storeType = store.GetType();
if (storeType.Name.Equals("OutOfProcSessionStateStore"))
{
FieldInfo uribaseInfo = storeType.GetField("s_uribase", BindingFlags.Static | BindingFlags.NonPublic);
uribaseInfo.SetValue(storeType, appName);
}
}
}
}
}
.net,sessionState的Session共享问题解决方案的更多相关文章
- Session共享的解决方案
http://www.cnblogs.com/xinhaijulan/archive/2010/08/21/1805116.html Session共享的解决方案 1.客户端SessionID值唯一: ...
- 集群间Session共享问题解决方案
两个基本概念的生命周期 session: 当新客户端发现一个HTTP请求时服务端会创建一个session.并分配一个sessionID作为服务端来客户端的识别,session对象会保存在服务端.此时s ...
- 负载均衡服务器session共享的解决方案
在ASP.NET的程序中要使用Session对象时,必须确保页面的@page指令中EnableSessionState属性是True或者Readonly,并且在web.config文件中正确的设置了S ...
- 分布式中session共享的解决方案:spring-session
Session是客户端与服务器通讯会话跟踪技术,是服务器与客户端保持整个通讯的会话基本信息.客户端在第一次访问服务器的时候,服务端会响应一个sessionId并且将它存入到本地的Cookie中,在之后 ...
- 负载均衡服务器session共享的解决方案 (转载)
http://luanzhz.blog.163.com/blog/static/58023129201101811445262/ 在ASP.NET的程序中要使用Session对象时,必须确保页面的@p ...
- Java集群之session共享解决方案
随着互联网的日益壮大,网站的pv和uv成线性或者指数倍的增加.单服务器单数据库早已经不能满足实际需求.比如像盛大,淘宝这样的大型网络公司,更是如此. 集群,也就是让一组计算机服务器协同工作,达 ...
- asp.net 分布式探讨之Session共享问题
---恢复内容开始--- Session共享是分布式架构设计中的一大难点,尽管session共享的解决方案不少,但是.net 下的解决方案还是比较少,而且说明文档也很少. 之前尝试用memcached ...
- 【Tomcat】Tomcat + Memcached 实现session共享
概述 web项目中,Tomcat的访问量总是有限的,这时候就需要用到Tomcat集群,多个Tomcat的时候就要考虑Session共享的问题,这里介绍一种使用Memcached做Session共享的解 ...
- 关于多台机器之前session共享,sessionState mode="StateServer" 问题的困扰
.net 多台机器共享session是很老的技术,一直很少用到session. 最近就出现了一个问题:三台前端,其中一台保存的session值死活不对,一样的环境,一样的配置文件,就是和另外两台获得的 ...
随机推荐
- 桶排序(BucketSort)
1 桶排序核心思想是 根据数据规模n划分 m个相同大小的区间 (每个区间为一个桶,桶可理解为容器) 2 每个桶存储区间内的元素(区间为半开区间 例如[0,10) 或者 [200,300) ) 3 将n ...
- PHP学习笔记(1) - 开发环境搭建
运行环境:phpstudy 它基本包括运行php应用需要的一切,php. apache.mysql,一键傻瓜安装 装好之后只需要配置虚拟主机和修改host文件就可以支持多站点 下载: http://w ...
- linux磁盘设备知识
linux分区数字编号: 1.分区数字编号1至4留给主分区或扩展分区使用,逻辑分区编号从5开始. 2.IDE硬盘设备名均以/dev/hd开头,不同硬盘编号依次是/dev/hda/./dev/hdb./ ...
- 使用内核定时器的second字符设备驱动及测试代码
驱动: #include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #inc ...
- C语言中宏定义(#define)时do{}while(0)的价值
最近在新公司的代码中发现到处用到do{...}while(0),google了一下,发现Stack Overflow上早有很多讨论,总结了一下讨论,加上自己的理解,do{...}while(0)的价值 ...
- CODEVS 1004四子连棋
[题目描述 Description] 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色棋子,有两个空白地带,任何一颗黑白棋子都可以向上下左右四个方向移动到相邻的空格,这叫行棋一步,黑 ...
- javascript 闭包暴露句柄和命名冲突的解决方案
暴露 最近在琢磨前端Js开源项目的东西,然后就一直好奇他们是怎么句柄暴露出来的,特整理一下两种方法. 将对象悬挂到window下面. 不使用var进行变量声明.下面上代码: (function(win ...
- scrollview始终显示滚动条 Android
设置scrollview的:android:fadeScrollbars="false"表示始终显示垂直滚动条
- sjtu1585 oil
Description Crystal家的公司最近承包了一个大油田.整块油田为一个矩形区域,被划分为\(n \times m\)个小块. Crystal亲自调查了每个小块的石油储备量.这些数据表示为\ ...
- iOS 并发:NSOperation 与调度队列入门(1)
一直以来,并发都被视为 iOS 开发中的「洪水猛兽」.许多开发者都将其视为危险地带,唯恐避之而不及.更有谣传认为,多线程代码应该尽力避免.笔者同意,如果你对并发的了解不够深入,就容易造成危险.但是,危 ...