最近项目因为要负载均衡所以就使用了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共享问题解决方案的更多相关文章

  1. Session共享的解决方案

    http://www.cnblogs.com/xinhaijulan/archive/2010/08/21/1805116.html Session共享的解决方案 1.客户端SessionID值唯一: ...

  2. 集群间Session共享问题解决方案

    两个基本概念的生命周期 session: 当新客户端发现一个HTTP请求时服务端会创建一个session.并分配一个sessionID作为服务端来客户端的识别,session对象会保存在服务端.此时s ...

  3. 负载均衡服务器session共享的解决方案

    在ASP.NET的程序中要使用Session对象时,必须确保页面的@page指令中EnableSessionState属性是True或者Readonly,并且在web.config文件中正确的设置了S ...

  4. 分布式中session共享的解决方案:spring-session

    Session是客户端与服务器通讯会话跟踪技术,是服务器与客户端保持整个通讯的会话基本信息.客户端在第一次访问服务器的时候,服务端会响应一个sessionId并且将它存入到本地的Cookie中,在之后 ...

  5. 负载均衡服务器session共享的解决方案 (转载)

    http://luanzhz.blog.163.com/blog/static/58023129201101811445262/ 在ASP.NET的程序中要使用Session对象时,必须确保页面的@p ...

  6. Java集群之session共享解决方案

    随着互联网的日益壮大,网站的pv和uv成线性或者指数倍的增加.单服务器单数据库早已经不能满足实际需求.比如像盛大,淘宝这样的大型网络公司,更是如此.     集群,也就是让一组计算机服务器协同工作,达 ...

  7. asp.net 分布式探讨之Session共享问题

    ---恢复内容开始--- Session共享是分布式架构设计中的一大难点,尽管session共享的解决方案不少,但是.net 下的解决方案还是比较少,而且说明文档也很少. 之前尝试用memcached ...

  8. 【Tomcat】Tomcat + Memcached 实现session共享

    概述 web项目中,Tomcat的访问量总是有限的,这时候就需要用到Tomcat集群,多个Tomcat的时候就要考虑Session共享的问题,这里介绍一种使用Memcached做Session共享的解 ...

  9. 关于多台机器之前session共享,sessionState mode="StateServer" 问题的困扰

    .net 多台机器共享session是很老的技术,一直很少用到session. 最近就出现了一个问题:三台前端,其中一台保存的session值死活不对,一样的环境,一样的配置文件,就是和另外两台获得的 ...

随机推荐

  1. 常用 VS 快捷键积累

    1. 代码块大纲显示与隐藏 Ctrl+M,L                   折叠或展开所有代码块 Ctrl+M,M                   折叠或展开当前所在的代码块 Ctrl+M, ...

  2. PHPEXCEL使用实例

    最近在项目中要用到PHP生成EXCEL,上网找了一下,发现PHPEXCEL挺不错,用了一下,感觉还行,就是设置单元格格式的时候比较麻烦,总体来说功能还是比较强大的,还有生成PDF什么的,发一个实例吧 ...

  3. 超过130个你需要了解的vim命令

    基础 :e filename Open filename for edition :w Save file :q Exit Vim :q! Quit without saving :x Write f ...

  4. mysql密码忘记后重置密码

    之前在centOS里安装了xampp,设置了mysql数据库root密码,今天需要增加个数据库,发现忘记之前设置的密码是什么了.经过一番摸爬滚打,终于搞明白了,注意以下的操作都是以linux的root ...

  5. entity framework mysql 那些写法你碰不得

    记 几次 ef 数据查询踩到的坑......未完待续

  6. iis7以上版本权限控制

    IIS7.5中(仅win7,win2008 SP2,win2008 R2支持),应用程序池的运行帐号,除了指定为LocalService,LocalSystem,NetWorkService这三种基本 ...

  7. oracle LogMiner配置使用

    一.安装LogMiner1.@D:\app\product\11.1.0\db_1\RDBMS\ADMIN\dbmslm.sql 2.@D:\app\product\11.1.0\db_1\RDBMS ...

  8. gwt 创建 超链接cell (HyperTextCell)

    package com.cnblogs.hooligen.client; import com.google.gwt.cell.client.AbstractCell; import com.goog ...

  9. eclipse中使用jython

    通过maven配置加载这个包,目前比较稳定的是python2.7的,见 <dependency> <groupId>org.python</groupId> < ...

  10. 7.MVC框架开发(创建层级项目)

    在一个项目比较大的时候,就会有多个层级项目 1)在项目中选定项目右建新建区域(新的层级项目),项目->右键->添加->区域,构成了一套独立的MVC的目录,这个目录包括Views,Co ...