追求极致解耦
技术这条路,即苦行僧般的修行!

导航

 

MSMQ是什么 

       MicroSoft Message Queuing(微软消息队列)是在多个不同的应用之间实现相互通信的一种异步传输模式,相互通信的应用可以分布于同一台机器上,也可以分布于相连的网络空间中的任一位置。它的实现原理是:消息的发送者把自己想要发送的信息放入一个容器中(我们称之为Message),然后把它保存至一个系统公用空间的消息队列(Message Queue)中;本地或者是异地的消息接收程序再从该队列中取出发给它的消息进行处理。

        消息Message是由通信的双方所需要传递的信息。

        队列的类型主要包括一下几种:

       “公共队列”在整个“消息队列”网络中复制,并且有可能由网络连接的所有站点访问。

       “专用队列”不在整个网络中发布。相反,它们仅在所驻留的本地计算机上可用。专用队列只能由知道队列的完整路径名或标签的应用程序访问。

       “管理队列”包含确认在给定“消息队列”网络中发送的消息回执的消息。指定希望 MessageQueue 组件使用的管理队列(如果有的话)。

       “响应队列”包含目标应用程序接收到消息时返回给发送应用程序的响应消息。指定希望 MessageQueue 组件使用的响应队列(如果有的话)。

        优点:稳定、消息优先级、脱机能力以及安全性,有保障的消息传递和执行许多业务处理的可靠的防故障机制。

        缺点:MSMQ不适合于Client需要Server端实时交互情况.大量请求时候,响应延迟.

安装启动MSMQ

 

 

 

MSMQ查看

       在图上我们可以看见系统的消息队列,为我们提供了3种队列传出队列,专用队列和系统队列,而用户能自由创建的只有专用队列。我们可以尝试创建一个专用队列,"右击"专用队列,再点击"新建",得到新增界面如下图。由于我们创建的是专用队列,所以在队列命名上面显示的添加"private$",另一个事务复选框表示我们要创建的是队列是否为事务队列,这个在稍后我会详细的解释

消息

      消息是MSMQ的存储对象,封装为System.Messaging.Message对象,它由一个主体(body)和若干属性构成,其中我们的用户数据通常被序列化装入body主体中,这也是我们称它为数据容器的原因。除了body属性,还有几个属性相对来说比较重要:Priority(消息的优先级),Label(用户定义的消息标识),Formatter(消息的序列组件,当用户将复杂类型数据填充到body中,用户的数据会先被序列化)

队列

  在前面,我们通过手动创建了一个专用队列。我们知道队列分事务性队列和非事务性队列,默认创建的是非事务性队列。当我们勾选事务性复选框,我们就会创建事务性队列,那么什么是事务性队列呢?事务性队列将消息保存在磁盘上,实现了持久化,也就是说当我们关机,断电后,下次再启动机器,我们的消息依然保存在队列里面,而非事务性队列则将消息保存在内存中,也就是说我重启电脑后,队列里面的消息将不存在了。

     队列支持事务操作,当我们把对多个消息的接收操作纳入一个事务中,那么只要有一个消息接收不成功,队列将抛弃前面接收的所有消息,实现事务回滚。队列事务同时支持消息按顺序接收与发送。

MSMQ新建

1:手动新建:在电脑管理中手动操作新建.......略..........

2:程序新建

      请先添加using System.Messaging;命名空间

 

      查询队列是否存在,值得注意的是,当访问远端消息时MSMQ不支持MessageQueue.Exists(string )等方法,所以建议直接使用new MessageQueue(QueuePath)查询

 

(1):普通模式(非事务模式)

       这种模式的新建的消息队列,在电脑关机,重新...等等导致操作系统奔溃时,消息队列中的消息会被清空,因为MSMQ的消息默认是放在内存里面的。

       

//消息队列路径
            string path = ".\\Private$\\myQueue";
            //如果存在指定路径的消息队列
            if (MessageQueue.Exists(path))
            {
                //获取这个消息队列
                queue = new MessageQueue(path);
            }
            else
            {
                //不存在,就创建一个新的,并获取这个消息队列对象
                queue = MessageQueue.Create(path);

            }
            //格式化消息格式
            queue.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });

 

 

static string strServer = @"FormatName:Direct=TCP:10.7.46.42\private$\msmq";
 
static void Main(string[] args)
{
    DeleteAllMessage();
    SendMessage();
    ReveiveMessage();
}
 
public static void DeleteAllMessage()
{
    MessageQueue myQueue = new MessageQueue(strServer);
    myQueue.Purge();        //删除此队列中包含的所有消息
    myQueue.Dispose();
    //删除此队列
    MessageQueue.Delete(strServer);  
}
 
public static void SendMessage()
{
    MessageQueue myQueue = new MessageQueue(strServer);
    Message myMessage = new Message();
    try
    {
        myMessage.Body = new Person() { Name = "jake", Age = 29, Birthday = Convert.ToDateTime("1987/07/20") };
        myMessage.Formatter = new XmlMessageFormatter(new Type[] { typeof(Person) });
        //设置消息发送的优先级别
        myMessage.Priority = MessagePriority.Highest; //最高消息优先级
        myQueue.Send(myMessage);
    }
    catch(Exception ex)
    {
 
    }
    finally
    {
        myMessage.Dispose();
        myQueue.Dispose();
    }
 
}
 
public static void ReveiveMessage()
{
    //连接到本地队列
    MessageQueue myQueue = new MessageQueue(strServer);
    myQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(Person) });
    Message myMessage = null;
    try
    {
        int count = myQueue.GetAllMessages().Length;
        //从队列中接收消息
        //Peek: 返回队列中第一条消息的副本,而不从队列中移除该消息
        //Receive: 接收队列中的第一条消息,但不将它从队列中移除
        //PeekById: 返回具有指定消息标识符的消息的副本,但不从队列中移除消息
        //ReceiveById: 接收匹配给定标识符的消息,并将其从队列中移除
        myMessage = myQueue.Receive();
        Person p = (Person)myMessage.Body; //获取消息的内容
        Console.WriteLine("Name:" + p.Name);
        Console.WriteLine("Age:" + p.Age.ToString());
        Console.WriteLine("Birthday:" + p.Birthday.ToString("yyyy/MM/dd"));
        count = myQueue.GetAllMessages().Length;
        Console.ReadLine();
 
    }
    catch
    {
        Console.WriteLine("error4");
    }
    finally
    {
        myMessage.Dispose();
        myQueue.Dispose();
    }
}

 

 1 /// <summary>
 2         /// 获取队列第一条数据,但保留数据
 3         /// </summary>
 4         /// <param name="queueTypes"></param>
 5         /// <returns></returns>
 6         public object PeekOneQueue(Type[] queueTypes)
 7         {
 8             object result = null;
 9             if (QueueExist())
10             {
11                 using (MessageQueue mq = new MessageQueue(QueuePath))
12                 {
13                     try
14                     {
15                         // 设置消息队列的格式化器
16                         mq.Formatter = new XmlMessageFormatter(queueTypes);
17 
18                         if (mq.CanRead)
19                         {
20                             Message oneMessage = mq.Peek(); // 获得消息队列中第一条消息
21                             result = oneMessage.Body; 
22                         }
23                     }
24                     catch (Exception ex)
25                     {
26                         throw new Exception("Error to query Queue!", ex);
27                     }
28                 }
29             }
30             return result;
31         }

 

 

 

(2):事务模式(可持久化存储,只有手动删除消息或者接受后,消息消失)

 

 

  首先创建事务性队列,在发送消息的时候,要严格遵守事务完整性约束,只有事务过程结束才能发送成功!

  public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();


            //消息队列路径
            string path = ".\\Private$\\myQueue";

            //如果存在指定路径的消息队列
            if (MessageQueue.Exists(path))
            {
                //获取这个消息队列
                queue = new MessageQueue(path);
            }
            else
            {
                //不存在,就创建一个新的,并获取这个消息队列对象
                //queue = MessageQueue.Create(path);
                //创建事务性队列
                queue = MessageQueue.Create(path, true);
            }
            //格式化消息格式
            queue.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
        }
        private MessageQueue queue;
       
        private void Btn_Send_Click(object sender, EventArgs e)
        {
            // 定义事务  
            MessageQueueTransaction transaction = new MessageQueueTransaction();
            if (queue.Transactional)
                transaction.Begin();
            queue.Send(new System.Messaging.Message(tb_SendContent.Text, new XmlMessageFormatter(new Type[] { typeof(string) })), transaction);

            // 如果消息队列采用了事务,则停止事务  
            if (queue.Transactional)
                transaction.Commit();
            //System.Messaging.Message msg = new System.Messaging.Message();
            ////内容
            //msg.Body = tb_SendContent.Text;
            ////指定格式化程序
            //msg.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
            //queue.Send(msg);

        }

        private void Btn_Re_Click(object sender, EventArgs e)
        {

            List<string> list_s = new List<string>();
            foreach (var item in queue.GetAllMessages())
            {
                item.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
                list_s.Add(item.Body.ToString());
            }
            this.LBX_M.DataSource = list_s;
        }

        private void BTN_Get_Click(object sender, EventArgs e)
        {
            var msg = queue.Receive();
            if (msg != null)
            {
                //msg.Formatter = new BinaryMessageFormatter();
                msg.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
                var body = (string)msg.Body;
                MessageBox.Show(body.ToString());
                //Console.WriteLine("消息内容:{0},{1}", body.Id, body.Name);
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            var enumerator = queue.GetMessageEnumerator2();
            while (enumerator.MoveNext())
            {
                System.Messaging.Message msg = (System.Messaging.Message)(enumerator.Current);
                //msg.Formatter = new BinaryMessageFormatter();
                msg.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });

                MessageBox.Show(msg.Body.ToString());
                //根据消息ID查询并删除消息队列
                queue.ReceiveById(msg.Id);

            }
        }

        private void Btn_Delete_Click(object sender, EventArgs e)
        {
            MessageQueue.Delete(".\\Private$\\myQueue");

        }
    }
View Code

 

 

 

 

 

posted on 2021-01-11 14:17  追求极致解构  阅读(299)  评论(0)    收藏  举报