上一篇文件介绍了使用nginx+iis 模拟负载均衡的问题。详细搭建过程请查看( http://blog.csdn.net/u010533180/article/details/52784696)
模拟负载均衡站点已经搭建完毕,那么问题来了,两个站点也许在不同的机器下使用不同ip和端口号,即使部署在同一机器下面,端口号也肯定不同。现在有很多的信息保存在session中,那么session信息的一致性该如何解决呢?
Session 保存在服务器中,唯一的回话标识,有一个对应的SessionId。用户关闭浏览器,即回话结束,当用户在打开浏览器浏览时,就是一个新的回话了。而且Session的唯一标识是基于cookie存储的。如果用户禁用了cookie,Session也是无法使用的。如何解决,请自行查阅相关资料,因为这是不是本文的重点。关于Session更详细的信息,也可以查阅微软官方的站点(https://msdn.microsoft.com/zh-cn/library/ms178581(v=vs.100).aspx)
这里Session的测试也是基于上次的搭建进行继续下去的。还是那个熟悉的default.aspx,里面的测试代码如下:
protected void Page_Load(object sender, EventArgs e)
{
int port = Request.Url.Port;
if (port == 8081)
{
Response.Write("第一个页面<br/>");
}
else if (port == 8082)
{
Response.Write("第二个页面<br/>");
}
else
{
Response.Write(port.ToString() + "<br/>");
}
Response.Write("请求开始时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "<br/>");
Response.Write("服务器名称:" + Server.MachineName + "<br/>"); //服务器名称
Response.Write("服务器IP地址:" + Request.ServerVariables["LOCAL_ADDR"] + "<br/>"); //服务器IP地址
Response.Write("HTTP访问端口:" + Request.ServerVariables["SERVER_PORT"]);//HTTP访问端口"
Response.Write(".NET解释引擎版本:" + ".NET CLR" + Environment.Version.Major + "." + Environment.Version.Minor + "quot;." + Environment.Version.Build + "." + Environment.Version.Revision + "<br/>"); //.NET解释引擎版本
Response.Write("服务器操作系统版本:" + Environment.OSVersion.ToString() + "<br/>");//服务器操作系统版本
Response.Write("服务器IIS版本:" + Request.ServerVariables["SERVER_SOFTWARE"] + "<br/>");//服务器IIS版本
Response.Write("服务器域名:" + Request.ServerVariables["SERVER_NAME"] + "<br/>");//服务器域名
Response.Write("虚拟目录的绝对路径:" + Request.ServerVariables["APPL_RHYSICAL_PATH"] + "<br/>");//虚拟目录的绝对路径
Response.Write("执行文件的绝对路径:" + Request.ServerVariables["PATH_TRANSLATED"] + "<br/>");//执行文件的绝对路径
Response.Write("虚拟目录Session总数:" + Session.Contents.Count.ToString() + "<br/>"); //虚拟目录Session总数
Response.Write("虚拟目录Application总数:" + Application.Contents.Count.ToString() + "<br/>");//虚拟目录Application总数
Response.Write("域名主机:" + Request.ServerVariables["HTTP_HOST"] + "<br/>");//域名主机
Response.Write("服务器区域语言:" + Request.ServerVariables["HTTP_ACCEPT_LANGUAGE"] + "<br/>");//服务器区域语言
Response.Write("用户信息:" + Request.ServerVariables["HTTP_USER_AGENT"] + "<br/>");
Response.Write("CPU个数:" + Environment.GetEnvironmentVariable("NUMBER_OF_PROCESSORS") + "<br/>");//CPU个数
Response.Write("CPU类型:" + Environment.GetEnvironmentVariable("PROCESSOR_IDENTIFIER") + "<br/>");//CPU类型
Response.Write("请求来源地址:" + Request.Headers["X-Real-IP"] + "<br/>");
Session["nginx"] = port;
if (Session["port"] == null)
{
Response.Write("Session为null<br/>");
LoginSession loginSession = new LoginSession();
loginSession.LoginPort = port;
loginSession.LoginTime = DateTime.Now;
Session["port"] = loginSession;
}
else
{
LoginSession loginSession = Session["port"] as LoginSession;
if (loginSession != null)
{
Response.Write(loginSession.LoginPort + ";" + loginSession.LoginTime.ToString("yyyy-MM-dd HH:mm:ss") + "</br>");
}
else
{
Response.Write("不知道何种原因从Session里面取到的LoginSession 为null");
}
}
}
添加一个LoginSession 类,记得要标记[Serializable],因为Session里面的对象需要支持序列化才能成功。 进行测试
using System;
using System.Collections.Generic;
using System.Web;
namespace NginxWebProject
{
[Serializable]
public class LoginSession
{
private int loginPort;
public int LoginPort
{
get
{
return loginPort;
}
set
{
loginPort = value;
}
}
private DateTime loginTime;
public DateTime LoginTime
{
get
{
return loginTime;
}
set
{
loginTime = value;
}
}
}
}
为了测试我这里把session统一放到了一个新的机器中(,该节点里面的信息读者还是自己查阅,信息量确实很大)。则最终的对应的web.config文件修改如下:
<?xml version="1.0"?>
<!--
有关如何配置 ASP.NET 应用程序的详细信息,请访问
http://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<system.web>
<compilation debug="true"/>
<httpRuntime/>
**<sessionState mode="StateServer" stateConnectionString="tcpip=192.168.164.129:42424" timeout="60" stateNetworkTimeout="20"/>**
</system.web>
</configuration>
上面的配置中我把Session的信息存储在了IP地址为 192.168.164.129 这台机器中,端口号为默认的42424。则要保证这台机器服务列表里面的Asp.net 状态服务启动。
注册表里面的 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\aspnet_state 下面的 AllowRemoteConnection 的值为1。 否则部署的站点也无法访问。
如果这样部署到服务器上面,会发现Session总是会被覆盖掉,达不到统一致性。
解决方法如下:在站点里面的Global.asax文件中添加如下代码即可。如果站点里面没有这个文件,则项目右键-》添加新建项-》在弹窗中找到Visual C# -》web》全局应用程序类
public override void Init()
{
base.Init();
foreach (string moduleName in this.Modules)
{
//string appName = "APPNAME";
string appName = moduleName;
IHttpModule module = this.Modules[moduleName];
SessionStateModule ssm = module as SessionStateModule;
if (ssm != null)
{
FieldInfo storeInfo = typeof(SessionStateModule).GetField("_store", BindingFlags.Instance | BindingFlags.NonPublic);
FieldInfo configMode = typeof(SessionStateModule).GetField("s_configMode", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);
SessionStateMode mode = (SessionStateMode)configMode.GetValue(ssm);
if (mode == SessionStateMode.StateServer)
{
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);
object obj = null;
uribaseInfo.GetValue(obj);
}
}
}
break;
}
}
}
浏览站点访问,我的测试效果图如下: