微软MSMQ消息队列的实际使用记录
实质
在消息传递机制中,有两个比较重要的概念。一个是消息,一个是队列。消息是由通信的双方所需要传递的信息,它可以是各式各样的媒体,如文本、声音、图象等等。消息最终的理解方式,为消息传递的双方事先商定,这样做的好处是,一是相当于对数据进行了简单的加密,二则采用自己定义的格式可以节省通信的传递量。消息可以含有发送和接收者的标识,这样只有指定的用户才能看到只传递给他的信息和返回是否操作成功的回执。消息也可以含有时间戳,以便于接收方对某些与时间相关的应用进行处理。消息还可以含有到期时间,它表明如果在指定时间内消息还未到达则作废,这主要应用与时间性关联较为紧密的应用。消息队列是发送和接收消息的公用存储空间,它可以存在于内存中或者是物理文件中。消息可以以两种方式发送,即快递方式(express)和可恢复模式(recoverable),它们的区别在于,快递方式为了消息的快速传递,把消息放置于内存中,而不放于物理磁盘上,以获取较高的处理能力;可恢复模式在传送过程的每一步骤中,都把消息写入物理磁盘中,以得到较好的故障恢复能力。消息队列可以放置在发送方、接收方所在的机器上,也可以单独放置在另外一台机器上。正是由于消息队列在放置方式上的灵活性,形成了消息传送机制的可靠性。当保存消息队列的机器发生故障而重新启动以后,以可恢复模式发送的消息可以恢复到故障发生之前的状态,而以快递方式发送的消息则丢失了。另一方面,采用消息传递机制,发送方不必要再担心接收方是否启动、是否发生故障等等非必要因素,只要消息成功发送出去,就可以认为处理完成,而实际上对方可能甚至未曾开机,或者实际完成交易时可能已经是第二天了。
作用
采用MSMQ带来的好处是:由于是异步通信,无论是发送方还是接收方都不用等待对方返回成功消息,就可以执行余下的代码,因而大大地提高了事物处理的能力;当信息传送过程中,信息发送机制具有一定功能的故障恢复能力;MSMQ的消息传递机制使得消息通信的双方具有不同的物理平台成为可能。在微软的.net平台上利用其提供的MSMQ功能,可以轻松创建或者删除消息队列、发送或者接收消息、甚至于对消息队列进行管理。
这是从百度百科收集到的一些MSMQ资料 基本和官方的说明相差无几 看起来都是血脉膨胀激励人心的话但是使用之中或多或少还存在着一些问题 需要自己从实践中去吸取经验 现在本人就自己的一些接触和使用分享一些中间遇到的问题 :
发送:个人觉得发送问题比较重要的就是客户端与远程MSMQ服务器传送MSMQ消息的问题,经过本人查资料发现远程MSMQ通信必须配置AD和开放对应权限,因为比较麻烦,而且本人所在项目本身没跨网络的需求,所以没有深入了解。其他OS TCP等内网方式通信都OK, 不过还有一点值得注意的就是:与虚拟机的MSMQ通信未能成功。
接收: 这里先贴一下本人测试的接收代码,这里是客户端发送和服务器接收分开做的测试,服务器接收端放到各种场景(虚拟机、外网、内网本机、内网其他机器)测试
{
// Define static class members.
static ManualResetEvent signal = new ManualResetEvent(false);
static int count = 0;
static void Main(string[] args)
{
string serverFormatName = "FormatName:DIRECT=OS:vis-pc\\Private$\\msmqtest";
Console.WriteLine("MSMQ测试:");
try
{
MessageQueue queue = new MessageQueue(serverFormatName,QueueAccessMode.Receive);
queue.ReceiveCompleted += new ReceiveCompletedEventHandler(queue_ReceiveCompleted);
queue.BeginReceive();
signal.WaitOne();
}
catch (MessageQueueException e)
{
Console.WriteLine(e.ToString());
}
return;
}
//判断成功接收后可更新队列表状态-需要队列表提供接口
protected static void queue_ReceiveCompleted(Object source, ReceiveCompletedEventArgs e)
{
try
{
string result = string.Empty;
if (source.Equals(null))
{
return;
}
MessageQueue mq = (MessageQueue)source;
Message m = mq.EndReceive(e.AsyncResult);
//var query = m.Body;
//result = (string)m.Body;
count += 1;
if (count == 10)
{
signal.Set();
}
//可更新系统的队列表状态-证明信息接收成功
string filePath = @"E:\Log.txt";
using (StreamWriter sw = new StreamWriter(filePath, true))
{
String logMsg = String.Format("[{0}]{1}", DateTime.Now.ToString(), "接收成功");
sw.WriteLine(logMsg);
}
mq.BeginReceive();
}
catch (MessageQueueException _mqe)
{
Console.WriteLine(_mqe.ToString());
}
return;
}
}
这里参考了微软官方的示例,加了线程等待等,而且模拟了监听功能,但是接收中出现了接收不到主体信息内容等(描述信息能收到),需要强调的是发送和接收在本机测试都没问题 把接收程序放在其他机器上会引发Message对象Boby属性的:System.InvalidOperationException异常 经分析可能是权限问题
同事找到MSDN上这样一篇文章:启用安全的远程读取 http://technet.microsoft.com/zh-cn/library/cc737783(WS.10).aspx 在这里做过测试,但是未能成功,不知道是不是其他权限没有给足:博客园也有人碰到过类似问题 http://www.cnblogs.com/yjmyzz/archive/2007/12/04/982440.html 和给出解决思路,这里就不细说了。
消息队列是微软对消息服务领域的开创性尝试。它采用了特殊的通信机制,对改善和提供系统的可扩展性和高可用性具有重要意义:
(1)对异步的消息发送方式和离线通信方式的支持
(2)消息发送方和消息处理方可以完全分离
(3)可靠的消息传输,消息队列通过特殊的传输机制,比如消息确认、超时处理、消息日志以及死信队列等,从分保证了消息的可靠传输
(4)事物的支持,提供对本地事物和分布式事物的支持,可以可以把一个消息队列的操作和一个基于SQLServer的操作纳入同一事物中
消息队列按照可访问性可分为两种类型的队列:
(1)公共消息队列,公共消息队列发布于活动目录AD并被复制Windows域。因为可以在不知道队列所在机器名称的情况下对公共队列进行检索,因而将公共队列从一台计算机移到另一台上,并不会对客户端应用造成任何影响
(2)私有消息队列,私有消息队列一般在没有AD的工作组环境中使用,它们不支持身份验证,并且需要队列所在的计算机名称方能定位
最终抛弃了Framework的MSMQ操作,使用WCF的MSMQ操作测试成功。WCF的可靠性会话是基于WS-RM标准的,而MSMQ只是ms自家的
WCF下基于消息队列的URL具有net.msmq前缀。net.msmq地址中必须要指明队列的类型(public/private)。由于默认为公有队列,所以对于公有队列类型部分可以省略,另外WCF的net.msmq是使用的动态端口,和net.tcp的默认端口808、http的80、https的443不同。