1

我们的业务层服务器上有一个 Web 前端。

我们的 Web 应用程序中的某些页面会实例化非常长时间运行的任务(可能长达 10 多分钟)。处理这些请求的方式是这样的: -

(在 HTTP 请求线程上)

  • 我们连接到业务服务器。
  • 我们创建一个新线程来进行长时间运行的调用,并传入连接对象。
  • 然后 HTTP 请求完成,将句柄传回浏览器,
  • 浏览器会定期轮询 Web 服务器以获取有关长时间运行的任务进度的更新。

对业务服务器的所有请求都经过身份验证 - 连接的用户主体页面必须有权调用业务服务器上的方法。

只要我们的 Web 应用程序在经典模式下运行,这种机制就可以正常工作。当我们在管道模式下运行时,我们会在浏览器轮询时得到 ObjectDisposedExceptions。

System.ObjectDisposedException: Safe handle has been closed
at System.StubHelpers.StubHelpers.SafeHandleC2NHelper(Object pThis, IntPtr CleanupWorkList)
at Microsoft.Win32.Win32Native.GetTokenInformation(SafeTokenHandle TokenHandle, UInt32 TokenInformationClass, SafeLocalAllocHandle TokenInformation, UInt32 TokenInformationLength, ref UInt32 ReturnLength)
at System.Security.Principal.WindowsIdentity.GetTokenInformation(SafeTokenHandle tokenHandle, TokenInformationClass tokenInformationClass, ref UInt32 dwLength)
at System.Security.Principal.WindowsIdentity.get_User()
at System.Security.Principal.WindowsIdentity.GetName()
at System.Security.Principal.WindowsIdentity.get_Name()

问题似乎是用于建立连接的 windows 主体在原始请求结束时被释放(这是可以理解的 - 事实上我很惊讶代码完全可以工作!)。

作为解决此问题的一种方法,我想知道是否可以创建 HTTP 请求主体的副本并使用它来创建连接(并在长时间运行的任务完成时将其丢弃),或者是否可以模拟主体处理后,工作线程上的HTTP请求原理?

更新

(我在 Aliostad 的问题下的评论是不正确的:测试页面确实失败了。我设法让自己感到困惑,以至于我编写了我的测试页面,因此它没有使用与真实(错误)代码相同的代码路径。没关系!)

我已经为这个问题编写了一个“解决方法”: - 我很幸运地知道在调用业务服务器之前业务服务器逻辑将查询哪些角色/组。因此,我的解决方法是根据请求的主体在这些角色中的成员身份创建一个新的通用主体。长时间运行的任务使用通用主体运行。

我对这种解决方法不是 100% 满意,因为它在很大程度上是一种“黑客行为”——即我可以看到,如果某些逻辑(非常明智)检查验证主体的身份是否经过身份验证,它很容易崩溃。

因此,我仍然非常感谢对此问题的任何帮助/见解。

谢谢

4

1 回答 1

1

好的,这是我的理解。

首先,如果你创建了一个线程,所有当前线程的安全上下文都会被复制到新线程——默认情况下。此操作很繁重但非常需要(您可以想象,如果没有它,大多数事情都将无法工作)。如果您需要阻止它并且您不需要复制上下文,有一种方法可以做到这一点,并且已经通过 CLR 在 Richter 的 C# 中进行了解释。幸运的是,他在这里分享了这本书的一小部分,并且基本上调用了一个静态方法来防止上下文flowed

ExecutionContext.SuppressFlow();

我不认为这是在 WCF 中调用的,尽管使用了 Reflector,我在这里找到了它的一个用法:

  [SecuritySafeCritical]
    private IAsyncResult BeginGetContext(bool startListening)
    {
        Exception exception;
        do
        {
            exception = null;
            try
            {
                try
                {
                    if (ExecutionContext.IsFlowSuppressed())
                    {
                        return this.listener.BeginGetContext(this.onGetContext, null);
                    }
                    using (ExecutionContext.SuppressFlow())
                    {
                        return this.listener.BeginGetContext(this.onGetContext, null);
                    }
                }
                // .... the rest

有趣的是,这在 3 个地方使用,其中之一在SharedHttpTransportManager.

现在这一切看起来好像我们已经找到了问题,这是一个错误,但我非常怀疑。

我的预感是在两者之间发生了一个进程回收,并且上下文丢失了。证明或反驳这一点的方法是使用 perfmon 来注册所有进程回收并找出两者之间是否有任何。

我的解决方案基本上是 - 你可能不喜欢!- 简单地将项目插入队列(MSMQ 或简单的数据库队列)并让 Windows 服务读取它。由于这个操作如此重要,我永远不会相信 IIS 会执行到底。

希望这对你有用。

于 2010-11-29T13:30:29.733 回答