2013-12-24 2 views
2

мы используем StateServer для обработки сеанса для известных преимуществ (веб-ферма, переработка IIS)..NET Отказоустойчивый StateServer

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

Однако, кажется, нет никакого способа обнаружения, если StateServer онлайн или нет, поэтому следующий код все работает нормально, даже если StateServer вниз

try 
{ 
    //It is not NULL as it has been configured 
    if (HttpContext.Current.Session != null) 
     Session["Test"] = "value"; 
} 
// No exception is thrown 
catch (Exception) 
{ 
    throw new Exception(); 
} 

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

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

Невозможно выполнить запрос состояния сеанса на сервер состояния сеанса. Убедитесь, что служба состояния ASP.NET запущена и что клиентские и серверные порты совпадают.

Что бы я хотел сказать, так это то, что запись просто терпит неудачу (или регистрирует ошибку), и клиенты не подвергаются воздействию. Поскольку это сейчас пишется, весь сайт падает из-за этой единственной точки отказа.

Любые идеи - я пропустил что-то очевидное?

+1

Вы пробовали ELMAH или событие OnError в global.asax, чтобы поймать эти ошибки? – tgolisch

+0

Да, это может сработать - и тогда я мог бы использовать User Server.ClearError(), если это тип ошибки. Позвольте мне поиграть с этим. –

+0

Если провайдер сеанса не удалось инициализировать из-за остановки службы StateServer, он будет разорвать процесс запроса и выполнит событие OnError в global.asax, но после этого вы не сможете возобновить обработку запроса. И Server.ClearError отключит публикацию этого исключения в EventLog. Он не возобновит обработку. Единственный способ, который я вижу, - написать собственный поставщик сеансов, как в моем ответе. –

ответ

0

Я хотел бы принять ответ tgolisch как решение, которое работает на меня.

  • В Global.asax мы будем искать недостающий ошибки StateServer в случае Application_Error
  • Если мы находим мы будем использовать сервер.ClearError() и регистрируется ошибка
  • Мы также будем использовать это, чтобы зарегистрировать ошибку и, возможно, отправить уведомление

Спасибо всем!

1

хорошо, это может быть трудно. Asp.net использует сеанс жестко, поэтому, если сбой сеанса невозможен, его asp.net также будет терпеть неудачу во время инициализации модуля сеанса. Вы можете написать собственный session state provider, который будет обертывать существующий, и в случае отказа он вернет пустые элементы сеанса, но его может быть сложно использовать, поскольку поведение сеанса может быть непредсказуемым.

Вы можете найти встроенный в SQL session state provider, который имеет переход на другой ресурс, если ваш SQL-сервер имеет репликацию.

Update1

Вот пример обертку для поставщиков сеансов по умолчанию

public class SessionProviderWrapper : SessionStateStoreProviderBase 
{ 
    private readonly SessionStateStoreProviderBase _provider; 

    private static Func<SessionStateStoreProviderBase> _createProvider; 

    static SessionProvider() 
    { 
     _createProvider = InitializerProvider(); 
    } 

    private static Func<SessionStateStoreProviderBase> InitializerProvider() 
    { 
     if (_createProvider != null) 
      return _createProvider; 

     var sessionType = "stateserver"; // you can switch to another session provider 

     Type type; 
     switch (sessionType) 
     { 
      case "inproc": 
       type = Type.GetType("System.Web.SessionState.InProcSessionStateStore, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); 
       break; 

      case "sql": 
       type = Type.GetType("System.Web.SessionState.SqlSessionStateStore, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); 
       break; 

      case "stateserver": 
       type = Type.GetType("System.Web.SessionState.OutOfProcSessionStateStore, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); 
       break; 

      default: 
       throw new ConfigurationErrorsException("Unknow session type: " + sessionType); 
     } 

     if (type == null) 
     { 
      throw new InvalidOperationException("Failed to find session provider for " + sessionType); 
     } 

     _createProvider = GenerateConstructorCall(type); 

     return _createProvider; 
    } 

    private static Func<SessionStateStoreProviderBase> GenerateConstructorCall(Type type) 
    { 
     // we are searching for public constructor 
     var constructor = type.GetConstructors().FirstOrDefault(c => c.GetParameters().Length == 0); 
     if (constructor == null) 
     { 
      // otherwise for internal. SQL session provider has internal constructor, but we don't care 
      constructor = type.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault(c => c.GetParameters().Length == 0); 
     } 

     var node = Expression.New(constructor); 
     var lambda = Expression.Lambda<Func<SessionStateStoreProviderBase>>(node, null); 
     var func = lambda.Compile(); 
     return func; 
    } 

    public SessionProvider() 
    { 
     var createProvider = InitializerProvider(); 

     _provider = createProvider(); 
    } 

    public override void Initialize(string name, NameValueCollection config) 
    { 
     _provider.Initialize(name, config); 
    } 

    public override string Name 
    { 
     get { return _provider.Name; } 
    } 

    public override string Description 
    { 
     get { return _provider.Description; } 
    } 

    public override void Dispose() 
    { 
     _provider.Dispose(); 
    } 

    public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback) 
    { 
     return _provider.SetItemExpireCallback(expireCallback); 
    } 

    public override void InitializeRequest(HttpContext context) 
    { 
     _provider.InitializeRequest(context); 
    } 

    public override SessionStateStoreData GetItem(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, 
             out SessionStateActions actions) 
    { 
     try 
     { 
      return _provider.GetItem(context, id, out locked, out lockAge, out lockId, out actions); 
     } 
     catch (Exception ex) 
     { 
      locked = false; 
      lockAge = TimeSpan.Zero; 
      lockId = null; 
      actions = SessionStateActions.None; 
      // log ex 
      return new SessionStateStoreData(new SessionStateItemCollection(), new HttpStaticObjectsCollection(), 10); 
     } 
    } 

    public override SessionStateStoreData GetItemExclusive(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, 
                out SessionStateActions actions) 
    { 
     return _provider.GetItemExclusive(context, id, out locked, out lockAge, out lockId, out actions); 
    } 

    public override void ReleaseItemExclusive(HttpContext context, string id, object lockId) 
    { 
     _provider.ReleaseItemExclusive(context, id, lockId); 
    } 

    public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem) 
    { 
     _provider.SetAndReleaseItemExclusive(context, id, item, lockId, newItem); 
    } 

    public override void RemoveItem(HttpContext context, string id, object lockId, SessionStateStoreData item) 
    { 
     _provider.RemoveItem(context, id, lockId, item); 
    } 

    public override void ResetItemTimeout(HttpContext context, string id) 
    { 
     _provider.ResetItemTimeout(context, id); 
    } 

    public override SessionStateStoreData CreateNewStoreData(HttpContext context, int timeout) 
    { 
     return _provider.CreateNewStoreData(context, timeout); 
    } 

    public override void CreateUninitializedItem(HttpContext context, string id, int timeout) 
    { 
     _provider.CreateUninitializedItem(context, id, timeout); 
    } 

    public override void EndRequest(HttpContext context) 
    { 
     _provider.EndRequest(context); 
    } 
} 

В принципе вы можете сделать попытку \ поймать на каждом методе, как и в методе GetItem, и в случае ошибки, вы можете вернуть пустой объект сеанса. Если это не удастся в try \ catch, приложение останется в живых. Но производительность будет уменьшена, так как для каждого запроса он выкинет пару исключений из Get \ Release, которые будут обрабатываться в секции catch. Но в любом случае эти исключения уменьшат производительность немного

Смежные вопросы