7

我们正在为 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;
}

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

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)

我们想了解两点:

  1. FireSessionOnEnd 何时为进程外提供者触发,更重要的是,我们如何在没有负载的开发环境中模拟这一点?我尝试过降低会话超时(设置为一分钟)、手动调用 Abandon() 和手动调用 GC.Collect(),但均无济于事。

  2. 我们可以捕获在这一步发生的错误以保护应用程序池吗?此处引发的异常使用Source=ASP.NET 2.0.50727.0进行记录,并且不会到达 global.asax 中的应用程序错误处理程序。即使在对会话绑定对象应用了适当的制衡之后,我们可以做些什么来防止这种情况发生?

任何见解将不胜感激。

4

3 回答 3

3

在 SOSS 技术支持的帮助下,我们能够解决这个问题 - 他们非常有帮助 - 以下是详细信息:

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

    1. 修复System.Exception派生类型(可序列化但不可序列化);

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

    3. 在这些情况下,用户很可能在他们的一个请求中遇到 SerializationException,这意味着它到达 Application_Error;在这里您可以清除会话密钥(必须全部清除)或彻底放弃会话;

    4. 订阅 AppDomain.UnhandledException以收到未处理异常的通知,如果它们发生(这里没有追索权,只是记录);它们也可以通过 legacyUnhandledExceptionPolicy禁用(不推荐);

于 2010-06-30T13:51:05.110 回答
1

我们可以捕获在这一步发生的错误以保护应用程序池吗?此处引发的异常使用 Source=ASP.NET 2.0.50727.0 进行记录,并且不会到达 global.asax 中的应用程序错误处理程序。即使在对会话绑定对象应用了适当的制衡之后,我们可以做些什么来防止这种情况发生?

我不知道是否可行,但你可以试一试

于 2010-06-30T04:47:33.453 回答
0

我通过简单地完全删除 SessionEnd 方法来解决这个问题。删除方法的内容是不够的,因为 Asp.net 使用反射搜索方法的存在,然后运行有问题的代码。

于 2016-01-27T12:18:02.323 回答