1. 程式人生 > >Nginx+iis 中搭建的站點解決session 一致性的解決方案

Nginx+iis 中搭建的站點解決session 一致性的解決方案

   模擬負載均衡站點已經搭建完畢,那麼問題來了,兩個站點也許在不同的機器下使用不同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;
                }
            }
        }

瀏覽站點訪問,我的測試效果圖如下:

這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述