.NET的消息处理
基本概念
• “消息”:是在两台计算机间传送的数据单位。消息可以非常简单,例如只包含文本字符串;也可以更复杂,可能包含嵌入对象。
• “消息队列”:是在消息的传输过程中保存消息的容器。消息队列管理器在将消息从它的源中继到它的目标时充当中间人。队列的主要目的是提供路由并保证消息的传递;如果发送消息时接收者不可用,消息队列会保留消息,直到可以成功地传递它。
• “消息队列”是Microsoft 的消息处理技术,它在任何安装了Microsoft Windows 的计算机组合中,为任何应用程序提供消息处理和消息队列功能,无论这些计算机是否在同一个网络上或者是否同时联机。
队列类型
• 用户队列
– 公共队列:在整个“消息队列”网络中复制,并且有可能由网络连接的所有站点访问。
– “专用队列”不在整个网络中发布。相反,它们仅在所驻留的本地计算机上可用。专用队列只能由知道队列的完整路径名或标签的应用程序访问。
– “管理队列”包含确认在给定“消息队列”网络中发送的消息回执的消息。
– “响应队列”包含目标应用程序接收到消息时返回给发送应用程序的响应消息。
• 系统队列
– 日记队列”可选地存储发送消息的副本和从队列中移除的消息副本。
– “死信队列”存储无法传递或已过期的消息的副本。
– “报告队列”包含指示消息到达目标所经过的路由的消息,还可以包含测试消息。每台计算机上只能有一个报告队列。
– “专用系统队列”是一系列存储系统执行消息处理操作所需的管理和通知消息的专用队列。
使用消息队列的优点
• 稳定性——组件失败对消息的影响程度远远小于组件间的直接调用,因为消息存储在队列中并一直留在那里,直到被适当地处理。
• 消息优先级——更紧急或更重要的消息可在相对不重要的消息之前接收,因此可以为关键的应用程序保证足够的响应时间。
• 脱机能力——发送消息时,它们可被发送到临时队列中并一直留在那里,直到被成功地传递。
• 安全性——MessageQueue 组件基于的消息队列技术使用Windows 安全来保护访问控制,提供审核,并对组件发送和接收的消息进行加密和验证
MSMQ简介
Microsoft Message Queue
• 利用Microsoft Windows“消息队列”,应用程序开发人员可以通过发送和接收消息方便地与应用程序进行快速可靠的通讯。
• MSMQ与XML Web Services和.Net Remoting一样,是一种分布式开发技术。但是在使用XML Web Services或.Net Remoting组件时,Client端需要和Server端实时交换信息,Server需要保持联机。MSMQ则可以在Server离线的情况下工作,将Message临时保存在Client端的消息队列中,以后联机时再发送到Server端处理。
• 采用MSMQ带来的好处是:由于是异步通信,无论是发送方还是接收方都不用等待对方返回成功消息,就可以执行余下的代码,因而大大地提高了事物处理的能力
MSMQ与Email
• MSMQ(微软消息队列)是Windows操作系统中消息应用程序的基础,是用于创建分布式、松散连接的消息通讯应用程序的开发工具。
• 消息队列和电子邮件有着很多相似处,他们都包含多个属性,用于保存消息,消息类型中都指出发送者和接收者的地址;
• 然而他们的用处却有着很大的区别:消息队列的发送者和接收者是应用程序,而电子邮件的发送者和接收者通常是人。
MessageQueue编程结构
• MessageQueue位于名称空间System.Messaging。
• MessageQueue 基类中的主要编程元素:
– 使用Create 方法创建使用指定路径的新消息队列,并使用Delete 方法删除现有队列。
– 使用Exists 方法查看特定消息队列是否存在。
– 使用GetPublicQueues 方法在“消息队列”网络中定位消息队列。
– 使用Peek 或BeginPeek 方法查看某个特定队列中的消息但不从该队列中移除消息。
– 使用Receive 和BeginReceive 方法检索指定队列最前面的消息并将其从该队列中移除。
– 使用Send 方法将消息发送到指定队列。
在.Net环境下编写Message Queue程序
1)先安装Message Queuing Services
– 通过控制面板,“添加或删除程序” – “添加/删除Windows 组件”步骤安装MSMQ。
– MSMQ可以安装为工作组模式或域模式。
2)配置MSMQ
– 打开“计算机管理” – 消息队列,在“专用队列”下创建指定的队列
3)编写代码
– MessageQueue 类提供对“消息队列”队列的引用。
【代码分析】
下面是通过手工方式添加队列:
在消息队列的“专用队列”下添加队列“MSMQDemo”,再通过两个按钮实现消息的发送和接收。
在逐步调试过程中,我们可以Send(message)后,MSMQDemo会产生一个字节点:消息,当Receive()后,该节点会删除,这也反映了消息队列起到一个管理消息的作用。
protected void btnSend_Click(object sender, System.EventArgs e)
{
// Open queue
MessageQueue queue = new MessageQueue(".//Private$//MSMQDemo");
// Create message
Message message = new Message();
message.Body = tbSend.Text.Trim();
message.Formatter = new XmlMessageFormatter(new Type[] {typeof(string)});
// Put message into queue
queue.Send(message);
}
protected void btnReceive_Click(object sender, System.EventArgs e)
{
// Open queue
MessageQueue queue = new MessageQueue(".//Private$//MSMQDemo");
// Receive message, 同步的Receive方法阻塞当前执行线程,直到一个message可以得到
Message message = queue.Receive();
message.Formatter = new XmlMessageFormatter(new Type[] {typeof(string)});
tbReceive.Text = message.Body.ToString();
}
创建、删除和管理队列
• 创建队列:
– 创建队列以提供消息处理组件可与之交互的资源。
– 有两种创建队列的方法:使用“服务器资源管理器”窗口(如上面所示)或使用代码中的Create 构造函数。
– 以编程方式创建公共队列:
MessageQueue.Create(@"myMachine/MyQueue");
– 以编程方式创建专用队列:
MessageQueue.Create(@"./Private$/MyPrivateQueue");
• 删除队列
– MessageQueue.Delete (@"myMachine/MyQueue");
• 清除队列内容
– 可使用Purge 方法清除在“消息队列”系统中具有其访问权限的任何队列的内容。
– MessageQueue MessageQueue1 = new MessageQueue();
– MessageQueue1.Path=(@"myMachine/MyQueue");//指明需要删除的队列
– MessageQueue1.Purge();
队列引用
• 通过路径——唯一标识计算机和感兴趣的队列的名称的路径。
– 公共队列MachineName/QueueName
– 专用队列MachineName/Private$/QueueName
• 通过格式名——队列的唯一标识符,创建队列时由MSMQ 生成,或者后来由应用程序生成。
– 公共队列
FORMATNAME:PUBLIC=QueueGUID
– 专用队列
FORMATNAME:PRIVATE=MachineGUID/QueueNumber
• 通过标签——可能不唯一的描述性队列名称,创建队列时由队列管理员指派。
MessageQueue1.Path= "LABEL:MyQueue";
【代码分析】
注解:我们可以在“服务器资源管理器”中直接创建一个消息队列helpRequestQueue,然后直接把“消息队列helpRequestQueue”直接拖进代码区,我们就轻松使用消息队列helpRequestQueue了。下面的功能是完成消息的添加和显示功能。
protected System.Messaging.MessageQueue helpRequestQueue;
protected void Page_Load(object sender, System.EventArgs e)
{
// 在此处放置用户代码以初始化页面
}
protected void btnSend_Click(object sender, System.EventArgs e)
{
Message theMessage = new Message(txtMessage.Text);
theMessage.Label = txtName.Text;
if (highPriority.Checked)
theMessage.Priority = MessagePriority.Highest;
else
theMessage.Priority = MessagePriority.Normal;
helpRequestQueue.Send(theMessage);
DisplayMessages();
}
private void DisplayMessages()
{
DataTable messageTable = new DataTable();
messageTable.Columns.Add("Name");
messageTable.Columns.Add("Message");
messageTable.Columns.Add("Priority");
Message[] messages;
messages = helpRequestQueue.GetAllMessages();
XmlMessageFormatter stringFormatter;
stringFormatter = new XmlMessageFormatter(new string[] {"System.String"});
for (int index = 0; index < messages.Length; index++)
{
messages[index].Formatter = stringFormatter;
messageTable.Rows.Add(new string[] {
messages[index].Label,
messages[index].Body.ToString(),
messages[index].Priority.ToString() });
}
dgMessage.DataSource = messageTable;
dgMessage.DataBind();
}
protected void btnFresh_Click(object sender, System.EventArgs e)
{
DisplayMessages();
}
protected void btnDele_Click(object sender, System.EventArgs e)
{
helpRequestQueue.Purge();
DisplayMessages();
}
发送和序列化消息
发送消息
• MSMQ消息队列中定义的消息由一个主体(body)和若干属性构成。消息的主体可以由文本、二进制构成,根据需要还可以被加密。在MSMQ中消息的大小不能够超过4MB。
• 简单消息的发送:步骤大致为
– 建立与要发送消息的队列的连接
– 指定你要发送的消息的格式
– 提供要发送的数据
– 使用Send方法将消息发送出去
• 发送复杂消息的步骤大致为:
– 创建一个MessageQueue的实例,然后对它的path属性设置,以指明操作的消息队列。
– 创建一个消息对象实例。
– 设置消息的主体(body), 然后修改不希望使用缺省值的属性。
– 同样使用send方法把消息对象发送至相应的队列中。
接收和读取消息
接收消息
• 收消息由两种方式:
– 通过Receive方法接收消息同时永久性地从队列中删除消息;
– 通过Peek方法从队列中取出消息而不从队列中移除该消息。
• 消息的接收分为两种,即同步和异步
– 同步方式,我们使用receive方法。当调用receive方法时,它会从指定的消息队列中选取出第一个适合要求的消息对象返回给用户,然后把它从消息队列中删除。
– 异步接收方式,是指接收消息时,不必理会调用的消息接收方法是否成功,方法将立即返回,并继续进行程序处理。
读取消息
• 在应用程序能够阅读的消息和消息队列中的消息格式不同,因而应用程序发送出去的消息经过序列化以后才发送给了消息队列,这一过程由系统自动完成了。
• 消息的序列化可以通过.NET Framework 附带的三个预定义的格式化程序来完成:
– XMLMessageFormatter 对象( MessageQueue 组件的默认格式化程序设置)、
– BinaryMessageFormatter 对象、
– ActiveXMessageFormatter 对象。
由于后两者格式化后的消息通常不能为人阅读,所以我们经常用到的是XMLMessageFormatter对象。
• 使用XMLMessageFormatter对象格式化消息的代码如下所示:
– string[] types = { "System.String" };
– ((XmlMessageFormatter)mq.Formatter).TargetTypeNames = types;
– Message m=mq.Receive(new TimeSpan(0,0,3));
事务性消息处理
• 利用事务性处理,您可以确保事务中的消息按照顺序传送,只传送一次,并且从目的队列成功地被检索。
• 内部事务是通过创建MessageQueueTransaction类的实例并将其关联到MessageQueue 组件的实例来执行的。内部事务是最简单的事务类型
• 部事务的编程模型非常简单——调用MessageQueueTransaction 类的Begin 方法,并将此类的实例传递到收发方法。然后,调用Commit 以将事务的更改保存到目的队列