Почему я не могу поделиться состоянием сеанса между двумя веб-приложениями с помощью StateServer? Что мне не хватает?

У меня проблемы с получением 2 одинаковых приложений ASP.NET MVC для совместного использования одного и того же сеанса с помощью Session StateServer. Причина, по которой я пытаюсь это сделать, заключается в том, что мы в конечном итоге развернем это приложение на 3 веб-серверах, которым необходимо использовать одно и то же состояние. Нам нужно использовать StateServer, потому что мы пытаемся свести к минимуму использование БД для хранения, не связанного с данными.

Настройка:

Я развернул ту же базу кода на http://localhost/App1 и http://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" />

Я использовал этот инструмент для генерации этих ключей машин

Тест:

Я поместил следующее в один из моих контроллеров, чтобы проверить, работает ли он:

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, поскольку это значение временной метки больше не равно нулю, и отображать это значение времени. Но вместо этого он снова отображает значение в реальном времени.

Когда я обновляю страницу, метка времени остается неизменной, а значение в реальном времени всегда обновляется. Это означает, что отметка времени сохраняется в сеансе, но значение отметки времени всегда различно для обоих развертываний, когда оно должно быть одинаковым, поэтому это означает, что сеанс не используется совместно.

Может кто-нибудь указать, что я делаю что-то не так или мне нужно что-то еще, чтобы заставить это работать? Спасибо

4 ответа

Решение

По умолчанию сессия не может быть разделена между различными приложениями. Из того, что я вижу, у вас есть два разных приложения App1 а также App2 которые запускаются в отдельных виртуальных каталогах и, возможно, даже в отдельных пулах приложений, поэтому не ожидайте, что между ними будет сеанс

Как всегда, есть обходные пути, которые вы можете найти полезными. Как вы видите, он использует хак (рефлексию), чтобы обойти решимость дизайнера команды ASP.NET не раскрывать определенные классы и свойства и усложнить нашу жизнь разработчиков.

На самом деле вы можете поделиться сессиями, используя режим сервера Sql.

Попробуй это:-

Я просто изменил процедуру, т.е.

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

Вы должны добавить Имя приложения, и оно должно быть одинаковым для всех приложений, для которых вы хотите использовать один и тот же сеанс.

Благодарю.

Обновление: вот предыдущий пост, на который я ответил на эту же тему.

Как уже указывалось, данные сеанса находятся в приложении. Это приложение, которое вы создаете в IIS. Таким образом, два приложения с одинаковым идентификатором сеанса не будут совместно использовать один и тот же сеанс из-за области видимости приложения.

Как альтернативная идея, которая может или не может быть осуществимой для вас. Вы можете создать корневое приложение и иметь код для D:\App1 и D:\App2 в двух подпапках.

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

Затем в IIS вы создаете приложение, указывающее на d:\Root.

Вы также можете создать приложение в IIS, а затем в приложении создайте два виртуальных каталога, один из которых будет указывать на 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.

В обоих случаях эффект состоит в том, что одно приложение разделено на два раздела, оба в одной и той же области действия сеанса, поэтому код для обоих может использовать одни и те же данные сеанса.

Вы также должны убедиться, что путь к приложению должен быть одинаковым на обоих веб-серверах. Здесь есть старая статья, которая может помочь

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

В настоящее время мы сталкиваемся с подобной проблемой, за исключением того, что мы используем IIS 7.5 и пути приложений скрыты для нас (больше не использует метабазу). Кто-нибудь знает способ устранения этой проблемы с IIS 7.5?

Другие вопросы по тегам