Queued Component设计时了解事项
摘要:本章概述了设计MSMQ队列组件时需要了解的9个事项。
4.1. 公有队列和私有队列
把一个COM+应用设置为排队之后,本地的MSMQ队列中将会增加7个队列:一个公有队列,6个私有队列。
公有队列,它的名字和COM+应用程序的名字相同。它通过活动目录通告它的存在。
6个私有队列中,序号为0-4的5个重试队列,一个死信队列。
4.2. COM+应用必须手动启动
为QCMsg接收任何消息的QCMsg的父应用程序必须是运行的。这一点很关键。为确信你的应用程序正在运行,在组件服务中在它上面简单地右击并选择Start。
注意,即使应用程序不在运行,客户仍能通过记录器发送消息---它们只是不拾取并播放给QCMsg的一个新实例,直到应用程序下一次运行。
应用程序需要明确地运行或者另外使用COM+管理对象启动,这也许看起来很奇怪。
正是QC和MSMQ的异步的、监视的属性,使明确地启动成为必须。
4.3. 禁止安全
出现过下面情况的话,那么你可以采用禁止安全的策略。
现象:
我的机器没有联网的情况下,我运行QCClientVC程序,调用QCCompVC队列组件。这时候,QCCompVC组件没有被调用,实际上消息队列的系统队列à事务性死信消息中出现了这些消息。错误原因,上面写的是“签名无效”。
同时,在应用程序日志中出现这样的日志:
事件类型: 错误
事件来源: AutoEnrollment
事件种类: 无
事件 ID: 15
描述:
本地系统 的自动证书注册联系 Active Directory (0x8007054b) 失败。 指定的域不存在,或无法联系。
将不会执行注册。
有关更多信息,请参阅在 http://go.microsoft.com/fwlink/events.asp 的帮助和支持中心。
和你指定的NT域拓扑相伴的安全设置和其他管理问题给这个处理增加了另一个更复杂的层。为了完全去除安全层,转到Tomo_QC_Demo的COM+应用程序的属性页,并在Security属性标签上单击,调整设置使Authentication level for calls设置为None,如下图所示:
如果你的测试机器Windows2000处于Workgroup工作组模式,那么必须应用同样的策略---禁止安全。因为在这个模式下,Windows 2000活动目录服务不能用来提供用户验证。用于创建的调用或使用你的队列组件将失败。
4.4. 事务队列
默认地,当你为一个COM+应用程序单击Queued复选框时,你获得了七个事务队列。
当COM+重复地试图投递一个事务的消息给一个崩溃的调用SetAbort()或者涉及一些其他失败的RM对象,它在有限的队列中移动----从最低到最高。最终,如果消息不能被投递,它在死消息队列中结束(或者我们自己定义一个“异常类”,让COM+播放给我的异常类)。
注意,七个队列中的每一个队列都是事务的,但当未能投递它时,从一个队列向更高数字队列的消息的移动并不意味着它是一个事务队列的一部分。通过六个队列的向上移动并最终达到一个死消息队列是一个COM+的约定---不是事务或MSMQ的约定。
重试次数是单一的----不是累积的。换句话说,在第一个失败上等待60秒后,COM+在第二个失败后等待60秒等等。
所以,你不要相信一个消息仅仅会被投递一次。
你也可以不使用事务队列。这些队列不保证投递,可以确信,消息可能完全丢失。
4.5. 异常类
如果一个队列方法调用未能投递时,你想被明确地通知,那么你仅仅需要声明一个异常类(exception class)。
如果你不想窥视一个队列并分析失败的消息,那么你可以请求COM+播放失败的方法调用给你的异常类。你的异常类所需要做的是,简单地实现所讨论的包含失败方法调用的队列接口。
换句话说,你能如此编写你的异常类以致在某种意义上它是接收器对象的有害孪生兄弟。如果方法调用未能播放给接收器对象,那么你的异常类将代替地接收方法调用,正好像它是最初的想到达的目标对象。
4.6. 释放Recorder对象的重要性
调用队列组件的人A根本不必知道它正在使用的组件是一个队列组件。A调用::CoGetObject函数为组件检查代理。这里,A找到的是一种队列组件对象的Recorder对象。
一旦A得到Record对象,它就能调用QCMsg服务器方组件。
当Recorder对象被释放后,数据将被传送到本地的MSMQ队列中。当客户机同服务器相联系时,MSMQ就把数据送到服务器进行处理。
换句话说,只有Recorder对象被立即释放,这个请求才会真的被传送到MSMQ队列。否则,将等待中。
所以,A中就是每一次请求QCMsg都重新创建一次Recorder对象,代码如下所示:
for (itPath = m_oUGSystemProfile.m_listQCInfo.begin();
itPath != m_oUGSystemProfile.m_listQCInfo.end();
itPath ++)
{
IInteraction *pInteraction = NULL;
HRESULT hr = ::CoGetObject((*itPath),
NULL,
IID_IInteraction,
(void**)&pInteraction);
if(pInteraction != NULL)
{
pInteraction->XXXXXXXXX;
pInteraction->Release();
pInteraction = NULL;
}
else
{
…错误处理
}
}
4.7. 没有安装COM+代理的结果
如果,请求的服务器名为一个你没有安装队列组件COM+代理的服务器,那么将会报告如下错误:
事件类型: 错误
事件来源: COM+
事件种类: (115)
事件 ID: 4808
描述:
消息队列 API 返回一个意外错误。MQPathNameToFormatName : 队列不存在,或者您没有足够权限来执行此操作。
服务器应用程序 ID: {BF966AC5-BDA0-4869-9430-BA3B0325D425}
服务器应用程序实例 ID:
{F277BBD0-BB06-4E4F-B633-2ABE09FFBB01}
服务器应用程序名: Tomo_QC
错误代码= 0xc00e0003 :
COM+ 服务内部信息:
文件: d:\nt\com\com1x\src\comsvcs\qc\msmqrt\msmqrt.cpp, 行: 746
Comsvcs.dll 文件版本: ENU 2001.12.4414.46 shp
有关更多信息,请参阅在 http://go.microsoft.com/fwlink/events.asp 的帮助和支持中心。
4.8. 重试的次数
根据
ms-help://MS.MSDNQTR.2002OCT.1033/cossdk/htm/pgservices_queuedcomponents_6l4j.htm
的说明:
The first retry queue. A message can be retried three times on this queue before being moved to the back of the second retry queue.
其他的retry队列也有相同的属性:进入一个retry队列,在移到下一个队列之前,会重试三次。
那么就是5个retry队列,总共rertry15次。
再加上最开始的一次,在到达Dead Queue之前,会发送消息16次。
4.9. Exception Class Component的制作
首先,一定要实现两个接口:
n 你的队列组件的要监听的那个接口;
n COMSVCS的IPlaybackControl接口。
VB中的例子代码为:
Implements IPlaybackControl
Implements IInteraction
Private Sub IInteraction_DisplayMessage(ByVal strMessege As String)
Dim oTrace As New Tracer
oTrace.Trace strMessege & CStr(Time())
Set oTrace = Nothing
End Sub
Private Sub IPlaybackControl_FinalClientRetry()
Dim oTrace As New Tracer
oTrace.Trace "IPlaybackControl_FinalClientRetry"
Set oTrace = Nothing
End Sub
Private Sub IPlaybackControl_FinalServerRetry()
Dim oTrace As New Tracer
oTrace.Trace "IPlaybackControl_FinalServerRetry"
Set oTrace = Nothing
End Sub
其次,在队列组件接口的属性页à高级的“队列异常类”编辑框中输入这个Exception Class Component的ProgID或者ClassID。如下图所示:
建议,把这个Exception Class Component也放入COM+。
这样,当以下两种情况发生时,这个组件将被调用:
第一种:ServerError
服务器端的调用遭遇崩溃,导致消息不断被retry,直至送到dead queue。这时候,ECC的IPlaybackControl_FinalServerRetry()方法将会被调用。
接下来,IInteraction_DisplayMessage(ByVal strMessege As String)被调用,参数如假包换地被传递进来。这样,就可以记录日志,或者做一些无害的处理。
第二种:ClientError
如果消息根本不能被传递给服务器端,比如服务器关闭。
这时候,ECC的IPlaybackControl_FinalClientRetry()方法将会被调用。
接下来,IInteraction_DisplayMessage(ByVal strMessege As String)被调用。
Microsoft、Windows是 Microsoft Corporation 在美国和/或其他国家(地区)的注册商标或商标。
本文提到的其他产品和公司名称可能是其各自所有者的商标。
Edited by zhengyun (at) tomosoft.com