【问题标题】:Why can't I share Session state between 2 web apps with StateServer? What am I missing?为什么我不能使用 StateServer 在 2 个 Web 应用程序之间共享会话状态?我错过了什么?
【发布时间】:2025-12-15 07:15:02
【问题描述】:

我无法让 2 个相同的 ASP.NET MVC 应用程序使用 Session StateServer 共享同一个 Session。我尝试这样做的原因是我们最终将在需要共享相同状态的 3 个 Web 服务器上部署这个应用程序。我们需要使用 StateServer,因为我们试图尽量减少对非数据相关存储的 db 的使用。

设置:

我已将相同的代码库部署到 http://localhost/App1http://localhost/App2

两者都有相同的 Web.Config 文件,内容如下:

<system.web>
<sessionState mode="StateServer" 
              cookieless="false" 
              timeout="20" 
              stateConnectionString="tcpip=127.0.0.1:42424" />
              //stateConnectionString="tcpip=192.168.1.52:42424" /> // also doesn't work
<machineKey 
  validationKey="8B9F68D0CC730F6F046D0173021C34B1A0D9A01C21D8E4D4A7A1DFF38332DEE8CBBAFEA503C18776614EE9D4F7EEA7E5D2D5571630547D822485A27B1EF53AC1"
  decryptionKey="60009563EFCFC594FD1BC46684943AA398EE70412A624B2EB488BBB071F15ECF"
  validation="SHA1" decryption="AES" />

我用this tool to generate these machine keys

测试:

我将以下内容放入我的一个控制器中以测试它是否正常工作:

ViewData["mode"] = requestContext.HttpContext.Session.Mode.ToString();

string timestamp = DateTime.Now.ToString();
if (requestContext.HttpContext.Session["timestamp"] == null)
{
    requestContext.HttpContext.Session["timestamp"] = timestamp;
}

ViewData["timestamp"] = requestContext.HttpContext.Session["timestamp"].ToString();
ViewData["realtime"] = timestamp;

在视图中显示:

<p>
    Mode: <%= ViewData["mode"].ToString() %>
</p>
<p>
    Time: <%= ViewData["timestamp"].ToString() %>
</p>
<p>
    real time: <%= ViewData["realtime"].ToString() %>
</p>

结果:

对于这两种部署,当页面首次加载时,我可以看到模式为 StateServer,并且时间戳设置为与实时值相同的时间。但是,如果这有效,则只有第一页应该有与实时值相同的时间。第二个页面加载应该从 StateServer 读取,因为该时间戳值不再为空,并显示该时间值。但相反,它再次显示实时值。

当我刷新页面时,时间戳保持不变,实时值始终在更新。这表明时间戳正在保存到 Session,但两个部署的时间戳值在应该相同时总是不同,因此这表明 Session 没有被共享。

有人可以指出我做错了什么,或者我需要做些什么才能让它工作吗?谢谢

【问题讨论】:

  • 你能完成这个吗?
  • @DaveDev 我知道我迟到了:P,但这可以帮助其他人。如果您可以找到共享相同域的会话 cookie(请参阅msdn.microsoft.com/en-IN/library/ms228262(v=vs.85).aspx)并将会话存储在公共服务(DB/Redis)中,那么您绝对可以在多个服务器之间共享会话

标签: c# asp.net asp.net-mvc session stateserver


【解决方案1】:

默认情况下会话不能在不同的应用程序之间共享。据我所知,您有两个不同的应用程序 App1App2,它们运行在不同的虚拟目录甚至可能是不同的应用程序池中,所以不要期望它们之间共享会话。

与往常一样,workarounds 可能对您有用。正如您所看到的,它使用 hack(反射)来规避 ASP.NET 团队设计人员不公开某些类和属性的决心,并使我们作为开发人员的生活变得困难。

【讨论】:

  • 你说得对,它们是不同的应用程序。一个存在于D:\App1,另一个存在于D:\App2,分别位于IIS中DefaultWebSite下的不同虚拟目录中。但是,两者都在 DefaultAppPool 中。这样做的原因是我们最终将在需要共享相同状态的 3 个 Web 服务器上部署这个应用程序。
  • 我认为是虚拟目录决定了应用程序名称而不是应用程序池,所以我猜你将不得不求助于强制相同应用程序名称的黑客攻击。
  • 好的,我会考虑到这一点。如果我们最终走 hacks 的路线来实现这个结果,我认为我们可能会更好地坚持使用数据库来存储会话。无论如何,db 方法似乎更健壮。
  • 很抱歉让您失望,但即使您采用数据库解决方案路线,您也将不得不求助于黑客:blogs.msdn.com/b/toddca/archive/2007/01/25/… 在 ASP 中的应用程序之间共享会话没有开箱即用的方法。网。当您在服务器场中运行相同的应用程序时,进程外会话非常有用。对于不同的应用程序,您可能会看到一种更标准的数据共享方式(GET、POST 请求)。
  • 我只是在想.. 你说它是确定应用程序名称的虚拟目录。此应用程序将部署在 3 个 Web 服务器上,每个服务器将在各自的 IIS 中具有相同的网站名称,负载平衡器在它们之间分配负载。这是否符合具有相同应用程序名称的条件?你认为它会在这样的环境中工作吗?
【解决方案2】:

其实你可以使用Sql server模式共享会话。

试试这个:-

我只是改变了程序,即

USE ASPState
GO

ALTER PROCEDURE dbo.TempGetAppID
    @appName tAppName,
    @appId int OUTPUT
AS

    -- start change

    -- Use the application name specified in the connection for the appname if specified
    -- This allows us to share session between sites just by making sure they have the
    -- the same application name in the connection string.
    DECLARE @connStrAppName nvarchar(50)
    SET @connStrAppName = APP_NAME()

    -- .NET SQLClient Data Provider is the default application name for .NET apps
    IF (@connStrAppName <> '.NET SQLClient Data Provider')
        SET @appName = @connStrAppName

    -- end change

SET @appName = LOWER(@appName)
SET @appId = NULL

SELECT @appId = AppId
FROM [ASPState].dbo.ASPStateTempApplications
WHERE AppName = @appName

IF @appId IS NULL BEGIN
BEGIN TRAN 

SELECT @appId = AppId
FROM [ASPState].dbo.ASPStateTempApplications WITH (TABLOCKX)
WHERE AppName = @appName

IF @appId IS NULL
BEGIN
EXEC GetHashCode @appName, @appId OUTPUT

INSERT [ASPState].dbo.ASPStateTempApplications
VALUES
(@appId, @appName)

IF @@ERROR = 2627 
BEGIN
DECLARE @dupApp tAppName

SELECT @dupApp = RTRIM(AppName)
FROM [ASPState].dbo.ASPStateTempApplications 
WHERE AppId = @appId

RAISERROR('SQL session state fatal error: hash-code collision between applications ''%s'' and ''%s''. Please rename the 1st application to resolve the problem.', 
18, 1, @appName, @dupApp)
END
END

COMMIT
END

RETURN 0 
GO

然后将web.config修改为:-

<sessionState mode="SQLServer" sqlConnectionString="Data Source=.;Integrated Security=True;Application Name=TEST" cookieless="false" timeout="20"></sessionState>
   <httpRuntime targetFramework="4.5"/>

您必须添加应用程序名称,并且对于您想要共享同一会话的所有应用程序,该名称必须相同。

谢谢。

【讨论】:

【解决方案3】:

更新:这是我在同一主题上回答的上一篇文章 Sharing sessions across applications using the ASP.NET Session State Service

正如已经指出的,会话数据的范围是应用程序。那就是您在 IIS 中创建的应用程序。因此,由于应用程序的作用域,具有相同会话 ID 的两个应用程序不会共享同一个会话。

作为一个替代想法,对您来说可能可行,也可能不可行。 您可以创建一个根应用程序并将 D:\App1 和 D:\App2 的代码放在两个子文件夹中。

d:\Root
  web.config
  \App1
     default.aspx
     ...
  \App2
     default.aspx
     ...

然后在 IIS 中创建一个指向 d:\Root 的应用程序。

你也可以在IIS中创建一个Application,然后在Application下创建两个虚拟目录,一个指向D:\App1,另一个指向D:\App2,那么它们也可以共享一个web.config在应用层。至关重要的是,这两个虚拟目录只是虚拟的,而不是作为应用程序创建的。

所以你的硬盘布局可能看起来像这样

D:\Root
  web.config

D:\App1
  default.aspx
  ...

D:\App2
  default.aspx
  ...

创建指向 D:\Root 的根应用程序,然后在应用程序下创建两个虚拟目录 App1 指向 D:\App1 和 App2 指向 D:\App2。

在这两种情况下的效果是您实际上将一个应用程序拆分为两个部分,都在同一个 Session 范围内,因此两者的代码可以共享相同的会话数据。

【讨论】:

    【解决方案4】:

    您还必须确保应用程序的应用程序路径在两个 Web 服务器上必须相同。这里有一篇旧文章可能会有所帮助

    http://support.microsoft.com/default.aspx?scid=kb;EN-US;q325056

    我们目前遇到了类似的问题,除了我们使用的是 IIS 7.5 并且应用程序路径对我们隐藏(不再使用元数据库)。有人知道使用 IIS 7.5 解决此问题的方法吗?

    【讨论】:

    • 在我看来,我认为微软在过去的几个版本中放弃了 IIS。
    最近更新 更多