SessionID меняется в разных экземплярах в Azure (и, вероятно, в веб-ферме) - PullRequest
6 голосов
/ 26 февраля 2011

У меня проблема с проектом Azure с одним WebRole, но несколькими экземплярами, в которых используются сеансы без файлов cookie. Приложению не требуется хранилище сеансов, поэтому оно не использует какого-либо поставщика хранилища сеансов, но мне нужно отслеживать идентификатор сеанса. Очевидно, SessionID должен быть одинаковым для всех экземпляров WebRole , но он неожиданно меняется без объяснения. Мы используем SessionID для отслеживания некоторых данных, поэтому это очень важно.

Для воспроизведения вопроса:

  1. Создание Облачного проекта .

  2. Добавить веб-роль ASP.NET . Код уже в нем подойдет.

  3. Открыть Default.aspx

  4. Добавить элемент управления, чтобы увидеть текущий SessionID и кнопку, чтобы вызвать обратную передачу

            <p><%= Session.SessionID %></p>
            <asp:Button ID="Button1" runat="server" Text="PostBack" onclick="Button1_Click" />
    
  5. Добавить обработчик события для кнопки, которая немного задержит ответ:

    protected void Button1_Click(object sender, EventArgs e)
    {
        System.Threading.Thread.Sleep(150);
    }
    
  6. Открыть Web.Config

  7. Включить сеансы без файлов cookie:

    <system.web>
            <sessionState cookieless="true" />
    </system.web>
    
  8. Запустите проект и быстро и неуклонно нажимайте кнопку «PostBack» , обращая внимание на идентификатор сеанса в адресной строке. Ничего не происходит, идентификатор сессии всегда один и тот же :). Останови это.

  9. Открыть ServiceConfiguration.csfg

  10. Включить четыре экземпляра:

    <Instances count="4" />
    
  11. Убедитесь, что в файле Web.config есть строка, связанная с ключом машины, который был автоматически добавлен Visual Studio. (в конце system.web).

  12. Перезапустите проект, быстро нажмите и отпустите кнопку "Postback" на некоторое время и обратите внимание на идентификатор сеанса в адресной строке. Вы увидите, как меняется SessionID через некоторое время.

Почему это происходит? Насколько я знаю, если все машины совместно используют machineKey, сеанс должен быть одинаковым для них. С файлами cookie проблем нет, очевидно, проблема заключается в том, что используются сессии без файлов cookie.

Мое лучшее предположение, что что-то не так происходит, когда есть несколько случаев, когда SessionID, сгенерированный в одном WebRole, переходит в другой, отклоняется и восстанавливается. Это не имеет смысла, так как все WebRole имеют одинаковые machineKey.

Чтобы выяснить проблему и увидеть ее более четко, я создал собственный SessionIDManager:

public class MySessionIDManager : SessionIDManager
{
    public override string CreateSessionID(HttpContext context)
    {
        if (context.Items.Contains("AspCookielessSession"))
        {
            String formerSessionID = context.Items["AspCookielessSession"].ToString();

           // if (!String.IsNullOrWhiteSpace(formerSessionID) && formerSessionID != base.CreateSessionID(context))
               // Debugger.Break();

            return formerSessionID;
        }
        else
        {
            return base.CreateSessionID(context);
        }
    }
}

И для его использования измените эту строку в WebConfig:

    <sessionState cookieless="true" sessionIDManagerType="WebRole1.MySessionIDManager" />

Теперь вы можете видеть, что SessionID не меняется, независимо от того, как быстро и как долго вы бьете. Если вы раскомментируете эти две строки, вы увидите, как ASP.NET создает новый sessionID, даже если он уже есть.

Чтобы заставить ASP.NET создать новый сеанс, просто перенаправьте на ваш сайт абсолютный URL:

 Response.Redirect(Request.Url.AbsoluteUri.Replace(Request.Url.AbsolutePath, String.Empty));

Почему это происходит с сеансами без печенья?

Насколько надежно мое решение в MySessionIDManager?

С уважением.

UPDATE

  • Я пробовал этот обходной путь: указанные пользователем машинные ключи Перезаписано на уровне авто Конфигурация , но проблема все еще стоит.

    public override bool OnStart()
    {
        // For information on handling configuration changes
        // see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
    
        using (var server = new ServerManager())
        {
            try
            {
                // get the site's web configuration
                var siteNameFromServiceModel = "Web"; // update this site name for your site. 
                var siteName =
                    string.Format("{0}_{1}", RoleEnvironment.CurrentRoleInstance.Id, siteNameFromServiceModel);
                var siteConfig = server.Sites[siteName].GetWebConfiguration();
    
                // get the appSettings section
                var appSettings = siteConfig.GetSection("appSettings").GetCollection()
                    .ToDictionary(e => (string)e["key"], e => (string)e["value"]);
    
                // reconfigure the machine key
                var machineKeySection = siteConfig.GetSection("system.web/machineKey");
                machineKeySection.SetAttributeValue("validationKey", appSettings["validationKey"]);
                machineKeySection.SetAttributeValue("validation", appSettings["validation"]);
                machineKeySection.SetAttributeValue("decryptionKey", appSettings["decryptionKey"]);
                machineKeySection.SetAttributeValue("decryption", appSettings["decryption"]);
    
                server.CommitChanges();
                _init = true;
            }
            catch
            {
            }
        }
        return base.OnStart();
    }
    
  • Я тоже пробовал это по поводу поставить обработчик запуска сеанса и добавление некоторые данные, но не повезло.

    void Session_Start(object sender, EventArgs e)
    {
        Session.Add("dummyObject", "dummy");
    }
    

Баунти up!

Ответы [ 3 ]

3 голосов
/ 02 марта 2011

Короче говоря, если вы не используете куки-файлы или поставщика сеансов, у идентификатора сеанса нет возможности передать один экземпляр веб-роли другому. В сообщении, о котором вы упоминаете, говорится, что SessionID НЕ будет одинаковым для всех веб-ролей, если вы не используете файлы cookie или хранилище сеансов.
Проверьте этот предыдущий вопрос о способах обработки хранилища состояний в Azure, например используя Table Storage

machineKey не имеет никакого отношения к сеансам или домену приложения, это ключ, используемый для шифрования, дешифрования, проверки аутентификации и просмотра данных состояния. Чтобы проверить это, откройте SessionIDManager.CreateSessionID с помощью Reflector. Вы увидите, что значение идентификатора - это просто случайное 16-байтовое значение, закодированное в виде строки.

Значение AspCookielessSession уже проверяется SessionIDManager в методе GetSessionID, а не CreateSessionID, поэтому проверка уже завершена до выполнения вашего кода. Так как режимом состояния сеанса по умолчанию является InProc, он предполагает, что отдельные веб-роли не смогут проверить ключ сеанса, поэтому они создают новый.

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

РЕДАКТИРОВАТЬ Наконец-то я получил TableStorageSessionStateProvider для работы в режиме без файлов cookie!

В то время как TableStorageSessionStateProvider поддерживает режим без файлов cookie, переопределяя SessionStateStoreProviderBase.CreateUnititializedItem , он не может правильно обрабатывать пустые сеансы в приватном сеансе SessionStateStoreData (блокировка HttpContext, идентификатор строки, идентификатор строки, выход из bo out object lockId, out действия SessionStateActions, исключение bool) . Решение состоит в том, чтобы вернуть пустой SessionStateStoreData, если в базовом хранилище больших двоичных объектов не найдено данных.

Метод длиной 145 строк, поэтому я не буду его вставлять. Искать следующий кодовый блок

if (actions == SessionStateActions.InitializeItem) 
{
     // Return an empty SessionStateStoreData                    
     result = new SessionStateStoreData(new SessionStateItemCollection(),
}

Этот блок возвращает пустой объект данных сеанса при создании нового сеанса. К сожалению, пустой объект данных не сохраняется в хранилище BLOB-объектов.

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

if (actions == SessionStateActions.InitializeItem || stream.Length==0)

Long Stroy Short Состояние сеанса cookieles работает, пока поставщик поддерживает его. Вам придется решить, оправдывает ли использование состояния без cookie использование примера провайдера. Возможно, vtortola должен проверить Кэширование AppFabric CTP . Он включает в себя готовые ASP.NET-провайдеры, намного быстрее и определенно имеет лучшую поддержку, чем примеры провайдеров. Существует даже пошаговое руководство о том, как настроить с ним состояние сеанса.

1 голос
/ 10 марта 2011

У меня возникла та же проблема, и после долгих исследований и отладки я обнаружил, что проблема возникла из-за того, что «виртуальные серверы» в Azure SDK отображают сайты по разным путям в метабазе IIS.(Это можно увидеть с помощью Request.ServerVariables ["APPL_MD_PATH"].)

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

1 голос
/ 28 февраля 2011

Звучит сложно.

У меня есть одно предложение / вопрос для вас. Не знаю, поможет ли это, но похоже, что ты готов попробовать что-нибудь!

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

Я думаю, что решение может прийти из: - используя Session_Start, как описано выше, чтобы вставить что-то в хранилище Session - плюс вставка постоянного поставщика памяти Session некоторого описания в web.config - например, некоторые из самых старых образцов Azure предоставляют поставщика на основе таблиц, или некоторые из более новых образцов предоставляют решение для кэширования AppFabric.

Я знаю, что ваш дизайн не использует хранилище сессий, но, возможно, вам нужно что-то добавить (немного похоже на Session_Start), плюс вам нужно определить что-то иное, чем управление сессиями в процессе.

В качестве альтернативы, вам нужно изменить дизайн своего приложения на нечто иное, чем сессии ASP.NET.

Надеюсь, это поможет - удачи!

...