Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 526|回复: 0

.net,sessionState的Session共享问题解决方案

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-6-11 09:55:28 | 显示全部楼层 |阅读模式

    最近项目因为要负载均衡所以就使用了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.Name == "ASP.NET_SessionId")
    application.Response.Cookies.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.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);
                            }
                        }
                    }
                }
            }

     

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-12-23 07:43 , Processed in 0.053873 second(s), 27 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表