在我上一篇文章《
ASP.NET中进行消息处理(MSMQ)一》里对MSMQ做了个通俗的介绍,最后以发送普通文本消息和复杂的对象消息为例介绍了消息队列的使用。 本文在此基础上继续介绍MSMQ的相关知识点,最后还是通过一个示例程序来分析MSMQ在实际项目开发中的应用。
建议:如果你对MSMQ不够了解,在你阅读本文前请先阅读第一部分:《
ASP.NET中进行消息处理(MSMQ)一》。
一、消息传递的优先级
在MSMQ中消息在队列里传输是分有优先级的,这里我就以实例的形式介绍下关于优先级的使用,优先级一共有七种,MessagePriority枚举里全部进行了封装。因这里只作程序演示就不一一列举出,仅用了
Highest和
Normal两种类型,关于消息队列上进行消息传输的七种优先级大家可以参考我下面提供的MessagePriority枚举源代码定义。
那么在发送消息的时候怎么指定消息的优先级呢?在Message对象里封装有一个属性
Priority,接受一个枚举MessagePriority类型的值来设置消息传输的优先级。如下:
1
System.Messaging.Message message = new System.Messaging.Message();
2
message.Priority = MessagePriority.Highest; //最高消息优先级
下面来看看一个在消息传输中使用优先级的示例程序,通过示例程序会学习得更明白。示例程序界面:
根据界面可知,提供了消息名字,消息优先级和消息内容三个输入项,前面曾经说过,这里为了方便演示就仅用了
Highest和
Normal两种类型,当点击发送消息的时候就通过是否选择优先级来设置消息的优先级,代码如下:
1
private void btnSend_Click(object sender, EventArgs e)
2

{
3
//连接到本地的专用队列myQueue
4
MessageQueue myQueue = new MessageQueue(".\\private$\\myQueue");
5
System.Messaging.Message message = new System.Messaging.Message();
6
message.Label = tbName.Text;
7
message.Body = tbContext.Text;
8
9
if (cbPriority.Checked)
10
{
11
message.Priority = MessagePriority.Highest;
12
}
13
else
14
{
15
message.Priority = MessagePriority.Normal;
16
}
17
myQueue.Send(message);
18
MessageBox.Show("成功发送消息到队列");
19
}
这里我们可以向队列里发送两条消息,以便后面测试读取方法,发送两条消息到队列,此时,从队列消息中可以看到:

"刷新队列"实质上就是把队列里的消息全部读取出来,具体的实现在《
ASP.NET中进行消息处理(MSMQ)一》里已经作了详细的介绍,这里就不在多说,看看下面的代码:
1
private void DisplayMessage()
2
{
3
//连接到本地队列
4
MessageQueue myQueue = new MessageQueue(".\\private$\\myQueue");
5
myQueue.MessageReadPropertyFilter.Priority = true;
6
DataTable messageTable = new DataTable();
7
messageTable.Columns.Add("名字");
8
messageTable.Columns.Add("消息内容");
9
messageTable.Columns.Add("优先级");
10
XmlMessageFormatter formatter = new XmlMessageFormatter(new string[]
{ "System.String" });
11
try
12
{
13
//从队列中接收消息
14
System.Messaging.Message[] messages = myQueue.GetAllMessages();
15
for (int index = 0; index < messages.Length; index++)
16
{
17
messages[index].Formatter = formatter;
18
19
string label = messages[index].Label;
20
string body = messages[index].Body.ToString();
21
string priority = messages[index].Priority.ToString();
22
23
messageTable.Rows.Add(new string[]
{ label, body, priority });
24
}
25
this.dgvMessage.DataSource = messageTable;
26
}
27
catch (MessageQueueException e1)
28
{
29
MessageBox.Show(e1.Message);
30
}
31
}
32
}
这里封装了一方法,专门负责从队列里读取全部消息并绑定在DataGridView控件上,这里我们只需要在按扭Click事件里调用这方法就OK。
1
private void btnRec_Click(object sender, EventArgs e)
2

{
3
DisplayMessage();
4
}
这就完成了给消息设置优先级的消息传输的应用,最终的测试结果如下:
注:要完成以上应用还需注意一点,由于消息的优先级是枚举类型,在直接
messages[index].Priority.ToString();这种方式来获取优先级转化到字符串的时候,他需要一个过滤器(Filter),否则会抛出一个InvalidCastExceptionle类型的异常,异常信息"接收消息时未检索到属性 Priority。请确保正确设置了 PropertyFilter。",要解决这问题只需要把消息对象的MessageReadPropertyFilter(过滤器) 的Priority设置为true就OK了。见上面代码里!^.^
MessagePriority枚举源代码定义详细如下:
1
// 摘要:
2
// 指定消息队列在消息传递到队列的过程中应用于该消息的优先级,以及指定何时将消息插入目标队列。
3
public enum MessagePriority
4

{
5
// 摘要:
6
// 最低消息优先级。
7
Lowest = 0,
8
//
9
// 摘要:
10
// 位于 Low 和 Lowest 消息优先级之间。
11
VeryLow = 1,
12
//
13
// 摘要:
14
// 低消息优先级。
15
Low = 2,
16
//
17
// 摘要:
18
// 普通消息优先级。
19
Normal = 3,
20
//
21
// 摘要:
22
// 位于 System.Messaging.MessagePriority.High 和 System.Messaging.MessagePriority.Normal
23
// 消息优先级之间。
24
AboveNormal = 4,
25
//
26
// 摘要:
27
// 高消息优先级。
28
High = 5,
29
//
30
// 摘要:
31
// 位于 Highest 和 High 消息优先级之间。
32
VeryHigh = 6,
33
//
34
// 摘要:
35
// 最高消息优先级。
36
Highest = 7,
37
}
二、事务性消息处理
事务我想大家对这个词应该都不会陌生,在操作数据库的时候经常都会用到事务,确保操作成功,要么全部完成(成功),要么全部不完成(失败)。在MSMQ中利用事务性处理,可以确保事务中的消息按照顺序传送,只传送一次,并且从目的队列成功地被检索。
那么,在MSMQ上使用事务性处理怎么实现呢?可以通过创建MessageQueueTransation类的实例并将其关联到MessageQueue组件的实例来执行,执行事务的Begin方法,并将其实例传递到收发方法。然后,调用Commit以将事务的更改保存到目的队列。
创建事务性消息和普通的消息有一点小小的区别,大家可从下图上体会到:

通过代码方式建立事务性消息队列只在Create方法的参数上动动手脚就OK,详细如下:
1
//创建普通的专用消息队列
2
MessageQueue myMessage = MessageQueue.Create(@".\private$\myQueue");
3
//创建事务性的专用消息队列
4
MessageQueue myTranMessage =MessageQueue.Create(@".\private$\myQueueTrans", true);
启动了事务,那么在发送和接收消息的时候肯定是与原来有一定的差别的,这里我就不做详细介绍,下面给出示意性代码,有兴趣的朋友可以直接下载本文示例程序代码了解更多。
普通的消息发送示意性代码:
1
//连接到本地的队列
2
MessageQueue myQueue = new MessageQueue(".\\private$\\myQueue");
3
Message myMessage = new Message();
4
myMessage.Body = "消息内容";
5
myMessage.Formatter = new XmlMessageFormatter(new Type[]
{ typeof(string) });
6
//发送消息到队列中
7
myQueue.Send(myMessage);
启动了事务后的消息发送示意性代码:
1
//连接到本地的队列
2
MessageQueue myQueue = new MessageQueue(".\\private$\\myQueueTrans");
3
4
Message myMessage = new Message();
5
myMessage.Body = "消息内容";
6
myMessage.Formatter = new XmlMessageFormatter(new Type[]
{ typeof(string) });
7
8
MessageQueueTransaction myTransaction = new MessageQueueTransaction();
9
//启动事务
10
myTransaction.Begin();
11
//发送消息到队列中
12
myQueue.Send(myMessage, myTransaction); //加了事务
13
//提交事务
14
myTransaction.Commit();
15
Console.WriteLine("消息发送成功!");
读取消息示意性代码:
1
//连接到本地队列
2
MessageQueue myQueue = new MessageQueue(".\\private$\\myQueueTrans");
3
myQueue.Formatter = new XmlMessageFormatter(new Type[]
{ typeof(string) });
4
if (myQueue.Transactional)
5

{
6
MessageQueueTransaction myTransaction = new MessageQueueTransaction();
7
//启动事务
8
myTransaction.Begin();
9
//从队列中接收消息
10
Message myMessage = myQueue.Receive(myTransaction);
11
string context = myMessage.Body as string; //获取消息的内容
12
myTransaction.Commit();
13
Console.WriteLine("消息内容为:" + context);
14
}
三、异步消息处理
在MSMQ中对消息的操作分有同步化操作和异步化操作两种,那两者到底有何区别呢?简单的说同步化操作就是一项操作没有完成前它将堵塞整个进程直到操作完成,下一项操作才会执行。所谓异步化操作则相反,不会堵塞启动操作的调用线程。如果你想在检索消息但不想堵塞其他程序的执行,则可使用异步消息处理。
在MSMQ中异步接收消息使用BeginReceive方法和EndReceive方法来标记操作的开始和结束,异步查看消息则使用BeginPeek和EndPeek两个方法来标记异步读取的开始和结束。同时,异步接收和查看消息还可以指定超时时间,关于这点我在后续文章里再做详细的介绍,有兴趣的朋友可以关注。
这里我将使用《
ASP.NET中进行消息处理(MSMQ)一》一文里使用过的Book类来作为消息传输,没阅读过的朋友请先阅读这篇文章,了解Book类的结构。下面提供了一个示例,创建队列和发送消息到队列在前面我们已经使用多次了这里就不贴代码了,发送消息这里与第一篇文章中介绍如何发送一个复杂的类型信息到队列比,只是多了事务处理,详细如下:
1
/**//// <summary>
2
/// 发送消息到队列
3
/// </summary>
4
private static void SendMessage()
5

{
6
MessageQueue myQueue = new MessageQueue(".\\private$\\myAsyncQueue");
7
if (myQueue.Transactional)
8
{
9
Book book = new Book();
10
book.BookId = 1001;
11
book.BookName = "ASP.NET";
12
book.BookAuthor = "ZhangSan";
13
book.BookPrice = 88.88;
14
Message message = new Message(book);
15
message.Formatter = new XmlMessageFormatter(new Type[]
{ typeof(MSMQ.Async.Book) });
16
17
MessageQueueTransaction myTransaction = new MessageQueueTransaction();
18
myTransaction.Begin();
19
myQueue.Send(message, myTransaction);
20
myTransaction.Commit();
21
Console.WriteLine("消息成功发送到队列!");
22
}
23
}
异步接收消息:
1
/**//// <summary>
2
/// 异步接收消息
3
/// </summary>
4
private static void AsyncReceiveMessage()
5

{
6
MessageQueue myQueue = new MessageQueue(".\\private$\\myAsyncQueue");
7
if (myQueue.Transactional)
8
{
9
MessageQueueTransaction myTransaction = new MessageQueueTransaction();
10
//这里使用了委托,当接收消息完成的时候就执行MyReceiveCompleted方法
11
myQueue.ReceiveCompleted += new ReceiveCompletedEventHandler(MyReceiveCompleted);
12
myQueue.Formatter = new XmlMessageFormatter(new Type[]
{ typeof(MSMQ.Async.Book) });
13
myTransaction.Begin();
14
myQueue.BeginReceive();//启动一个没有超时时限的异步操作
15
signal.WaitOne();
16
myTransaction.Commit();
17
}
18
}
1
private static void MyReceiveCompleted(Object source, ReceiveCompletedEventArgs asyncResult)
2

{
3
try
4
{
5
MessageQueue myQueue = (MessageQueue)source;
6
//完成指定的异步接收操作
7
Message message = myQueue.EndReceive(asyncResult.AsyncResult);
8
signal.Set();
9
Book book = message.Body as Book;
10
Console.WriteLine("图书编号:{0}--图书名称:{1}--图书作者:{2}--图书定价:{3}",
11
book.BookId.ToString(),
12
book.BookName,
13
book.BookAuthor,
14
book.BookPrice.ToString());
15
myQueue.BeginReceive();
16
}
17
catch (MessageQueueException me)
18
![]()