MQ是一种企业服务的消息中间节技术,这种技术常常伴随着企业服务总线相互使用,构成了企业分布式开发的一部分,如果考虑到消息的发送和传送之间是可以相互不联系的并且需要分布式架构,则可以考虑使用MQ做消息的中间价技术,MQ的功能已经足够开发使用。
MSMQ 原理
MSMQ的实现原理是:消息的发送者把自己想要发送的信息放入一个容器,然后把它保存到一个系统公用空间的消息队列中,本地或异地的消息接收程序再从该队列中取出发给它的消息进行处理。
消息队列是一个公用存储空间,它可以存在于内存中或物理文件中,因此,消息以两种方式发送,即快递方式和可恢复模式。它们的区别是消息存储位置的不同,快递方式,为了消息的快速传递,所以把消息放置在内存中,而不放在物理磁盘上,以获得较高的处理能力;而可恢复模式在传送过程的每一步骤中,都把消息写入物理磁盘上,这样当保存消息队列的机器发生故障而重新启动后,可以把发送的消息恢复到故障发送之前的状态,以获得更好的消息恢复能力。消息队列可以放在发送方、接收方所在的机器上,也可以单独放置在另外一台机器上。另外,采用消息队列机制,发送方不必要担心接收方是否启动,是否发生故障等因素,只要消息成功发送出去,就可以认为处理完成,而实际上对方可能甚至未开机,或者实际消息传递到对方可能在第二天。MSMQ机制类似QQ消息传递机制。下图演示了MSMQ的实现原理。
公共队列:在整个消息队列网络中复制,有可能由网络连接的所有站点访问。
路径格式为:
机器名称\队列名称
专用队列(或叫私有队列):不在整个网络中发布,它们仅在所驻留的本地计算机上可用,专用队列只能由知道队列的完整路径名称或标签的应用程序访问。
路径格式为:
机器名称\Private$\队列名称
日志队列:包含确认在给定“消息队列中发送的消息回执消息”。
路径格式为:机器名称\队列名称\Journal$
响应队列:包含目标应用程序接收到消息时返回给发送应用程序的响应消息,包括机器日志队列、机器死信队列和机器事务死信队列。其中,
机器信道死信队列对应的格式为:机器名称\XactDeadletterKaTeX parse error: Undefined control sequence: \Deadletter at position 20: …死信队列对应的格式为:机器名称\̲D̲e̲a̲d̲l̲e̲t̲t̲e̲r̲;
机器日志队列对应的格式为:机器名称\Journal$;
优点:稳定、消息优先级、脱机能力以及安全性,有保障的消息传递和执行许多业务处理的可靠的防故障机制。
缺点:MSMQ不适合于Client需要Server端实时交互情况.大量请求时候,响应延迟.
工作组模式或域模式
MSMQ可以安装为工作组模式或域模式。如果安装程序没有找到一台运行提供目录服务的消息队列的服务器,则只可以安装为工作组模式,此计算机上的“消息队列”只支持创建专用队列和创建与其他运行“消息队列”的计算机的直接连接。
《安装与启动服务》
我的电脑 >> 管理 >> 服务和应用程序 >> 消息队列 。出现消息队列则说明安装成功
MSMQ 客户端
using System;
using System.Messaging; // 需要添加System.Messaging引用
namespace MSMQClient
{
class Program
{
static void Main(string[] args)
{
//私有队列 、专用队列
if (MessageQueue.Exists(@".\Private$\AresWangMSMQ"))
{
// 创建消息队列对象
using (MessageQueue mq = new MessageQueue(@".\Private$\AresWangMSMQ"))
{
// 设置消息队列的格式化器
mq.Formatter = new XmlMessageFormatter(new string[] { "System.String" });
foreach (Message msg in mq.GetAllMessages())
{
Console.WriteLine("Received Private Message is: {0}", msg.Body);
}
if(mq.CanRead)
{
//mq.Peek() // 获得消息队列中第一条消息,但删除第一条记录
Message firstmsg = mq.Receive(); // 获得消息队列中第一条消息,且删除第一条记录,
Console.WriteLine("Received The first Private Message is: {0}", firstmsg.Body);
}
}
}
Console.Read();
}
}
}
MSMQ 服务端
using System;
using System.Messaging;
namespace MSMQServer
{
class Program
{
static void Main(string[] args)
{
// 创建一个公共队列,公共队列只能创建在域环境里
//if (!MessageQueue.Exists(@".\AresWangMSMQ")) // 判断此路径下是否已经有该队列
//{
// using (MessageQueue mq = MessageQueue.Create(@".\AresWangMSMQ"))
// {
// mq.Label = "AresWangMSMQ"; // 设置队列标签
// Console.WriteLine("已经创建了一个公共队列");
// Console.WriteLine("路径为:{0}", mq.Path);
// Console.WriteLine("队列名字为:{0}", mq.QueueName);
// mq.Send("MSMQ Message", "Leaning Hard"); // 发送消息
// }
//}
//if (MessageQueue.Exists(@".\Private$\AresWangMSMQ"))
//{
// // 删除消息队列
// MessageQueue.Delete(@".\Private$\AresWangMSMQ");
//}
// 创建一个私有消息队列
if (!MessageQueue.Exists(@".\Private$\AresWangMSMQ"))
{
using (MessageQueue mq = MessageQueue.Create(@".\Private$\AresWangMSMQ"))
{
mq.Label = "AresWangMSMQ";
Console.WriteLine("已经创建了一个私有队列");
Console.WriteLine("路径为:{0}", mq.Path);
Console.WriteLine("私有队列名字为:{0}", mq.QueueName);
mq.Send("MSMQ Private Message", "AresWangMSMQ"); // 发送消息
}
}
// 遍历所有的公共消息队列
//foreach (MessageQueue mq in MessageQueue.GetPublicQueues())
//{
// mq.Send("Sending MSMQ public message" + DateTime.Now.ToLongDateString(), "Learning Hard");
// Console.WriteLine("Public Message is sent to {0}", mq.Path);
//}
if (MessageQueue.Exists(@".\Private$\AresWangMSMQ"))
{
// 获得私有消息队列
MessageQueue mq = new MessageQueue(@".\Private$\AresWangMSMQ");
if(mq.CanWrite)
{
mq.Send("Sending MSMQ private message" + DateTime.Now.ToLongDateString(), "AresWangMSMQ");
}
Console.WriteLine("Private Message is sent to {0}", mq.Path);
}
Console.Read();
}
}
}