设为首页收藏本站language 语言切换
查看: 2099|回复: 0
收起左侧

在.NET中使用COM+队列组件

[复制链接]
发表于 2010-2-25 10:26:53 | 显示全部楼层 |阅读模式
<p >.NET在异步函数调用上作了很大的努力。通过“代表(DELEGATE)”程序员可以非常方便对函数进行异步调用。编译器在幕后加入了BeginEnvoke()以及EndEnvoke()函数的定义和实体,这样你可以对对象的函数进行异步调用,并方便地拿到函数返回结果。<p >但是.NET提供的异步调用还不能完全取代COM+队列组件提供的异步功能,原因如下:<p >第一,.NET不支持断开的网络。客户端和服务器程序必须同时在线运行,并且中间的网络必须保持连接;<p >第二,在.NET中为使用异步调用,客户端程序要做不少改动。使用的方法和通常的同步调用有不小的区别;<p >第三,.NET没有不支持“自动再试(Auto-Retry")”机制。总之,如果你涉及到异步调用,COM+提供的队列组件应该是一个值得考虑的方案。<p ><center><font color="#000099"><strong>COM+队列组件的工作原理</strong></font></center><p >COM+的队列组件是COM+和Microsoft提供的消息队列服务(MSMQ)相结合的产物。本质上讲是利用了“消息中间层”来将客户端和服务器端程序异步地连接在一起。MSMQ提供了消息的中间存储和传递服务。<p >从表面上看,客户端程序创建了队列组件类的实例,并调用实例的函数。但实际上,这只是一个假象。你创建的只是一个叫做“记录器(Recorder)”的对象。它的作用是记录你的函数调用行为。而后你的函数调用记录会形成一个消息(Message)并放到了队列中,留给COM+的组件去处理。对于客户端程序来说,函数调用会马上返回,线程(THREAD)可以马上进行其它的工作。被调用的函数什么时候正真被COM+内的组件执行则不是客户端程序所需要操心的事了。MSMQ和COM+会把记录了客户端的函数通用行为的消息传达到COM+内的组件那里,COM+内的组件会执行原先客户端程序的函数调用。其过程如下所示:<p ><center><ccid_nobr><table border="1" width="502" class="content"><tr><td>客户端程序</td><td>COM+端服务器程序</td></tr><tr><td>1.创建对象</td><td>1. 创建记录器对象(Recorder Object)</td></tr><tr><td>2.调用对象函数</td><td>2.记录函数调用</td></tr><tr><td>3.释放对象</td><td>3.函数调用记录放置到一个消息中</td></tr><tr><td>4.继续其它操作</td><td>4.消息被放到消息队列中</td></tr></table></ccid_nobr></center><p >放置在队列中的消息是由COM+的消息“监听者(Listener)”来处理的。监听者随时观察着消息队列,当有消息被放置到队列中后,它读出这个消息,创建一个队列组件的实例。当这个实例生成后,它就重放原先客户端程序对组件的函数调用过程。<p >如果函数调用过程中出现了错误,消息处理将被终止,并且这个消息会自动被放置到“再试”队列去。间隔一定时间后,这个消息会再次被处理。如果在“再次处理”过程中,消息仍然出现错误,那么这个消息会被放置到再下一级的“再试”队列中去。这样的“再试”队列共有5级。如果经过这5级后消息仍没有能被成功的处理,那么这个“问题消息”将会被放到“死队列”。管理员必须亲自过问这个消息了。<p ><center><ccid_nobr><table border="1" width="502" class="content"><tr><td>再试次数</td><td>时间间隔</td></tr><tr><td>一次</td><td>1分钟</td></tr><tr><td>二次</td><td>2分钟</td></tr><tr><td>三次</td><td>4分钟</td></tr><tr><td>四次</td><td>8分钟</td></tr><tr><td>五次</td><td>16 分钟</td></tr></table></ccid_nobr></center><p >COM+处理消息的过程如下所示<p ><center><ccid_nobr><table border="1" width="502" class="content"><tr><td>COM+监听者</td><td>我们的队列组件</td></tr><tr><td>1.监视消息队列</td><td>  </td></tr><tr><td>2.从队列中读取消息</td><td>  </td></tr><tr><td>3.创建对象</td><td>1.队列组件的实例被创建</td></tr><tr><td>4.重放客户端的函数调用</td><td>2.执行函数调用</td></tr><tr><td>5.如果出现问题,消息被放置到下一级队列中去。</td><td>  </td></tr></table></ccid_nobr></center><p ><center><font color="#000099"><strong>COM+队列组件的开发</strong></font></center><p >COM+队列组件的开发和其它队列组件的开发过程相似。区别之处在于设置一些队列有关的属性。下面这个小例程是一个比较典型的示例。<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>using System;using System.Windows.Forms;using System.Reflection;using System.EnterpriseServices;// 说明COM+应用程序名字[assembly: ApplicationName(&quot;QCSimpleDemo&quot;)]// 说明COM+应用程序的激活方式。对于队列组件,需要采用&quot;服务器&quot;激活方式[assembly: ApplicationActivation(ActivationOption.Server)]// 说明COM+应用程序的的钥匙文件名[assembly: AssemblyKeyFile(&quot;QCsimpleDemo.snk&quot;)]// 要使用COM+提供的队列服务,ApplicationQueuing和QueueListenerEnabled两个// 属性必须设置为 True。这样COM+会自动创建和该COM+应用程序同名的消息队列// 并且设置在COM+应用程序启动的时候,队列监听者将被创建并监视消息队列,处理消息。 [assembly: ApplicationQueuing(Enabled=true, QueueListenerEnabled=true)]// 本例程是在WINDOWS的工作组环境下工作的。在这种网络环境下,消息认证不被支持。为了避免// 出现&quot;拒绝访问&quot;的错误,在这里&quot;访问管理&quot;被关闭,认证也没有设置。在生产实际中, MSMQ// 一般是在域(DOMAIN)下工作的,适当的访问管理和认证是必要的。[assembly: ApplicationAccessControl(Value=false,                Authentication=AuthenticationOption.None)]namespace QCSimpleDemo{    [InterfaceQueuing]    // 使用这一属性使得客户端的函数调用可以被放置到队列中去。    public interface IQComponent     {        void ComplicatedFunction(string parameter)而;    }    // 队列组件必须继承ServicedComponent类。同时它还要实现IQComponent接口    public class QComponent  : ServicedComponent, IQComponent    {        public ComplicatedFunction(string parameter)        {            //将你的复杂程序放置在这里。这里的程序只是一个示范MessageBox.Show(parameter, &quot;Message is processed!&quot;);        }    }}</ccid_code></pre></td></tr></table></ccid_nobr><p ><center><font color="#000099"><strong>COM+队列组件的客户端程序开发</strong></font></center><p >在客户端的程序开发中,只有一点要注意,那就是如何生成记录器对象。.NET的System. Runtime.InteropServices中的Marshal类提供了一个静态函数来帮助你生成记录器对象。客户端程序如下所示。<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>using System;using System.Runtime.InteropServices;using QCSimpleDemo;public class ConsoleClient{        public static void Main(String[] args)        {                if(args.Length !=1)                {                        Console.WriteLine(&quot;Usage: ConsoleClient message&quot;);                        return;                }                // 客户端程序不能直接创建队列组件的对象。它将创建一个RECORDER对象来记录客户端                // 程序的调用。创建的方法是利用.NET的Marshal类的静态函数BindToMoniker(...)                // BindToMoniker函数接收一个字符串参数,返回一个相应的对象。try                {                        IQComponent iQC = (IQComponent)                  Marshal.BindToMoniker(&quot;queue:/newCSimpleDemo.QComponent&quot;);                        iQC.ComplicatedFunction (args[0]);                        Marshal.ReleaseComObject(iQC);                }                       catch (Exception ex)                {                        Console.WriteLine(&quot;An exception was caught : &quot; + ex.Message );                }        }}</ccid_code></pre></td></tr></table></ccid_nobr><p ><center><font color="#000099"><strong>程序的编译和运行</strong></font></center><p ><b>1.服务器端队列组件</b><p ><center><ccid_nobr><table border="1" width="502" class="content"><tr><td>步骤</td><td>.NET的DOS窗口中的命令</td></tr><tr><td>1.生成钥匙文件</td><td>sn -k QCSimpleDemo.snk</td></tr><tr><td>2.编译源程序</td><td>csc /target: library /outCSimpleDemo.dll /r:System.<br/>EnterpriseServices.dll /r:System.Windows.Forms.dll  /r: <br/>System.dll QCObj.cs</td></tr><tr><td>3.注册队列组件</td><td>regsvcs QCSimpleDemo.dll</td></tr><tr><td>4.安装ASSEMBLY</td><td>gacutil -i QCSimpleDemo.dll</td></tr></table></ccid_nobr></center><p >当你完成上述步骤后,你应该在“组件服务”控制台中看到如下的COM+应用程序:<p ><center><img  src="http://www.hh010.com/upload_files/article/244/9_a0gii845407.jpg"></center><p >另外,在“计算机管理”控制台中应该看到和COM+应用程序同名的队列以及0-4级5个“再试”队列和“死消息”队列。如下图所示:<p ><center><img  src="http://www.hh010.com/upload_files/article/244/9_yl4xin45408.jpg"></center><p ><b>2.客户端程序</b><p ><center><ccid_nobr><table border="1" width="502" class="content"><tr><td>步骤</td><td>.NET的DOS窗口中的命令</td></tr><tr><td>编译源程序</td><td>csc /t:exe /out:ConsoleClient.exe /rCSimpleDemo.dll /r:System.dll ConsoleClient.cs</td></tr><tr><td>运行程序</td><td>ConsoleClient Hello_guys</td></tr></table></ccid_nobr></center><p ><b>3.启动COM+应用程序</b><p >在“组件管理”控制台中选中COM+应用程序,右键单击,在菜单中选取“启动(Start)”,之后你会看到弹出的消息框,表明COM+中的队列组件收到了客户端的消息,并做了相应的操作。<p ><center><font color="#000099"><strong>COM+队列组件的开发的注意事项</strong></font></center><p ><b>1.队列组件函数设计的问题</b><p >队列组件是以异步方式响应客户端的函数调用,客户端程序调用的函数可能在很久以后才会被COM+应用程序处理,所以客户端无法得到函数返回结果。这样,你在设计函数时要注意函数不能设有返回值(VB.NET中只能使用SUB)<p ><b>2.客户端对消息的控制</b><p >如果客户端程序是传统的COM程序,当客户程序释放了记录器(Recorder)的引用后,记录器会将客户端程序的函数调用以消息的形式放到队列里去(本质上讲是记录器的"引用计数器"(Reference Count)为0时)。在.NET里,没有“引用计数器”的概念,那么什么时候消息会被放到队列中呢?<p >.NET用的是“垃圾清洁工(Garbage Collector)”来回收和管理内存。当记录器的包装对象(Wrapper Object)被回收清理的时候,客户端的函数调用才会被放到队列中去。由于“垃圾清洁工”的工作特性使得消息被放到队列中这一动作有时间上的不确定性。为了加速这一过程,客户端程序可以调用一个函数让让包装对象马上释放记录器。这个函数是ServicedComponet的一个静态函数,叫做DisposeObject()。我们在这里可以对先前的客户程序做一些改进。其核心语句为:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>try{        IQComponent iQC = (IQComponent)         Marshal.BindToMoniker(&quot;queue:/newCSimpleDemo.QComponent&quot;);iQC.ComplicatedFunction (args[0]);        Marshal.ReleaseComObject(iQC);                ServicedComponent sc = iQC as ServicedComponent;        If(sc != null)                sc.DisposeObject(sc);}</ccid_code></pre></td></tr></table></ccid_nobr><p >本文旨在通过一个小的例程来介绍一下如何在.NET中使用COM+的队列组件服务。希望给那些频繁使用异步函数调用的程序员一些新的思路。<p >(责任编辑 <ccid_nobr>Sunny</ccid_nobr>)                                 <p align="center"></p></p>
您需要登录后才可以回帖 登录 | 论坛注册

本版积分规则

QQ|Archiver|手机版|小黑屋|sitemap|鸿鹄论坛 ( 京ICP备14027439号 )  

GMT+8, 2025-4-10 15:49 , Processed in 0.081044 second(s), 23 queries , Redis On.  

  Powered by Discuz!

  © 2001-2025 HH010.COM

快速回复 返回顶部 返回列表