“消息队列”学习文摘及Demo演示
一、相关背景知识
作用:利用 Microsoft Windows“消息队列”,应用程序开发人员可以通过发送和接收消息方便地与应用程序进行快速可靠的通信。
相关概念:
A、“消息”是在两台计算机间传送的数据单位。消息可以非常简单,例如只包含文本字符串;也可以更复杂,可能包含嵌入对象。消息被发送到队列中。
B、“消息队列”是在消息的传输过程中保存消息的容器。消息队列管理器在将消息从它的源中继到它的目标时充当中间人。队列的主要目的是提供路由并保证消息的传递;如果发送消息时接收者不可用,消息队列会保留消息,直到可以成功地传递它。
C、“消息队列”是 Microsoft 的消息处理技术,它在任何安装了 Microsoft Windows 的计算机组合中,为任何应用程序提供消息处理和消息队列功能,无论这些计算机是否在同一个网络上或者是否同时联机。
D、“消息队列网络”是能够相互间来回发送消息的任何一组计算机。网络中的不同计算机在确保消息顺利处理的过程中扮演不同的角色。它们中有些提供路由信息以确定如何发送消息,有些保存整个网络的重要信息,而有些只是发送和接收消息。
队列类型:
有两种主要的队列类型:由您或网络中的其他用户创建的队列和系统队列。用户创建的队列可能是以下任何一种队列:
A、“公共队列”在整个“消息队列”网络中复制,并且有可能由网络连接的所有站点访问。
B、“专用队列”不在整个网络中发布。相反,它们仅在所驻留的本地计算机上可用。专用队列只能由知道队列的完整路径名或标签的应用程序访问。
C、“管理队列”包含确认在给定“消息队列”网络中发送的消息回执的消息。指定希望 MessageQueue 组件使用的管理队列(如果有的话)。
D、“响应队列”包含目标应用程序接收到消息时返回给发送应用程序的响应消息。指定希望 MessageQueue 组件使用的响应队列(如果有的话)。
系统生成的队列一般分为以下几类:
A、“日记队列”可选地存储发送消息的副本和从队列中移除的消息副本。每个“消息队列”客户端上的单个日记队列存储从该计算机发送的消息副本。在服务器上为每个队列创建了一个单独的日记队列。此日记跟踪从该队列中移除的消息。
B、“死信队列”存储无法传递或已过期的消息的副本。如果过期或无法传递的消息是事务性消息,则被存储在一种特殊的死信队列中,称为“事务性死信队列”。死信存储在过期消息所在的计算机上。有关超时期限和过期消息的更多信息,请参见默认消息属性。
C、“报告队列”包含指示消息到达目标所经过的路由的消息,还可以包含测试消息。每台计算机上只能有一个报告队列。
D、“专用系统队列”是一系列存储系统执行消息处理操作所需的管理和通知消息的专用队列。
在应用程序中进行的大多数工作都涉及访问公共队列及其消息。但是,根据应用程序的日记记录、确认和其他特殊处理需要,在日常操作中很可能要使用几种不同的系统队列。
同步和异步通信:
队列通信天生就是异步的,因为将消息发送到队列和从队列中接收消息是在不同的进程中完成的。另外,可以异步执行接收操作,因为要接收消息的人可以对任何给定的队列调用 BeginReceive 方法,然后立即继续其他任务而不用等待答复。这与人们所了解的“同步通信”截然不同。
在同步通信中,请求的发送方在执行其他任务前,必须等待来自预定接收方的响应。发送方等待的时间完全取决于接收方处理请求和发送响应所用的时间。
注意 消息的同步或异步“检索”是一个独立于消息发送的问题。消息总是异步“发送”。
有关消息队列异步处理的更多信息,请参见异步消息处理。
消息安全性:
可用如下方式帮助保护发送和接收的消息内容:
可使用身份验证验证应用程序接收的消息的来源。
可使用加密确保未经授权的人无法读取或使用您的消息。
可使用访问控制权限,用基于 ACL 的安全性限制用户和用代码访问安全性限制代码向计算机上的特定队列发送消息或从中读取消息。
可使用审核记录尝试访问“消息队列”对象的用户,尝试的操作类型,以及该访问成功还是失败。
有关更多信息,请参见消息队列安全性。
安装“消息队列”:
如果要在代码或者服务器资源管理器中使用消息队列,必须在开发计算机上安装“消息队列”。在 Windows 2000 或 Windows NT 文档中可以找到有关如何安装“消息队列”的说明。在安装时,必须指定正在创建的新主“消息队列”服务器所起的作用:是充当新网络的集线器、现有网络的独立客户端还是现有网络的依赖客户端。多数情况下,不用创建新的网络,而是可以加入公司或工作组的现有“消息队列”网络。
二、消息队列安装配置
1、 安装消息队列服务器
通过“控制面板”,“添加/删除程序”,“添加/删除Windows组件”的步骤来安装MSMQ。MSMQ可以安装为工作组模式或域模式。如果安装程序没有找到一台运行提供目录服务的消息队列的服务器,则只可以安装为工作组模式,此计算机上的“消息队列”只支持创建专用队列和创建与其他运行“消息队列”的计算机的直接连接。
Domo过程:我的服务器的操作系统为win2000server,因为安装了Exchange,是域控制器,被默认安装为域模式。另一台客户机的操作系统是win2003server,原来设定在workgroup工作组下,将其加入服务器的域中。
通过“我的电脑”,“管理”,“服务与应用程序”,可以查看到安装的消息队列服务。
点击工具栏最右端的“帮助”图标,可以查找到关于“消息队列”管理和使用的文档。
2、创建消息队列
创建消息队列的方法有多种:
A、在上图的“公用队列”或“专用队列”上,新建队列。
B、在Microsoft Visual Studio.Net中建队列。
(1)在视图中调出服务器资源管理器,然后将其打开。
(2)展开“服务器”节点。
(3)展开本地服务器的节点。按计算机名识别本地服务器的节点。
(4)展开“消息队列”节点。右击“专用队列”或“公用队列”并从快捷菜单中选择“创建队列”。
C、用编程的方式创建队列。
注意:右击项目并选择“添加引用”。将引用添加到 System.Messaging.dll。如果使用MessageQueue组件,开发环境将自动添加引用。
(1)公用队列
使用 Create 方法在计算机上创建公共队列。您的代码应类似于:
' Visual Basic
System.Messaging.MessageQueue.Create("myMachine\MyQueue")
// C#
System.Messaging.MessageQueue.Create(@"myMachine\MyQueue");
可使用 Create 方法的重载来指示您要创建事务性队列。还可以在路径中使用句点 ( . ) 来表示本地计算机。
(2)专用队列
使用 Create 方法,指定所需的语法 Private$,以便在计算机上创建专用队列。您的代码应类似于:
' Visual Basic
System.Messaging.MessageQueue.Create(".\Private$\MyPrivateQueue")
// C#
System.Messaging.MessageQueue.Create(@".\Private$\MyPrivateQueue");
Demo过程:使用方法A或B,创建一专用队列,将其命名为:“request_q”。不要选中“使队列具有事务性”。
三、Demo演练
1、 客户端
A、为您的消息队列添加 MessageQueue 组件
将“request_q”队列从服务器资源管理器拖到窗体上。为 HelpRequest 队列配置的新 MessageQueue 组件被添加到项目中。 MessageQueue 组件用于以编程的方式访问在上一节中创建的“request_q”队列中所包含的消息。将 MessageQueue 组件的 Name 属性设置为 helpRequestQueue。在“属性”窗口中,展开“MessageReadPropertyFilter”节点。将 Priority 的值设置为 true。这样,当从队列中检索消息时,同时会检索消息的优先级。
在下一个步骤中创建的用户界面允许用户输入一些帮助请求文本并设置消息的优先级。用户单击“发送”按钮将请求发送到队列。一个数据网格显示队列的内容。用户界面还包含用队列的当前内容更新网格的按钮和清除队列的按钮。
B、创建用户界面
在工具箱的“Windows 窗体”选项卡中,将以下控件添加到 Form1:
两个标签
两个文本框
三个按钮
一个复选框
一个数据网格
设置以下控件属性:
控件 |
属性 |
新值 |
Label1 |
Text |
名称 |
Label2 |
Text |
消息 |
TextBox1 |
Name |
txtName |
|
Text |
(空白) |
TextBox2 |
Name |
txtMessage |
|
Text |
(空白) |
|
Multiline |
true |
Button1 |
Name |
sendMessage |
|
Text |
发送消息 |
Button2 |
Name |
refreshMessages |
|
Text |
刷新消息列表 |
Button3 |
Name |
purgeMessages |
|
Text |
清除消息列表 |
CheckBox1 |
Name |
highPriority |
|
Text |
高优先级 |
DataGrid1 |
Name |
messageGrid |
C、向队列发送消息
在设计器中,双击“sendMessage”按钮以在代码编辑器中创建 Click 事件处理程序。在方法中添加代码以创建新的 Message 实例并将其发送到队列,并更新消息显示。下一步中将编写 DisplayMessages 方法。
//C#
private void sendMessage_Click(object sender, System.EventArgs e)
{
System.Messaging.Message theMessage = new System.Messaging.Message(txtMessage.Text);
theMessage.Label = txtName.Text;
if (highPriority.Checked)
theMessage.Priority = System.Messaging.MessagePriority.Highest;
else
theMessage.Priority = System.Messaging.MessagePriority.Normal;
helpRequestQueue.Send(theMessage);
DisplayMessages();
}
添加方法以显示 DataGrid 控件中队列的内容。该方法使用 MessageQueue.GetAllMessages 方法来检索队列中的所有消息。选定的队列属性被添加到 DataTable 对象,该对象用作 DataGrid 控件的数据源。若要检索消息的文本,您需要为消息创建格式化程序。
private void DisplayMessages()
{
DataTable messageTable = new DataTable();
messageTable.Columns.Add("Name");
messageTable.Columns.Add("Message");
messageTable.Columns.Add("Priority");
System.Messaging.Message[] messages;
messages = helpRequestQueue.GetAllMessages();
System.Messaging.XmlMessageFormatter stringFormatter;
stringFormatter = new System.Messaging.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() });
}
messageGrid.DataSource = messageTable;
}
D、显示队列的内容
在设计器中,双击“refreshMessage”按钮以在代码编辑器中创建 Click 事件处理程序。
在 Click 事件处理程序中,调用 DisplayMessages 方法。
private void refreshMessages_Click(object sender, System.EventArgs e)
{
DisplayMessages();
}
E、清除队列的内容
在设计器中,双击“purgeMessage”按钮以在代码编辑器中创建 Click 事件处理程序。
调用 helpRequestQueue 的 Purge 方法,然后刷新 DataGrid 控件的内容。
private void purgeMessages_Click(object sender, System.EventArgs e)
{
helpRequestQueue.Purge();
DisplayMessages();
}
F、测试应用程序
按 F5 键运行该应用程序。
输入名称和短消息。
单击“发送消息”将消息发送到队列并更新显示。
单击“清除消息”以删除队列中的所有消息。消息列表将为空。
G、在客户机上测试
将生成的客户端程序拷贝到客户机上,同F测试。在实际过程中,提示没有读取队列权限异常。通过“我的电脑”,“管理”,“服务与应用程序”,依次展开“消息队列”,“专用队列”节点,右键点击“request_q”在弹出的菜单中选择属性。选择“安全”选项框,将ANONYMOUS LOGON的权限设置为完全控制。(正确的做法也许应该是点击“添加”按钮,然后选择客户端计算机名称,则其在该队列中拥有写消息的权限。未尝试)
2、关于队列引用
如果创建 MessageQueue 组件的实例,则需要指示希望组件与之通信的队列。有三种方法可以在代码中引用一个队列:
通过路径 — 唯一标识计算机和感兴趣的队列的名称的路径。
通过格式名 — 队列的唯一标识符,创建队列时由 MSMQ 生成,或者后来由应用程序生成。
通过标签 — 可能不唯一的描述性队列名称,创建队列时由队列管理员指派。
当创建新队列而不是 MessageQueue 组件的新实例时,必须使用路径。创建了队列后,由“消息队列”系统指派格式名和标签。但是,当在代码中或从工具箱引用现有队列时,可以选择自己要使用的引用方法。所选择的任何方法均存储在组件的 Path 属性中。
在上面的程序中,查看helpRequestQueue组件属性path,可以得到path的值为:“FormatName:DIRECT=OS:fireball\private$\request_q”。这里是通过格式名来引用队列。
3、服务器端
这里,建立的是控制台程序,主要用到的是BeginReceive()方法,具体用法说明可以参考MSDN。代码如下:
using System;
using System.Messaging;
using System.Threading;
namespace ConsoleApplication1
{
public class MQReceive
{
public static void Main(String[] args)
{
string mqPath = "FormatName:DIRECT=OS:fireball\\private$\\request_q";
MessageQueue mq = new MessageQueue(mqPath);
((XmlMessageFormatter)mq.Formatter).TargetTypeNames = new string[]{"System.String,mscorlib"};
mq.ReceiveCompleted += new ReceiveCompletedEventHandler(OnReceiveCompleted);
mq.BeginReceive();
Console.WriteLine("按 Enter 键退出该示例");
Console.ReadLine();
}
public static void OnReceiveCompleted(Object source, ReceiveCompletedEventArgs asyncResult)
{
MessageQueue mq = (MessageQueue)source;
Message m = mq.EndReceive(asyncResult.AsyncResult);
m.Formatter = new XmlMessageFormatter(new string[]{"System.String, mscorlib"});
Console.WriteLine("消息:{0}", (string)m.Body);
mq.BeginReceive();
}
}
}
测试效果如图(为方便截图,在服务器端与客户端程序都在本机)
参考资料:
MSDN
http://chs.gotdotnet.com/quickstart/default.aspx