【问题标题】:How to protect application pools from session serialization exceptions?如何保护应用程序池免受会话序列化异常的影响?
【发布时间】:2023-04-03 18:01:01
【问题描述】:

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

重现和处理这个场景变得更加有趣。

终止进程的异常在 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 中的应用程序错误处理程序。即使在对会话绑定对象应用了适当的制衡机制之后,我们可以做些什么来防止这种情况发生?

我们将不胜感激。

【问题讨论】:

  • 也许他们应该修复他们的代码以不允许序列化异常导致 AppDomain 崩溃。只是一个想法。
  • 这是个好主意,我认为他们同意;这是一种极为罕见的情况,但仍应加以防范。
  • @John:看起来他正在使用 Lucene.net。虽然它是第 3 方应用程序,AFAIK,它的开源,他应该标记违规类Serializable
  • 你说得对,应该由 ScaleOut 产品的供应商来防范它。

标签: asp.net session serialization httpmodule restart


【解决方案1】:

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

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

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

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

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

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

【讨论】:

    【解决方案2】:

    我们可以捕获在此发生的错误吗 保护应用程序池的步骤?这 此处引发的异常记录 w/ Source=ASP.NET 2.0.50727.0 并且不要 到达应用程序错误处理程序 在 global.asax 中。我们能做些什么 提防这种情况,即使 经过适当的制衡 应用于会话绑定对象?

    我不知道this 是否有效,但你可以试一试

    【讨论】:

    • 谢谢,但这并不能真正防范这种情况,它只是让您记录它。这是一种快速重现的方法:brianlow.com/index.php/2007/01/11/…。后台线程必须捕获异常,否则船会沉没。
    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-28
      • 2012-02-16
      • 2016-05-14
      • 2011-02-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多