COM+在负载下调用COM对象时挂起

3
我们有一些使用大家最喜欢的语言(?)VB6编写的COM+代码。这个COM+组件调用了一个由第三方编写的标准COM组件,该组件执行对SQL Server数据库的调用。在COM+中我们什么也不做,除了(以下是示例,我们并不真正调用doStuff函数 :-) ):
Function doStuff
   Dim o As Library.Object
   Set o = New Library.Object
   str = o.DoSomething()
   Set o = Nothing
   doStuff = str
End Function

作为一个可怕的快速压力测试,我们将调用包装在一个非常简单的VBScript中,它简单地创建对象,在循环中调用方法,将对象设置为无效,并重复几次。然后我们在命令提示符中并行运行四个这样的脚本。
我们的经验是,四个COM+窗口停止了,就好像它们在某种方式上相互阻塞一样。根据输出的行为,看起来不同的窗口在某个地方共享对象的实例:例如,输出在窗口之间出现的速度在窗口之间同步...所以两个窗口可以以很快的速度运行,而另外两个则每秒钟输出一行(当它们输出这一行时,它们同时进行)。
最终,所有四个窗口似乎都停止了——在组件服务中,我们看到调用时间开始上升(从每个调用的几毫秒,上升到30、40秒)。有时dllhost.exe会失败,我们会看到一个COM代理错误对话框(此时窗口会恢复,生成一个新的dllhost)。

数据库上没有任何活动,因此我们排除了数据库层面的问题。我们尝试通过将 COM+ 组件设置为“禁用事务”来获得更好的结果,但挂起问题并未消失。我们将尝试使用 CreateObject 创建 COM 对象而不是 new,以查看它是否会有什么作用(如果有)。在 COM+ 和 VBScript 层面完成对象后都将其设为 Nothing

值得注意的是,如果直接从 VBScript 调用第三方库(绕过 COM+),则不会出现任何问题。因此,似乎与 COM+ 与 COM 对象交互的方式有关,但除了在组件服务下调整对象属性的不同设置外,不确定还发生了什么。

有什么建议可以解决这个问题吗?或者可以调整哪些设置?

额外信息
回答问题中的答案:

进一步的工作... 看起来这是一个同步问题,深层次涉及到COM+或COM。在我们的测试脚本中,如果我们在每个迭代中添加一个10-50ms的随机延迟,问题就会消失。如果我们有一个固定的延迟,我们就会锁定。一些谷歌搜索似乎表明,在具有STA的高负载COM+上可能存在问题,这在MS博客上有记录。回到Server 2000或Server 2003 SP1可能是下一步要做的事情...
3个回答

2
听起来你可能遇到了COM+和STA的问题,这是因为跨单元调用。Microsoft曾经发表过一篇由Michael McKeown撰写的文章“从MTS迁移到COM+时保持应用程序性能”,但现在已被删除(有一个存档版本here)。
基本上,COM+ STA线程池将每个STA线程绑定到5个活动。当您进行跨单元调用(第三方组件或SQL Server)时,COM+允许其他请求作为STA线程上的另一个活动进行服务。这可以发生多达5个活动(每个线程)。此外,一旦控制权交给另一个活动,原始活动就无法重新获得控制权,直到第二个活动完成。在负载较重和/或调用“长时间运行”的情况下,第一个活动完成所需的时间是所有其他活动(在线程上)完成时间的总和。这会影响您的性能。
如果您能够切换整个COM+服务器的设置,您可以配置COM+使用旧的MTS 100 STA线程方法。有关详细信息,请参见用于调整COM+线程和活动的注册表键。您可以查看是否有助于提高性能。另一种方法是避免STA组件。

这基本上是我根据谷歌和实验得出的结论...虽然很高兴得到了确认。通常调用不应该是长时间运行的(在正常操作中也不是),所以我们遇到了一些限制,基本上破坏了线程池的编组。需要审查是否将服务器放入旧方法是继续进行的最佳方式:我怀疑我们会尝试通过不过度使用服务器来避免它(毕竟,我们只在潜在的人工负载测试期间使用它),除非我们没有其他选择。 - Chris J

0

或许 COM+对象池概念 和类似的文章,如配置组件以进行池化 会有所帮助。

可池化对象必须满足某些要求,以使单个对象实例可以被多个客户端使用。例如,它们不能持有客户端状态或具有任何线程亲和性。


由于这是一个VB6 COM+对象,所以不可能设置池选项,因为VB6 COM+对象仅限于STA。该第三方组件未在COM+中注册,而是标准的COM组件。 - Chris J
那是确实如此。这可能与VB6代码中使用全局变量有关吗?http://support.microsoft.com/kb/815053 - Bob77
没有 -- 已经检查过了。我们还双重检查了无人值守执行标志和保留在内存中的设置,按照http://support.microsoft.com/kb/264957的说明正确设置。 - Chris J

0

有两件事需要考虑:

  1. 你尝试过将 o 变量设为局部变量而不是模块级别吗?

    Function doStuff Dim o as Library.Object
    Set o = New Library.Object str = o.DoSomething() Set o = Nothing doStuff = str End Function

  2. 你确定 Library.Object 组件和 .DoSomething 方法不包含全局变量(或 MessageBox 语句)吗?

  3. 你可以在每行代码后添加日志记录语句,以查看代码出现问题的位置吗?

  4. 使用 ProcMon 查看它何时停止访问注册表。最后一次调用失败了吗?如果是,那么失败的原因是什么?


  1. 已存在,只是缺失[已修复];
  2. 没有MessageBox - DLL没有设计直接与UI交互 - 全局变量不知道;
  3. & 4. 我们可能需要做的事情:我的怀疑是调用第三方库...另外,刚刚在问题中添加了更多信息:在重载下,似乎COM+与STA可能会锁定...
- Chris J

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接