如何保护应用程序池免受会话序列化异常的影响?

Nar*_*man 7 asp.net session serialization restart httpmodule

我们正在为ASP.NET应用程序使用进程外会话提供程序(ScaleOut),我们注意到一个没有正确设置反序列化的对象无意中进入会话时,它最终会导致整个终止的过程.

重现和处理这种情况是它变得更有趣的地方.

AnyStaObjectsInSessionState中引发了终止进程的异常,其实现非常简单:

internal static bool AnyStaObjectsInSessionState(HttpSessionState session)
{
    if (session != null)
    {
        int count = session.Count;
        for (int i = 0; i < count; i++)
        {
            object obj2 = session[i];
            if (((obj2 != null) && (obj2.GetType().FullName == "System.__ComObject"))
                && (UnsafeNativeMethods.AspCompatIsApartmentComponent(obj2) != 0))
            {
                return true;
            }
        }
    }
    return false;
}
Run Code Online (Sandbox Code Playgroud)

这是堆栈跟踪,显示了异常如何终止进程:

An unhandled exception occurred and the process was terminated.

Application ID: /LM/W3SVC/1/ROOT

Process ID: 4208

Exception: System.Runtime.Serialization.SerializationException

Message: The constructor to deserialize an object of type 'Lucene.Net.QueryParsers.ParseException' was not found.

StackTrace:    at System.Runtime.Serialization.ObjectManager.CompleteISerializableObject(Object obj, SerializationInfo info, StreamingContext context)
   at System.Runtime.Serialization.ObjectManager.FixupSpecialObject(ObjectHolder holder)
   at System.Runtime.Serialization.ObjectManager.DoFixups()
   at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
   at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
   at System.Web.Util.AltSerialization.ReadValueFromStream(BinaryReader reader)
   at System.Web.SessionState.SessionStateItemCollection.ReadValueFromStreamWithAssert()
   at System.Web.SessionState.SessionStateItemCollection.DeserializeItem(String name, Boolean check)
   at System.Web.SessionState.SessionStateItemCollection.DeserializeItem(Int32 index)
   at System.Web.SessionState.SessionStateItemCollection.get_Item(Int32 index)
   at System.Web.SessionState.HttpSessionStateContainer.get_Item(Int32 index)
   at System.Web.Util.AspCompatApplicationStep.AnyStaObjectsInSessionState(HttpSessionState session)
   at System.Web.HttpApplicationFactory.FireSessionOnEnd(HttpSessionState session, Object eventSource, EventArgs eventArgs)
   at System.Web.SessionState.SessionOnEndTargetWorkItem.RaiseOnEndCallback()
   at System.Web.Util.WorkItem.CallCallbackWithAssert(WorkItemCallback callback)
   at System.Threading.ExecutionContext.runTryCode(Object userData)
   at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallbackInternal(_ThreadPoolWaitCallback tpWaitCallBack)
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(Object state)

InnerException: System.Runtime.Serialization.SerializationException

Message: The constructor to deserialize an object of type 'Lucene.Net.QueryParsers.ParseException' was not found.

StackTrace:    at System.Runtime.Serialization.ObjectManager.GetConstructor(Type t, Type[] ctorParams)
   at System.Runtime.Serialization.ObjectManager.CompleteISerializableObject(Object obj, SerializationInfo info, StreamingContext context)
Run Code Online (Sandbox Code Playgroud)

我们想了解两件事:

  1. FireSessionOnEnd什么时候为进程外提供程序启动,更重要的是,我们如何在没有负载的开发环境中模仿它?我已经尝试降低会话超时(设置为一分钟),手动调用Abandon(),并手动调用GC.Collect(),都无济于事.

  2. 我们是否可以捕获此步骤中发生的错误以保护应用程序池?这里引发的异常是使用Source = ASP.NET 2.0.50727.0记录的,并且没有到达global.asax中的应用程序错误处理程序.即使在对会话绑定对象应用适当的检查和平衡之后,我们还能做些什么来防范这种情况?

任何见解将不胜感激.

Nar*_*man 3

我们在 SOSS 技术支持 \xe2\x80\x93 的帮助下解决了这个问题,他们非常有帮助 \xe2\x80\x93 以下是详细信息:

\n\n
    \n
  • 会话过期后,SOSS 在其客户端库中引发过期事件,而客户端库又负责触发 Global.asax 中的 Session_End (注意: ScaleOut 负载平衡客户端之间的过期事件,因此创建会话的 Web 服务器可能不一定会收到其过期事件 - 这对于尝试重现这些问题至关重要)。
  • \n
  • 由于这种情况发生在请求上下文之外,因此该异常未得到处理并终止应用程序池;
  • \n
  • 这是一种极其罕见的情况,但他们仍将在即将发布的维护版本中解决这一问题;
  • \n
  • 补救措施如下:

    \n\n
      \n
    1. 修复System.Exception派生类型(\xe2\x80\x99s 可序列化但不可反序列化);

    2. \n
    3. 删除 Global.asax 中的 Session_End 事件或禁用过期事件(在 soss_params.txt 中将max_event_retries设置为 0);

    4. \n
    5. 在这些场景中,用户很可能在其请求之一上遇到SerializationException ,这意味着它达到了Application_Error;在这里您可以清除\n会话密钥(必须清除所有\n它们)或直接放弃\n会话;

    6. \n
    7. 订阅\n AppDomain.UnhandledException,\n如果发生未处理的异常,\n将收到通知(此处无追索权,\n仅记录);也可以通过LegacyUnhandledExceptionPolicy禁用它们(不推荐);

    8. \n
  • \n
\n