Почему я не могу поделиться состоянием сеанса между двумя веб-приложениями с помощью 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?