远程MSMQ
简介
MSMQ(微软消息队列)是Windows操 作系统中消息应用程序的基础,是用于创建分布式、松散连接的消息通讯应用程序的开发工具。消息队列和电子邮件有着很多相似处,他们都包含多个属性,用于保存消息,消息类型中都指出发送者和接收者的地址;然而他们的用处却有着很大的区别:消息队列的发送者和接收者是应用程序,而电子邮件的发送者和接收者通常 是人。
如同电子邮件一样,消息队列的发送和接收也不需要发送者和接收者同时在场,可以存储在消息队列或是邮件服务器中。
开发MSMQ应用程序并不是十分困难的事情。不过要使用MSMQ开发你的消息处理程序,必须在开发系统和使用程序的主机上安装消息队列。消息队列的安装属于Windows组件的安装,和一般的组件安装方法类似。安装好消息队列后,就可以开发你自己的消息处理程序了。不过有一点需要注意,如果你的计算机处于工作组中,而不是某个域中,可能你的公用队列不能使用,不过这并不影响你的程序开发。
消息处理程序不外乎消息的发送和接收,然而要收发消息,还必须引用一个队列,通常我们引用公用队列和专用队列,这两个队列都存放用户生成的消息。引用队列后,就可以发送、接收和阅读消息了。消息接收服务位于System.Messaging中,如果你找不到这一命名空间,你必须手动添加。点击[项目]中的[添加引用],按下浏览按钮,找到System.Messaging.dll文件,添加进来即可。
引用队列
引用队列有三种方法,通过路径、格式名和标签引用队列,这里我只介绍最简单和最常用的方法:通过路径应用队列。队列路径的形式为 machinename\queuename。指向队列的路径总是唯一的。下表列出用于每种类型的队列的路径信息:
队列类型 |
路径中使用的语法 |
公共队列 |
MachineName\QueueName |
专用队列 |
MachineName\Private$\QueueName |
日志队列 |
MachineName\QueueName\Journal$ |
如果是发送到本机上,还可以使用”.”代表本机名称。具体的引用方法通过Path属性来进行,也可以在初始化消息队列时进行。
如果在初始化时引用消息队列,那么消息队列必须存在于系统中,否则会产生中断。往系统中添加队列十分的简单,打开[控制面板]中的[计算机管理],展开[服务和应用程序],找到并展开[消息队列](如果找不到,说明你还没有安装消息队列),右击希望添加的消息队列的类别,选择新建队列即可。当然,在程序中也可以实现消息队列的创建,下文会有相应的说明。在初始化时引用消息队列的代码很简单,如下所示:
MessageQueue Mq=new MessageQueue(“.\\private$\\jiang”);
通过Path属性引用消息队列的代码也十分简单:
MessageQueue Mq=new MessageQueue();
Mq.Path=”.\\private$\\jiang”;
使用 Create 方法可以在计算机上创建队列:
System.Messaging.MessageQueue.Create(@".\private$\jiang");
发送消息
队列引用过后,就可以发送消息了。消息的发送可以分为简单消息和复杂消息,简单消息类型就是常用的数据类型,例如整型、字符串等数据;复杂消息的数据类型通常对应于系统中的复杂数据类型,例如结构,对象等等。
简单消息的发送示例如下:
Mq.Send(1000); //发送整型数据
Mq.Send(“This is a test message!”); //发送字符串
复杂消息的发送和简单消息的发送大同小异,只是发送时,通常不是直接给出发送的消息内容,而是代表发送消息内容的变量。下面的代码分别通过消息变量和复杂数据类型变量发送一条复杂消息。
//下面的代码中发送的消息由消息变量表示
Message Msg;
Msg=new Message(“A Complex Message!”);
Msg.Label=”This is the label”;
Msg.Priority=MessagePriority.High;
Mq.Send(Msg);
//下面的代码中发送的消息由复杂数据类型变量表示,Customer为自定义的一个类
Customer customer = new Customer();
customer.LastName = "Copernicus";
customer.FirstName = "Nicolaus";
Mq.Send(customer);
接收消息
接收消息相比发送消息要复杂一点。接收消息由两种方式:通过Receive方法接收消息同时永久性地从队列中删除消息;通过Peek方法从队列中取出消息而不从队列中移除该消息。如果知道消息的标识符(ID),还可以通过ReceiveById方法和PeekById方法完成相应的操作。
接收消息的代码很简单:
Mq.Receive(); //或Mq.ReceiveById(ID);
Mq.Peek(); // 或Mq.PeekById(ID);
阅读消息
接 收到的消息只有能够读出来才是有用的消息,因此接收到消息以后还必须能读出消息,而读出消息算是最复杂的一部操作了。在应用程序能够阅读的消息和消息队列中的消息格式不同,因而应用程序发送出去的消息经过序列化以后才发送给了消息队列,这一过程由系统自动完成了,程序开发人员不必为此编写代码,然而在接收 到消息后就面临着消息序列化的问题。
消息的序列化可以通过Visual Studio 和 .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));
将接收到的消息传送给消息变量以后,通过消息变量m的Body属性就可以读出消息了:
MessageBox.Show((string)m.Body);
关闭消息队列
消息队列的关闭很简单,和其他对象一样,通过Close函数就可以实现了:
Mq.Close();
到此为止,MSMQ应用程序的基础知识就介绍完全了,但是开发出一个功能强大的MSMQ应用程序显然不是这么简单,要了解更详细的资料可以参考MSDN和Windows操作系统中关于消息队列的帮助内容。
我们先简单的了解一下什么是消息队列(MSMQ)?消息队列是 Windows 2000(NT也有MSMQ,WIN95/98/me/xp不含消息队列服务但是支持客户端的运行)操作系统中通讯的基础,也是用于创建分布式、松散连接通讯应用程序的工具。这些应用程序可以通过不同种类的网络进行通讯,也可以与脱机的计算机通讯。消息队列分为用户创建队列和系统队列,用户队列分为:
· "公共队列"在整个可传递消息的"消息队列"网络中复制并传输,并且有可能由网络连接的所有站点访问。
· "专用队列"不在整个网络中发布。相反,它们仅在所驻留的本地计算机上可用。专用队列只能由知道队列的完整路径名或标签的应用程序访问。
· "管理队列"包含确认在给定"消息队列"网络中发送的消息回执的消息。指定希望 MessageQueue 组件使用的管理队列
· "响应队列"包含目标应用程序接收到消息时返回给发送应用程序的响应消息。指定希望 MessageQueue 组件使用的响应队列。
系统队列分为:
· "日记队列"可选地存储发送消息的副本和从队列中移除的消息副本。
· "死信队列"存储无法传递或已过期的消息的副本。
· "专用系统队列"是一系列存储系统执行消息处理操作所需的管理和通知消息的专用队列。
现在大家对消息队列有了简单的了解后,就该进入主题了。要使用msmq进行软件开发需要安装msmq。安装完后就该进入实际的开发阶段。先打开 VS.net ide中的"服务起资源管理器"展开你想建立消息队列的计算机名,再展开"消息队列"右击它在弹出菜单中选择"新建"建立一个新的消息队列,并为它指定一 个名字,这个名字可以随意。也可以通过编程来完成,代码如下:
system.Messaging.MessageQueue.Create(".\Private$\MyPrivateQueue")'建立专用队列
System.Messaging.MessageQueue.Create("myMachine\MyQueue")'建立公共队列
其实我认为使用那中方法并不重要,重要的是搞清楚专用队列和公共队列的差别(其他队列不是必须的)。在本例中是通过"服务器资源管理器"分别在服务器上建立了专用队列和公共队列。
程序功能:本程序分为两部分包括服务器程序(安装在SQL Server服务器上)和客户端程序,客户端的作用是用来编写t-sql语句并将t-sql语句放在消息中,并将消息发送到sql server服务器上的消息队列中去。服务器程序检查指定的消息队列当发现有新消息到达时,就开始执行消息中的内容,由于消息中的内容是t-sql语句所 以服务器端实际上是执行对数据库的操作。
客户端程序:
Dim tM As New System.Messaging.MessageQueue()
tM.Path = ".\Private$\jk" '"FORMATNAME:PUBLIC=3d3dc813-c555-4fd3-8ce0-79d5b45e0d75"'与指定计算机中的消息队列建立连接,
Dim newMessage As New System.Messaging.Message(TextBox1.Text)'接受文本筐的t-sql语句
newMessage.Label = "This is the label"'消息名字,
tM.Send(newMessage)'发送消息
End Sub
服务端程序:
Dim NewQueue As New System.Messaging.MessageQueue(".\Private$\jk")'"FORMATNAME:PUBLIC=3d3dc813 -c555-4fd3-8ce0-79d5b45e0d75"'与指定计算机中的消息队列建立连接,
Dim m As System.Messaging.Message
'查看消息队列中的消息
m = NewQueue.Receive
m.Formatter = New System.Messaging.XMLMessageFormatter(New String() {"System.String,mscorlib"})
Dim st As String
st = m.Body'消息队列中消息的消息内容。既sql语句
Dim con As New OleDb.OleDbConnection("输入自己的数据库连接字符串")
con.Open()
Dim com As New OleDb.OleDbCommand(st, con)'执行消息中的sql语句
com.ExecuteNonQuery()
con.Close()
End Sub
我为什么要使用消息队列来处理数据库的操作这个问题我一直没回答,现在我就来回答这个问题。在本程序中你会发现在sub client()中我并没连接数据库和请求数据,而是通过发消息来操作数据库的,这个好处是节省了两部分时间:
1、对数据库连解请求数据的时间。
2、从数据库返回数据的时间。
在很多情况下其实我们并不需要看见具体的数据就知道该怎么修改数据库中的数据。例如要删除张三的记录,就可以将一条简单的删除语句放入消息中,发给服务器让服务器程序去处理对数据的更改。
此外消息队列的另一个主要用途也就是当前ERP软件中必不可少的,就是在断开连接时保存信息,当连接恢复时发送消息。消息在如下两种情况中无法 迅速地传递到它们的队列:当队列驻留的计算机无法工作时,或当路由消息所需的域控制器无法工作时。"消息队列"可让您应对这些情况,使得在从网络上断开连 接或必要的计算机或控制器无法工作时,仍可以继续发送消息。在这些情形下,消息暂时存储在本地计算机或传递路由上的某个计算机的队列中,直到完成传递所需的资源重新联机。
例如,假设有一个记录所有在出差的销售人员发送的订单的中央队列。这些销售人员每天的大部分时间都以断开连接的方式工作,记录来自客户站点的订单信息,并且每天拨号连接一次,将所有这些信息传输到中央队列中。因为消息在发送方断开连接时仍可发送到队列,所以销售人员可以在记录客户信息时立即发送 他们的消息,但系统会缓存这些消息直到晚间进行拨号连接为止。
在断开连接时要怎么保存消息呢?向断开连接的队列发送消息同向可用队列发送消息的过程几乎完全相同。当要向其发送的队列不可用时,不必进行任何特殊的配置以使组件将消息存储在临时队列中。在client代码的tM.Path = ".\Private$\jk"后面有一条注释语句,其实这条语句就是实现向断开连接的队列发送消息的功能。只要将tM.Path = ".\Private$\jk"这条语句换成tM.Path = "FORMATNAME:PUBLIC=3d3dc813-c555-4fd3-8ce0-79d5b45e0d75"其中PUBLIC后面的数字是要发 送到计算机的guid数字。这个数字可以打开那台计算机的消息队列的属性看见。使用这种方法就可以在断开连接的情况下保证对服务器的操作是有效。现在运行这个程序后,打开win2000中的"开始"-》"程序"-》"管理工具"-》"计算机管理"。在"计算机管理"窗口中展开"服务和应用程序"-》"消息 队列"-》"传出队列",你将在右边的窗口中看见你建立的消息。(如果你使用tM.Path = ".\Private$\jk"语句,在"计算机管理"窗口中展开"服务和应用程序"-》"消息队列"-》"专用队列"可以看见你建立的队列。)
其实消息队列的编程并不复杂,但它在网络环境的程序开发中是非常有用的,可以简化大量的开发过程和节省开发时间。
其实消息队列的编程有很大的灵活性,几乎可以解决网络编程的大部分问题。比如聊天程序,远程控制程序。
本文针对消息队列做了一个简单的介绍,并举了一个例来说明怎么在.net下使用消息编程,达到快速高效稳定的对数据库进行操作。最后补充要说的 是在internet中也一样可以使用消息队列,只需要将tM.Path = "FORMATNAME:PUBLIC=3d3dc813-c555-4fd3-8ce0-79d5b45e0d75语句后面的数字变成消息队列所在服务器的数字就可以了。但是要提醒大家的是使用消息在传输时将占有大量的带宽,所以在不是必须的时候,internet下的编程不要使用消息。
远程MSMQ
1.消息发送
{
System.Messaging.MessageQueue Queue;
Queue = new System.Messaging.MessageQueue(@"FormatName:DIRECT=TCP:172.26.230.2\ptest1");
System.Messaging.Message Msg;
Msg = new System.Messaging.Message();
Msg.Formatter =new System.Messaging.BinaryMessageFormatter();
Msg.Body="Testing 3 times";
Queue.Send(Msg);
}
catch(Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.ToString());
}
2.消息接收
{
System.Messaging.MessageQueue mq = new System.Messaging.MessageQueue(@"FormatName:DIRECT=tcp:172.26.230.2\ptest1");
// Set the queue'ss formatter to decode Point objects
mq.Formatter = new System.Messaging.BinaryMessageFormatter();
System.Messaging.Trustee trustee=new System.Messaging.Trustee();
System.Messaging.Message msg = mq.Peek ( new TimeSpan(10000)) ;
// Convert received message to object that we think was sent
string pt = (string) msg.Body ;
// Display it to the user
MessageBox.Show (pt) ;
}
catch(Exception ex)
{
MessageBox.Show(ex.ToString());
}
几点要注意的地方: