为什么我不能使用 StateServer 在 2 个 Web 应用程序之间共享会话状态?我缺少什么?

发布于 2024-09-10 12:09:32 字数 2182 浏览 10 评论 0原文

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

设置:

我已将相同的代码库部署到 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" />

我使用此工具生成这些机器密钥

测试:< /strong>

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

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 读取,因为该时间戳值不再为空,并显示该时间值。但相反,它再次显示实时值。

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

有人可以指出我是否做错了什么,或者我是否需要做其他事情才能使其正常工作?谢谢

I'm having trouble getting 2 identical ASP.NET MVC applications to share the same Session using a Session StateServer. The reason I'm trying to do this is we will eventually be deploying this app across 3 web servers that need to share the same state. We need to use StateServer because we are trying to minimise use of the db for non data-related storage.

The Setup:

I've deployed the same code base to http://localhost/App1 and http://localhost/App2

both have identical Web.Config files with the following:

<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" />

I used this tool to generate these machine keys

The Test:

I put the following into one of my Controllers to test if it was working:

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;

with this in the view:

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

The Result:

For both deployments, when the page first loads I can see that the mode is StateServer and the timestamp is getting set to the same time as the realtime value.. However, if this was working, only the first page should have the same time as the realtime value. The second page load should read from the StateServer because that timestamp value is no longer null, and display that time value. But instead, it's displaying the realtime value again.

When I refresh the page, I the timestamp stays the same and the realtime value is always updating. This indicates that the timestamp is being saved to the Session, but the time stamp value is always different for both deployments when it should be the same, so this indicates that the Session is not being shared.

Can somebody point out if I'm doing something wrong or if there's something else I need to do to get this to work? Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

凉墨 2024-09-17 12:09:32

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

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

By default session cannot be shared between different applications. From what I can see you have two distinct applications App1 and App2 which run in separate virtual directories and probably even separate application pools, so don't expect to share session between them.

As always there are workarounds that you may find useful. As you can see it's using a hack (reflection) to circumvent ASP.NET team designer's determination to not expose certain classes and properties and make our life as developers difficult.

宫墨修音 2024-09-17 12:09:32

实际上,您可以使用 Sql 服务器模式共享会话。

试试这个:-

我刚刚更改了程序 ie

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"/>

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

谢谢。

Actually you can share sessions using Sql server mode.

Try this:-

I just changed the procedure i.e.

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

and then modified web.config as:-

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

You have to add Application Name and that have to be the same for all the application for which you want to share the same session.

Thanks.

南风几经秋 2024-09-17 12:09:32

更新:这是我之前就同一主题回答过的帖子
使用 ASP.NET 会话在应用程序之间共享会话状态服务

正如已经指出的,会话数据的范围仅限于应用程序。这就是您在 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。

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

Update: Here is a previous post I answered on this same topic
Sharing sessions across applications using the ASP.NET Session State Service

As already pointed out, Session data is scoped to the application. That is the Application you create in IIS. So two applications with the same session id will not be sharing the same session because of the application scoping.

As an alternative idea that might or might not be feasible for you.
You can create a root application and have the code for D:\App1 and D:\App2 in two subfolders.

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

Then in IIS you create an Application pointing to d:\Root.

You can also create an Application in IIS and then under the Application you create two virtual directories, one pointing to D:\App1 and the other to D:\App2, then they can also share a single web.config at the Application level. It is critical that the two virtual directories are just virtual and not created as Applications.

So you harddisk layout might look something like this

D:\Root
  web.config

D:\App1
  default.aspx
  ...

D:\App2
  default.aspx
  ...

Create the root application pointing to D:\Root and then under the application create the two virtual directories App1 pointing to D:\App1 and App2 pointing to D:\App2.

The effect in both cases is that you actually have one application split into two sections, both in the same Session scope therefore the code for both can share the same session data.

难以启齿的温柔 2024-09-17 12:09:32

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

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

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

You also have to make sure that the application path for the app has to be the same on both web servers. There is an old article here that might help

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

We are currently experiencing a similar problem, except that we are using IIS 7.5 and the application paths are hidden to us (does not use the metabase anymore). Does anyone know a way of troubleshooting this with IIS 7.5?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文