说说Asp.net的StateServer和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中需要添加以下两个节点:
<sessionState cookieless="UseCookies" mode="StateServer" stateConnectionString="tcpip=192.168.5.2:42424" timeout="20"/>
<machineKey validationKey="78AE3850338BFADCE59D8DDF58C9E4518E7510149C46142D7AAD7F1AD49D95D4" decryptionKey="5FC88DFC24EA123C" validation="SHA1"/>
这个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";
}
}
}
这里关于OutOfProcSessionStateStore和s_uribase使用reflector自己研究一下吧。我大概看了看,感觉程序就是我上面做的那样的一个解释。后在web.config中注册这个http模块:
<httpModules>
<add name="test" type="WebApplication5.Test,WebApplication5"/>
</httpModules>
来源
https://blog.csdn.net/ahywg/article/details/39232809?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164991906016782092928537%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=164991906016782092928537&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~baidu_landing_v2~default-3-39232809.142^v8^pc_search_result_control_group,157^v4^new_style&utm_term=StateServer&spm=1018.2226.3001.4187
相关文章