一、前言

      上一篇博客简单的向大家介绍了一下MSMQ的一些简单的概念。所以在这篇博客中小编就和大家一起进行一些对消息的处理,包括了创建消息、发送消息、接收消息、异步消息处理。其中整体上还是比较相似的,在其中的一些过程中我们可以添加事务来保证操作的完整性。

二、消息的处理流程

      下图是小编对整个处理过程每个小过程的总结、分析。下面依次详细介绍各个部分。

      MSMQ消息队列中定义的消息由一个主体(body)和若干属性构成。消息的主体可以由文本,二进制构成,根据需要还可以被加密。

      在MSMQ中消息大小不能够超过4MB。


【消息队列】MSMQ(二)——消息处理流程_优先级

三、创建消息

      可以说创建消息是消息的开始,.Net框架里MessageQueue类下有一个静态方法Create,用来完成消息队列的创建:

//path: 要创建的队列的路径。  
//transactional: 事务性队列,为 true;非事务性队列,则为 false
public static MessageQueue Create(string path);
public static MessageQueue Create(string path, bool transactional);

      事务性消息,可以确保事务中的消息按照顺序传送,只传送一次,并且从目的队列成功的被检索。

      通过Create方法创建使用指定路径的新消息队列


        /// <summary>
        /// 通过Create方法创建使用指定路径的新消息队列
        /// </summary>
        /// <param name="queuePath"></param>
        public  static MessageQueue Createqueue(string queuePath)
        {
            try
            {
                if (!MessageQueue.Exists(queuePath))
                {
                //Create方法
                 MessageQueue myTranMessage = MessageQueue.Create(@".\private$\myQueue"); 

                //创建事务性的专用消息队列
                //MessageQueue myTranMessage =MessageQueue.Create(@".\private$\myQueueTrans", true);

                //创建远程服务器连接的消息队列,这里注意写法
                //MessageQueue myTranMessage =MessageQueue.Create(@"FormatName:Direct=TCP:192.168.22.232\private$\myQueue");
}
                else { }
            }
            catch (MessageQueueException e) {}
}
四、发送消息

      发送消息是通过Send方法完成的,需要一个Message参数,用于存储数据。

      步骤:

  • a.连接队列

  • b.指定消息发送的优先级

  • c.指定消息格式

  • d.提供要发送的数据(主体)

  • e.调用Send()方法讲消息发送出去

      下面小编把其中有技术点的地方向大家介绍一下:

4.1 设置发送优先级

      在MSMQ中消息在队列中传输是分优先级的,优先级有7种,在枚举MessagePriority中进行了封装:

public enum MessagePriority
{
Lowest = 0,VeryLow = 1,Low = 2,Normal = 3,AboveNormal = 4,
High = 5,VeryHigh = 6,Highest = 7,
}

      设置优先级:

message.Priority = MessagePriority.Highest;  //最高消息优先级

4.2 格式化消息

      消息序列化可以通过.NET Framework 附带的三个预定义格式化程序来完成:

  • XMLMessageFormatter对象 (MessageQueue组件默认格式)

  • BinaryMessageFormatter对象

  • ActiveXMessageFormatter对象

      由于后两者格式化后的信息通常不能为人阅读,所以我们经常用到的是XMLMessageFormatter对象。该对象构造方法有三种重载:

public XmlMessageFormatter();
public XmlMessageFormatter(string[] targetTypeNames);
public XmlMessageFormatter(Type[] targetTypes);

4.3 提供要发送的数据

      消息队列可以传输简单消息类型,也可以传输复杂消息类型。

      简单消息类型就是常用的类型,例如整形、字符串等数据;复杂消息的数据类型通常对应于系统中复杂数据库类型,例如结构,对象等等。

      简单消息发送示例:

mq.send(1000); //发送整型数据 
mq.send("this is a test message!"); //发送字符串

      复杂消息类型发送和简单类型大同小异,只是发送的是实体,把要有的属性封装到实体中。

      例如:

            Customer customer = new Customer();
            customer.FirstName = "copernicus";
            customer.LastName = "nicolaus";
            mq.send(customer);

4.3 发送消息示例

/// <summary>
/// 连接消息队列并发送消息到队列
/// </summary>
public static void SendMessage()
{
    try
    {
        //连接到本地的队列
        MessageQueue myQueue = new MessageQueue(".\\private$\\myQueue");
        Message myMessage = new Message();
        myMessage.Body = "消息内容";
       //设置最高消息优先级
       message.Priority = MessagePriority.Highest;
       //序列化为字符串
        myMessage.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
       //发送消息到队列中
       myQueue.Send(myMessage);
        //事务性消息需加上下面几句
        //MessageQueueTransaction myTransaction = new MessageQueueTransaction();
        //启动事务 myTransaction.Begin();
        //myQueue.Send(myMessage, myTransaction);  //加了事务
        //提交事务 myTransaction.Commit();
    }
    catch (ArgumentException e){}
}
五、接收消息

      接收消息,MessageQueue提供了两个方法来接收消息:

  • Receive方法接收消息同时永久性地从队列中删除消息;

  • Peek方法从队列中取出消息而不从队列中移除该消息。

      如果知道消息的标识符(ID),还可以通过ReceiveById方法和PeekById方法完成相应的操作。

/// <summary>
/// 连接消息队列并从队列中接收消息
/// </summary>
public static void ReceiveMessage()
{
    //连接到本地队列
MessageQueue myQueue = new MessageQueue(".\\private$\\myQueue");
//注:由于消息的优先级是枚举类型,在直接messages[index].Priority.ToString();这种方式来获取优先级转化到字符串的时候,他需要一个过滤器(Filter),否则会抛出一个InvalidCastExceptionle类型的异常,异常信息"接收消息时未检索到属性 Priority。请确保正确设置了 PropertyFilter。",要解决这问题只需要把消息对象的MessageReadPropertyFilter(过滤器) 的Priority设置为true。
myQueue.MessageReadPropertyFilter.Priority = true;
//接收端必须反序列化
    myQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
    try
    {    
        //从队列中接收消息
        Message myMessage = myQueue.Receive();
        string context = (string)myMessage.Body; //获取消息的内容
        string priority = messages[index].Priority.ToString();
    }
    catch (MessageQueueException e){}
    catch (InvalidCastException e){}
}
六、异步消息处理

      在MSMQ中对消息的操作分有同步化操作和异步化操作两种,那两者到底有什么区别?简单来说同步化操作就是一项操作没有完成前它将堵塞整个进程直到操作完成,下一项操作才会执行。 所谓的异步化操作则相反,不会堵塞启动操作的调用线程。如果想在检索消息但不想堵塞其他程序的执行,则可使用异步消息处理。

      在MSMQ中异步接收消息使用BeginReceive方法和EndReceive方法来标记操作的开始和结束,异步查看消息则使用BeginPeek和EndPeek两个方法来标记异步读取的开始和结束。同时,异步接收和查看消息还可以指定超时时间。

      发送消息需加上事务处理,同上。异步接收消息:利用委托机制

myQueue.ReceiveCompleted += newReceiveCompletedEventHandler(MyReceiveCompleted);

      异步接收消息操作示例:

//通知一个或多个正在等待的线程已发生事件
static ManualResetEvent signal = new ManualResetEvent(false);
/// <summary>
/// 异步接收消息
/// </summary>
private static void AsyncReceiveMessage()
{
    MessageQueue myQueue = new MessageQueue(".\\private$\\myAsyncQueue");
    if (myQueue.Transactional)
    {
        MessageQueueTransaction myTransaction = new MessageQueueTransaction();
        //这里使用了委托,当接收消息完成的时候就执行MyReceiveCompleted方法
        myQueue.ReceiveCompleted += new ReceiveCompletedEventHandler(MyReceiveCompleted);
        myQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(MSMQ.Async.Book) });
        myTransaction.Begin();
        myQueue.BeginReceive();//启动一个没有超时时限的异步操作
        signal.WaitOne();
        myTransaction.Commit();
    }
}
private static void MyReceiveCompleted(Object source, ReceiveCompletedEventArgs asyncResult)
{
    try
    {
        MessageQueue myQueue = (MessageQueue)source;
        //完成指定的异步接收操作
        Message message = myQueue.EndReceive(asyncResult.AsyncResult);
        signal.Set();
        Book book = message.Body as Book;
        myQueue.BeginReceive();
    }
    catch (MessageQueueException me){}
}

最后另外MSMQ示例项目代码,有兴趣的朋友,可以down下来尝试一下:点击我下载。

七、小结

      通过这次的总结,自己算是对MSMQ的理解更加的深刻了,其实还是有很多的地方用到了消息队列,消息队列对整个框架的性能提升有了很大的进步。所以在下一篇博客中,向大家介绍一下消息队列的应用。