负载均衡时,在State_Server模式中的Session共享问题(不讨论负载均衡的问题)

前言:

配置Session的mode为State_Server模式,不说明,请按照如下配置

<sessionState mode="StateServer" stateConnectionString="tcpip=192.168.224.1:42424"cookieless="true" cookieName="APSNET_SessionId" timeout="6000" ></sessionState>

红色部分是关键部分,上面的ip是我的虚拟机的ip地址。

通过services.msc找到ASP.Net状态服务,并且开启,然后到注册表中打开AllowRemoteConnection值,设置成1.注册表地址:自己去网上搜吧,太长了不写了。


以上内容是网上大部分的内容,然后你都做好了,也未必就能Session共享。不信你去试试。(不要给我拿两个浏览器去查询,然后问我为啥不共享,我要咬人的)


补充:关于负载均衡的配置我是使用nginx做的测试,刚刚使用这个工具,还不是很熟练,你可以在网上找到。至于webFrame嘛,装起来真TMD麻烦。


正文

废话说了一大堆,上正文吧。

以上的配置都做好了之后需要检查你的iis配置,网站的应用程序池和网站的id是否相同,一定要保证相同。

那么还有一个很重要的就是要修改你的global.asax文件,添加如下代码

    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)
            {
                System.Reflection.FieldInfo storeInfo = typeof(SessionStateModule).GetField("_store", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
                SessionStateStoreProviderBase store = (SessionStateStoreProviderBase)storeInfo.GetValue(ssm);
                if (store == null)//In IIS7 Integrated mode, module.Init() is called later
                {
                    System.Reflection.FieldInfo runtimeInfo = typeof(HttpRuntime).GetField("_theRuntime", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
                    HttpRuntime theRuntime = (HttpRuntime)runtimeInfo.GetValue(null);
                    System.Reflection.FieldInfo appNameInfo = typeof(HttpRuntime).GetField("_appDomainAppId", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
                    appNameInfo.SetValue(theRuntime, appName);
                }
                else
                {
                    Type storeType = store.GetType();
                    if (storeType.Name.Equals("OutOfProcSessionStateStore"))
                    {
                        System.Reflection.FieldInfo uribaseInfo = storeType.GetField("s_uribase", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
                        uribaseInfo.SetValue(storeType, appName);
                    }
                }
            }
        }
    }



一切就绪,然后就嗨起来吧,真的可以了。我再这里不上截图了,附件将源码带上(其中包括网站项目、mvc项目和网站应用,都需要配置global文件)。


如果一切到这里就结束了,好像也能接受,但是我告诉你,我们还没完呢

通过上面的代码我们可以看到主要思路就是 HttpRuntime的_appDomainAppId属性赋值或者是给OutOfProcSessionStateStore的s_uribase赋值。

我们通过查看.net源码发现这两个属性都是私有属性,在我的源码附件中的mvc项目中可以看到,在不同的机器和iis版本下,输出的内容不一致,这个内容就是存储session库的一个关键值。

以下是我查看到的部分源码,抛出来给大家打打牙祭。

void OneTimeInit() {
            SessionStateSection config = RuntimeConfig.GetAppConfig().SessionState;
 
            s_configPartitionResolverType = config.PartitionResolverType;
            s_configStateConnectionString = config.StateConnectionString;
            s_configStateConnectionStringFileName = config.ElementInformation.Properties["stateConnectionString"].Source;
            s_configStateConnectionStringLineNumber = config.ElementInformation.Properties["stateConnectionString"].LineNumber;
            s_configCompressionEnabled = config.CompressionEnabled;
 
            if (_partitionResolver == null) {
                String stateConnectionString = config.StateConnectionString;
 
                SessionStateModule.ReadConnectionString(config, ref stateConnectionString, "stateConnectionString");
 
                s_singlePartitionInfo = (StateServerPartitionInfo)CreatePartitionInfo(stateConnectionString);
            }
            else {
                s_usePartition = true;
                s_partitionManager = new PartitionManager(new CreatePartitionInfo(CreatePartitionInfo));
            }
 
            s_networkTimeout = (int)config.StateNetworkTimeout.TotalSeconds;
 
            string appId = HttpRuntime.AppDomainAppId;
            string idHash = Convert.ToBase64String(CryptoUtil.ComputeSHA256Hash(Encoding.UTF8.GetBytes(appId)));
 
            // Make sure that we have a absolute URI, some hosts(Cassini) don't provide this.
            if (appId.StartsWith("/", StringComparison.Ordinal)) {
                s_uribase = appId + "(" + idHash + ")/";
            }
            else {
                s_uribase = "/" + appId + "(" + idHash + ")/";
            }
 
            // We only need to do this in one instance
            s_onAppDomainUnload = new EventHandler(OnAppDomainUnload);
            Thread.GetDomain().DomainUnload += s_onAppDomainUnload;
 
            s_oneTimeInited = true;
        }

 通过上面红色的代码,我们可以看到,不管是在global中赋值给哪个变量,最后都汇集到s_uribase这个变量中。那我们继续找,看一下是在哪里使用到了这个变量吧

void MakeRequest(
                UnsafeNativeMethods.StateProtocolVerb   verb,
                String                                  id,
                UnsafeNativeMethods.StateProtocolExclusive    exclusiveAccess,
                int                                     extraFlags,
                int                                     timeout,
                int                                     lockCookie,
                byte[]                                  buf,
                int                                     cb,
                int                                     networkTimeout,
                out UnsafeNativeMethods.SessionNDMakeRequestResults results) {//笔者注:这是一个很重要的out参数
 
            int                         hr;
            string                      uri;
            OutOfProcConnection         conn = null;
            HandleRef                   socketHandle;
            bool                        checkVersion = false;
 
            Debug.Assert(timeout <= SessionStateModule.MAX_CACHE_BASED_TIMEOUT_MINUTES, "item.Timeout <= SessionStateModule.MAX_CACHE_BASED_TIMEOUT_MINUTES");
 
            SessionIDManager.CheckIdLength(id, true /* throwOnFail */);
 
            if (_partitionInfo == null) {
                Debug.Assert(s_partitionManager != null);
                Debug.Assert(_partitionResolver != null);
 
                _partitionInfo = (StateServerPartitionInfo)s_partitionManager.GetPartition(_partitionResolver, id);
 
                // If its still null, we give up
                if (_partitionInfo == null) {
                    throw new HttpException(SR.GetString(SR.Bad_partition_resolver_connection_string, "PartitionManager"));
                }
            }
 
            // Need to make sure we dispose the connection if anything goes wrong
            try {
                conn = (OutOfProcConnection)_partitionInfo.RetrieveResource();
                if (conn != null) {
                    socketHandle = new HandleRef(this, conn._socketHandle.Handle);
                }
                else {
                    socketHandle = new HandleRef(this, INVALID_SOCKET);
                }
 
                if (_partitionInfo.StateServerVersion == -1) {
                    // We don't need locking here because it's okay to have two
                    // requests initializing s_stateServerVersion.
                    checkVersion = true;
                }
 
                Debug.Trace("OutOfProcSessionStateStoreMakeRequest",
                            "Calling MakeRequest, " +
                            "socket=" + (IntPtr)socketHandle.Handle +
                            "verb=" + verb +
                            " id=" + id +
                            " exclusiveAccess=" + exclusiveAccess +
                            " timeout=" + timeout +
                            " buf=" + ((buf != null) ? "non-null" : "null") +
                            " cb=" + cb +
                            " checkVersion=" + checkVersion +
                            " extraFlags=" + extraFlags);
 
                // Have to UrlEncode id because it may contain non-URL-safe characters
                uri = HttpUtility.UrlEncode(s_uribase + id);//笔者注:在这里使用到了s_uribase
 		//笔者注:在这里使用uri和主方法的out参数作为参数调用了另外一个方法,并且返回了一个int类型,到此可以看到 hr 对我们的价值已经没有了,我们进入到这个方法看看吧。	
                hr = UnsafeNativeMethods.SessionNDMakeRequest(
                        socketHandle, _partitionInfo.Server, _partitionInfo.Port, _partitionInfo.ServerIsIPv6NumericAddress /* forceIPv6 */, networkTimeout, verb, uri,
                        exclusiveAccess, extraFlags, timeout, lockCookie,
                        buf, cb, checkVersion, out results);
 
                Debug.Trace("OutOfProcSessionStateStoreMakeRequest", "MakeRequest returned: " +
                            "hr=" + hr +
                            " socket=" + (IntPtr)results.socket +
                            " httpstatus=" + results.httpStatus +
                            " timeout=" + results.timeout +
                            " contentlength=" + results.contentLength +
                            " uri=" + (IntPtr)results.content +
                            " lockCookie=" + results.lockCookie +
                            " lockDate=" + string.Format("{0:x}", results.lockDate) +
                            " lockAge=" + results.lockAge +
                            " stateServerMajVer=" + results.stateServerMajVer +
                            " actionFlags=" + results.actionFlags);
 
                if (conn != null) {
                    if (results.socket == INVALID_SOCKET) {
                        conn.Detach();
                        conn = null;
                    }
                    else if (results.socket != socketHandle.Handle) {
                        // The original socket is no good.  We've got a new one.
                        // Pleae note that EnsureConnected has closed the bad
                        // one already.
                        conn._socketHandle = new HandleRef(this, results.socket);
                    }
                }
                else if (results.socket != INVALID_SOCKET) {
                    conn = new OutOfProcConnection(results.socket);
                }
 
                if (conn != null) {
                    _partitionInfo.StoreResource(conn);
                }
            }
            catch {
                // We just need to dispose the connection if anything bad happened
                if (conn != null) {
                    conn.Dispose();
                }
 
                throw;
            }
 
            if (hr != 0) {
                HttpException e = CreateConnectionException(_partitionInfo.Server, _partitionInfo.Port, hr);
 
                string phase = null;
 
                switch (results.lastPhase) {
                case (int)UnsafeNativeMethods.SessionNDMakeRequestPhase.Initialization:
                    phase = SR.GetString(SR.State_Server_detailed_error_phase0);
                    break;
 
                case (int)UnsafeNativeMethods.SessionNDMakeRequestPhase.Connecting:
                    phase = SR.GetString(SR.State_Server_detailed_error_phase1);
                    break;
 
                case (int)UnsafeNativeMethods.SessionNDMakeRequestPhase.SendingRequest:
                    phase = SR.GetString(SR.State_Server_detailed_error_phase2);
                    break;
 
                case (int)UnsafeNativeMethods.SessionNDMakeRequestPhase.ReadingResponse:
                    phase = SR.GetString(SR.State_Server_detailed_error_phase3);
                    break;
 
                default:
                    Debug.Assert(false, "Unknown results.lastPhase: " + results.lastPhase);
                    break;
                }
 
                WebBaseEvent.RaiseSystemEvent(SR.GetString(SR.State_Server_detailed_error,
                            phase,
                            "0x" + hr.ToString("X08", CultureInfo.InvariantCulture),
                            cb.ToString(CultureInfo.InvariantCulture)),
                            this, WebEventCodes.WebErrorOtherError, WebEventCodes.StateServerConnectionError, e);
 
                throw e;
            }
 
            if (results.httpStatus == 400) {
                if (s_usePartition) {
                    throw new HttpException(
                        SR.GetString(SR.Bad_state_server_request_partition_resolver,
                                    s_configPartitionResolverType, _partitionInfo.Server, _partitionInfo.Port.ToString(CultureInfo.InvariantCulture)));
                }
                else {
                    throw new HttpException(
                        SR.GetString(SR.Bad_state_server_request));
                }
            }
 
            if (checkVersion) {
                _partitionInfo.StateServerVersion = results.stateServerMajVer;
                if (_partitionInfo.StateServerVersion < WHIDBEY_MAJOR_VERSION) {
                    // We won't work with versions lower than Whidbey
                    if (s_usePartition) {
                        throw new HttpException(
                            SR.GetString(SR.Need_v2_State_Server_partition_resolver,
                                        s_configPartitionResolverType, _partitionInfo.Server, _partitionInfo.Port.ToString(CultureInfo.InvariantCulture)));
                    }
                    else {
                        throw new HttpException(
                            SR.GetString(SR.Need_v2_State_Server));
                    }
                }
            }
        }

     下面是SessionNDMakeRequest方法的源代码,很明显在上面使用了这个方法,

    [DllImport(ModName.ENGINE_FULL_NAME, CharSet=CharSet.Ansi, BestFitMapping=false, ThrowOnUnmappableChar=true)]
        internal static extern int SessionNDMakeRequest(
                HandleRef               socket,
                string                  server,
                int                     port,
                bool                    forceIPv6,
                int                     networkTimeout,
                StateProtocolVerb       verb,
                string                  uri,
                StateProtocolExclusive  exclusive,
                int                     extraFlags,
                int                     timeout,
                int                     lockCookie,
                byte[]                  body,
                int                     cb,
                bool                    checkVersion,
                out SessionNDMakeRequestResults results);


真的要到此为止,往下也找不到了,也看不懂了,等到高手来指导,说的就是你,不要走,告诉我这是为什么  ^_^!!!


源码在这里点击下崽哟

原文地址:https://www.cnblogs.com/xiaoch/p/13417970.html