您的位置:首页 > 编程语言 > C#

使用微软消息队列实现C#进程间通信

2009-04-19 14:07 1111 查看
顾名思义,微软消息队列(MSMQ)是一种给队列发送消息以便稍后进行处理的方法。消息由一个“Producer”(生产者)应用程序发送出去,再由一个“Consumer”(消费者)应用程序返回。

54ne.com

 这两个应用程序可以在同一台机器上,在整个网络中,或甚至是位于并不总是连接在一起的不同机器上。MSMQ具有故障保险特性,因为如果第一次传送失败,它会重新发送消息。这样可保证你的应用程序消息到达它们的目的地。

54com.cn

 我将应用一个叫做“TechRepublic”的队列。当你运行本文下载版本中的样本实例时,如果这个队列不存在,它会自动建立。 54com.cn

 在前面的一篇文章中,Zach Smith说明了如何使用IPC通道在同一台机器上的两个进程间通信。他将在本文中说明如何在同一台机器或网络上的应用程序间实现进程间通信。 feedom.net

访问MSMQ

中国网管联盟www.bitscn.com

 通过.NET访问队列由System.Messaging.MessageQueue对象完成。列表A说明了如何在一台名为“SRV-MESSAGING”的计算机上访问TechRepublic队列。

54ne.com

列表A

中国网管论坛bbs.bitsCN.com

MessageQueue queue = new MessageQueue("SRV-MESSAGINGTechRepublic");
feedom.net

注:要应用这个对象,你必须在你的项目中添加一个参考。

中国网管联盟www、bitsCN、com

 现在我们有了一个MessageQueue对象,这个对象为你提供与队列交互需要的所有功能。 54ne.com

 如果队列不存在,你可以调用MessageQueue对象的静态Create方法编程建立队列。列表B中的代码说明如何检查队列是否存在,建立队列或给队列添加一个参考。 中国网管联盟www、bitsCN、com

列表B 网管网bitsCN_com

MessageQueue queue = null;

中国网管联盟www、bitsCN、com

string queueName = "SRV-MESSAGINGTechRepublic";

网管网bitsCN.com

if (MessageQueue.Exists(queueName))

中国网管联盟www、bitsCN、com

queue = newMessageQueue(queueName);

网管网bitsCN.com

else

中国网管联盟www_bitscn_com

queue = MessageQueue.Create(queueName, false); 中国网管联盟www.bitscn.com

54ne.com

改写队列 54com.cn

改写队列时,用到MessageQueue.Send方法。列表C举例说明如何向TechRepublic队列发送一条消息。

中国网管论坛bbs.bitsCN.com

列表C 中国网管联盟www.bitscn.com

queue.Send("My message body", "Message Label");
中国网管联盟www.bitscn.com

 在这个例子中,我们给TechRepublic队列发送一条正文为“My message body”的消息,并对这个消息应用了一个“Message Label”标签。消息标签允许你不需阅读消息正文就可以分割消息。如果从计算机管理控制台中查看队列,还可在“队列消息”部分看到这些标签。

54ne.com

读取队列

中国网管联盟www.bitscn.com

 可以使用几种方法从队列中读取消息。最常见的情况是从队列中取出所有消息,然后一次性处理它们。这时要调用MessageQueue.GetAllMessages方法。列表D举例说明如何应用这个方法。 网管网bitsCN.com

列表D

System.Messaging.Message[] messages = queue.GetAllMessages();
foreach (System.Messaging.Message message in messages)

中国网管联盟www_bitscn_com

{ 54com.cn

//Do something with the message. 网管网bitsCN_com

}

网管联盟www.bitsCN.com

feedom.net

 你也可以用GetMessageEnumerator2方法代替上面的MessageQueue.GetAllMessages方法。虽然这两个方法的用法类似,但GetMessageEnumerator2只能向前(forward-only)。对于非常庞大的队列,则应用使用这个方法,而不是MessageQueue.GetAllMessages方法。

网管网bitsCN.com

 这是因为GetAllMessages方法领取所有消息,把它们保存在当地内存中;而GetMessageEnumerator2方法只领取当前消息在本地保存,在调用MoveNext时才领取下一条消息。列表E举例说明了GetMessageEnumerator2方法的用法。这段代码检查队列中的每一条消息,再删除它。

MessageEnumerator enumerator = queue.GetMessageEnumerator2(); 中国网管联盟www.bitscn.com

while (enumerator.MoveNext()) 中国网管联盟www、bitsCN、com

enumerator.RemoveCurrent(); 中国网管论坛bbs.bitsCN.com

中国网管联盟www_bitscn_com

 在使用GetMessageEnumerator2方法时,还要考虑另外一个问题,即你要访问队列中增加的任何新消息,即使它们是在你调用GetMessageEnumerator2后再增加的。这假定新消息被添加到队列末尾。

54com.cn

 如果你只希望返回队列中的第一条消息,你应该使用MessageQueue.Receive方法。这个方法会领取队列中的第一条消息,在这个过程中将它从队列中删除。由于消息在读取的时候被删除,你可以确保你的进程是唯一收到消息的进程。Receive方法的应用实例如列表F所示。

中国网管论坛bbs.bitsCN.com

列表F 中国网管论坛bbs.bitsCN.com

System.Messaging.Message message = queue.Receive();
54ne.com

 可以用Peek方法代替Receive方法。Peek方法像Receive方法一样领取队列中的第一条消息;但是,它在队列中保留消息备份。这允许你从队列中删除消息之前检查消息内容。Peek的语法与Receive类似。

54ne.com

列表G

中国网管联盟www、bitsCN、com

System.Messaging.Message message = queue.Peek();
中国网管联盟www_bitscn_com

发送/接收序列化对象

网管网bitsCN.com

 虽然给队列发送文本的功能非常有用,但队列还允许你发送可序列化对象。这意味着你可以建立一个自定义的.NET类,实例化它的一个实例,将其发送给队列以便其它应用程序使用。要完成这个过程,首先得使用XML Serializer序列化被发送的对象,然后对序列化对象放到消息的正文中。

54com.cn

例如,假设我们希望给TechRepublic消息队列发送以下对象(列表H):

网管联盟www.bitsCN.com

列表H 网管联盟www.bitsCN.com

[Serializable()] 54ne.com

publicclassMessageContent feedom.net

{ 中国网管论坛bbs.bitsCN.com

privateDateTime _creationDate = DateTime.Now;

网管联盟www.bitsCN.com

privatestring _messageText; 54ne.com

public MessageContent() 网管网bitsCN_com

{ 网管网bitsCN.com

}

54ne.com

public MessageContent(string messageText)

54ne.com

{

中国网管论坛bbs.bitsCN.com

_messageText = messageText; 54com.cn

}

中国网管联盟www_bitscn_com

publicstring MessageText

中国网管联盟www、bitsCN、com

{ 网管联盟www.bitsCN.com

get { return _messageText; }

中国网管联盟www、bitsCN、com

set { _messageText = value; }

feedom.net

} 54ne.com

publicDateTime CreationDate 网管联盟www.bitsCN.com

{

中国网管联盟www.bitscn.com

get { return _creationDate; } feedom.net

set { _creationDate = value; } 54com.cn

} 中国网管联盟www、bitsCN、com

} 网管网bitsCN_com

网管网bitsCN.com

 给队列发送这个对象的一个实例只需简单调用MessageQueue.Send方法,并把一个对象实例作为参数提交给这个方法。列表I说明了这种情况。

列表I

中国网管联盟www_bitscn_com

MessageContent message = newMessageContent("Hello world!"); 54ne.com

queue.Send(message, "Sample Message"); 网管联盟www.bitsCN.com

网管联盟www.bitsCN.com

 如你所见,上面的代码类似于我们前面发送正文为一个字符串的消息时使用的代码。接收一个包含序列化对象的消息更加困难一些。我们需要告诉消息它包含哪种对象。 网管网bitsCN_com

 为向消息指出它包含哪种对象,我们必须建立消息的格式化器(formatter)。给消息的Formatter属性指定一个System.Messaging.XmlMessageFormatter对象即可建立格式化器。由于我们的消息包含一个MessageContent对象,我们希望为它配置XmlMessageFormatter。

message.Formatter = new System.Messaging.XmlMessageFormatter( 54ne.com

newType[1] { typeof(MessageContent) } 中国网管联盟www_bitscn_com

); 中国网管联盟www、bitsCN、com

网管联盟www.bitsCN.com

 既然我们已经给消息指定了一个格式化器,我们可以从消息中提取MessageContent对象。但在这之前,我们需要把message.Body属性的返回值分配给一个MessageContent对象。

中国网管联盟www、bitsCN、com

列表K 网管联盟www.bitsCN.com

MessageContent content = (MessageContent)message.Body;
54com.cn

 在这个例子中,“content”变量是我们向队列发送的原始MessageContent对象的序列化版本,我们可以访问原始对象的所有属性和值。

feedom.net

设定消息优先级别 中国网管联盟www_bitscn_com

 在正常情况下,队列中的消息以先进先出的形式被访问。这表示如何你先发送消息A,再发送消息B,那么队列将首先返回消息A,然后才是消息B。在多数情况下,这样处理没有问题。但是,有时,由于一条消息比其它消息更加重要,你希望将它提到队列前面。要实现这种功能,你就需要设定消息优先级别。

中国网管联盟www_bitscn_com

 一条消息的优先级别由它的Message.Priority属性值决定。下面是这个属性的所有有效值(全部来自MessagePriority的列举类型):

中国网管联盟www、bitsCN、com

·最高(Highest)

中国网管联盟www_bitscn_com

·非常高(VeryHigh)

中国网管联盟www.bitscn.com

·高(High)

中国网管联盟www、bitsCN、com

·高于正常级别(AboveNormal)

网管联盟www.bitsCN.com

·正常(Normal) 中国网管论坛bbs.bitsCN.com

·低(Low) 网管网bitsCN.com

·非常低(VeryLow)

网管网bitsCN_com

·最低(Lowest)

中国网管论坛bbs.bitsCN.com

中国网管联盟www.bitscn.com

 消息在队列中的位置由它的优先级别决定——例如,假如队列中有四条消息,两条消息的优先级别为“正常”(Normal),另两条为“高”(High)。则队列中消息排列如下: 网管网bitsCN.com

·High Priority A——这是发送给队列的第一条“高”优先级消息。

54com.cn

·High Priority B——这是发送给队列的第二条“高”优先级消息。 网管网bitsCN_com

·Normal Priority A——这是发送队列的第一条“正常”优先级消息。

中国网管联盟www.bitscn.com

·Normal Priority B——这是发送队列的第二条“正常”优先级消息。

网管联盟www.bitsCN.com

 根据这个顺序,如果我们给队列发送另一条“最高”优先级的消息,它将位于队列的顶部。

中国网管联盟www、bitsCN、com

 如果需要使用消息优先级功能,你必须修改发送消息的代码。因为Message对象的构造器没有指定消息优先级别的功能,你必须实例化一个Message对象,并在将它发送给队列之前给它设定相应的属性。列表L中的代码说明如何设定优先级别,并给队列发送一条“最高”优先级别的消息。 feedom.net

列表L

网管联盟www.bitsCN.com

//Instantiate the queue 中国网管联盟www.bitscn.com

MessageQueue queue = newMessageQueue(queueName);

中国网管联盟www.bitscn.com

//Create a XmlSerializer for the object type we're sending.

网管联盟www.bitsCN.com

XmlSerializer serializer = new 中国网管联盟www_bitscn_com

XmlSerializer(typeof(MessageContent));

54com.cn

//Instantiate a new message.

网管网bitsCN_com

System.Messaging.Message queueMessage =

中国网管联盟www.bitscn.com

new System.Messaging.Message();

中国网管联盟www、bitsCN、com

//Set the priority to Highest.

中国网管联盟www_bitscn_com

queueMessage.Priority = MessagePriority.Highest; 网管联盟www.bitsCN.com

//Create our MessageContent object.

feedom.net

MessageContent messageContent = 中国网管联盟www、bitsCN、com

newMessageContent("Hello world - IMPORTANT!"); feedom.net

//Serialize the MessageContent object into the queueMessage. 中国网管联盟www、bitsCN、com

serializer.Serialize(queueMessage.BodyStream, messageContent); 54com.cn

//Send the message.

中国网管联盟www_bitscn_com

queue.Send(queueMessage, "HIGH PRIORITY"); 54ne.com

中国网管联盟www、bitsCN、com

 这段代码和上面代码的最明显区别在于它使用了XmlFormatter。它实际是可选的,列表L中的代码也可用列表M中的代码代替。

中国网管论坛bbs.bitsCN.com

列表M

网管网bitsCN_com

//Instantiate a new message. feedom.net

System.Messaging.Message queueMessage = 54com.cn

new System.Messaging.Message(); 中国网管论坛bbs.bitsCN.com

//Set the priority to Highest. 中国网管联盟www.bitscn.com

queueMessage.Priority = MessagePriority.Highest;

网管联盟www.bitsCN.com

//Create our MessageContent object. 中国网管联盟www.bitscn.com

MessageContent messageContent = 54ne.com

newMessageContent("Hello world - IMPORTANT!"); 中国网管论坛bbs.bitsCN.com

//Set the body as the messageContent object. 中国网管联盟www、bitsCN、com

queueMessage.Body = messageContent; 网管网bitsCN.com

//Send the message.

网管网bitsCN_com

queue.Send(queueMessage, "HIGH PRIORITY"); 54com.cn

中国网管论坛bbs.bitsCN.com

这段代码执行和列表L中的代码相同的任务,但代码更少。 网管网bitsCN.com

应用

网管网bitsCN.com

 输入消费者请求是MSMQ功能的一个简单实例。消费者提出一个请求,由一个面向消费者的应用程序将它送交给消息队列。向队列发送请求后,它会向消费者送出一个确认(acknowledgement)。

中国网管论坛bbs.bitsCN.com

 然后,一个独立的进程从队列中提取消息,并运行任何所需的业务逻辑(business logic)。完成业务逻辑后,处理系统将向另一个队列提交一个响应。接下来,面向消费者的应用程序从队列中提取这个响应,并给消费者返回一个响应。

网管网bitsCN.com

 这种类型的配置能够加快面向消费者的应用程序的速度,使其迅速做出反应,同时在一个内部系统中完成大量处理工作。这样还可以将请求处理分散到多台内部机器上,提供可扩展性。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: