ASP.NET中进行消息处理(MSMQ) 二

在MSMQ中消息的队列是分优先级的,优先级一共分为七种;在MessagePriority里面已经给予全部的枚举

// 摘要:
// 指定消息队列在消息传递到队列的过程中应用于该消息的优先级,以及指定何时将消息插入目标队列。
public enum MessagePriority
{
// 摘要:
// 最低消息优先级。
Lowest = 0,
//
// 摘要:
// 位于 Low 和 Lowest 消息优先级之间。
VeryLow = 1,
//
// 摘要:
// 低消息优先级。
Low = 2,
//
// 摘要:
// 普通消息优先级。
Normal = 3,
//
// 摘要:
// 位于 System.Messaging.MessagePriority.High 和 System.Messaging.MessagePriority.Normal
// 消息优先级之间。
AboveNormal = 4,
//
// 摘要:
// 高消息优先级。
High = 5,
//
// 摘要:
// 位于 Highest 和 High 消息优先级之间。
VeryHigh = 6,
//
// 摘要:
// 最高消息优先级。
Highest = 7,
}

定义信息的优先级顺序为

1System.Messaging.Message message = new System.Messaging.Message();
2message.Priority = MessagePriority.Highest; //最高消息优先级

下面通过一个案例进行总结:



代码为:

"刷新队列"实质上就是把队列里的消息全部读取出来

        private void DisplayMessage()
{
//连接到本地队列
MessageQueue myQueue = new MessageQueue(".\\private$\\myQueue");
myQueue.MessageReadPropertyFilter.Priority = true;
DataTable messageTable = new DataTable();
messageTable.Columns.Add("名字");
messageTable.Columns.Add("消息内容");
messageTable.Columns.Add("优先级");
XmlMessageFormatter formatter = new XmlMessageFormatter(new string[] { "System.String" });
try
{
//从队列中接收消息
System.Messaging.Message[] messages = myQueue.GetAllMessages();
for (int index = 0; index < messages.Length; index++)
{
messages[index].Formatter = formatter;

string label = messages[index].Label;
string body = messages[index].Body.ToString();
string priority = messages[index].Priority.ToString();

messageTable.Rows.Add(new string[] { label, body, priority });
}
this.dgvMessage.DataSource = messageTable;
}
catch (MessageQueueException e1)
{
MessageBox.Show(e1.Message);
}
}

注:要完成以上应用还需注意一点,由于消息的优先级是枚举类型,在直接messages[index].Priority.ToString();这种方式来获取优先级转化到字符串的时候,他需要一个过滤器(Filter),否则会抛出一个InvalidCastExceptionle类型的异常,异常信息"接收消息时未检索到属性 Priority。请确保正确设置了 PropertyFilter。",要解决这问题只需要把消息对象的MessageReadPropertyFilter(过滤器) 的Priority设置为true就OK了。见上面代码里!^.^

二、事务性消息处理

          事务我想大家对这个词应该都不会陌生,在操作数据库的时候经常都会用到事务,确保操作成功,要么全部完成(成功),要么全部不完成(失败)。在MSMQ中利用事务性处理,可以确保事务中的消息按照顺序传送,只传送一次,并且从目的队列成功地被检索。      那么,在MSMQ上使用事务性处理怎么实现呢?可以通过创建MessageQueueTransation类的实例并将其关联到MessageQueue组件的实例来执行,执行事务的Begin方法,并将其实例传递到收发方法。然后,调用Commit以将事务的更改保存到目的队列。      创建事务性消息和普通的消息有一点小小的区别,大家可从下图上体会到:                                  

通过代码方式建立事务性消息队列只在Create方法的参数上动动手脚就OK,详细如下:

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

启动了事务,那么在发送和接收消息的时候肯定是与原来有一定的差别的,这里我就不做详细介绍,下面给出示意性代码,有兴趣的朋友可以直接下载本文示例程序代码了解更多。

普通的发送消息的方式:

//连接到本地的队列
MessageQueue myQueue = new MessageQueue(".\\private$\\myQueue");
Message myMessage = new Message();
myMessage.Body = "消息内容";
myMessage.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
//发送消息到队列中
myQueue.Send(myMessage);

加上事务的发送消息的方式:

 ///<summary>
/// 连接消息队列并发送消息到队列
///</summary>
public static void SendMessage()
{
try
{
//连接到本地的队列
MessageQueue myQueue = new MessageQueue(".\\private$\\myQueueTrans");

Message myMessage = new Message();
myMessage.Body = "消息内容";
myMessage.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });

MessageQueueTransaction myTransaction = new MessageQueueTransaction();
//启动事务
myTransaction.Begin();
//发送消息到队列中
myQueue.Send(myMessage, myTransaction);
//提交事务
myTransaction.Commit();
Console.WriteLine("消息发送成功!");
}
catch (ArgumentException e)
{
Console.WriteLine(e.Message);
}
}

读取消息的示意代码:

   ///<summary>
/// 连接消息队列并从队列中接收消息
///</summary>
public static void ReceiveMessage()
{
//连接到本地队列
MessageQueue myQueue = new MessageQueue(".\\private$\\myQueueTrans");
myQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
try
{
if (myQueue.Transactional)
{
MessageQueueTransaction myTransaction = new MessageQueueTransaction();
//启动事务
myTransaction.Begin();
//从队列中接收消息
Message myMessage = myQueue.Receive(myTransaction);
string context = myMessage.Body as string; //获取消息的内容
myTransaction.Commit();
Console.WriteLine("消息内容为:" + context);
}
}
catch (MessageQueueException e)
{
Console.WriteLine(e.Message);
}
catch (InvalidCastException e)
{
Console.WriteLine(e.Message);
}
}
}

三、异步消息处理

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

在MSMQ中异步接收消息使用BeginReceive方法和EndReceive方法来标记操作的开始和结束,异步查看消息则使用BeginPeek和EndPeek两个方法来标记异步读取的开始和结束。同时,异步接收和查看消息还可以指定超时时间,关于这点我在后续文章里再做详细的介绍,有兴趣的朋友可以关注。       这里我将使用过的Book类来作为消息传输,没阅读过的朋友请先阅读这篇文章,了解Book类的结构。下面提供了一个示例,创建队列和发送消息到队列在前面我们已经使用多次了这里就不贴代码了,发送消息这里与第一篇文章中介绍如何发送一个复杂的类型信息到队列比,只是多了事务处理,详细如下:

 ///<summary>
/// 发送消息到队列
///</summary>
private static void SendMessage()
{
MessageQueue myQueue = new MessageQueue(".\\private$\\myAsyncQueue");
if (myQueue.Transactional)
{
Book book = new Book();
book.BookId = 1001;
book.BookName = "ASP.NET";
book.BookAuthor = "ZhangSan";
book.BookPrice = 88.88;
Message message = new Message(book);
message.Formatter = new XmlMessageFormatter(new Type[] { typeof(MSMQ.Async.Book) });

MessageQueueTransaction myTransaction = new MessageQueueTransaction();
myTransaction.Begin();
myQueue.Send(message, myTransaction);
myTransaction.Commit();
Console.WriteLine("消息成功发送到队列!");
}
}
 ///<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;
Console.WriteLine("图书编号:{0}--图书名称:{1}--图书作者:{2}--图书定价:{3}",
book.BookId.ToString(),
book.BookName,
book.BookAuthor,
book.BookPrice.ToString());
myQueue.BeginReceive();
}
catch (MessageQueueException me)
{
Console.WriteLine("异步接收出错,原因:" + me.Message);

}
}
 

四、MSMQ在邮件发送程序中的应用

       在邮件程序的应用中,实现发送邮件的方法有很多种,很多朋友都比较喜欢邮件发送组件(如:JMail),在.NET Framework里也给我们提供了发送邮件的类StmpClient,位于System.Net下。我想很多朋友都曾用到过此类,如果说是在一个小型的邮件应用里,完全没有使用MSMQ的必要,因为数据量不大,直接处理就OK,这里我以邮件程序来说只是出于对MSMQ应用的介绍。说实在的,我也不知道怎么才能把这个东东给介绍清楚,先看看一张图吧!                                现在的需求是这样的,有一个邮件发送客户端(SendMail.aspx,界面效果如上图所示)和一个邮件发送管理的服务端(MailServer.aspx),当在SendMail.aspx里发送邮件的时候,我们不直接将其发到目标地址去,而是将其发送到消息队列,然后由MailServer.aspx来负责从消息队列里读取出邮件信息在将其发送到目标地址。其实SendMail.aspx的职责就是完成把邮件信息发送到消息队列,示意性代码如下: 

定义实体类:

///<summary>
/// MailInfo 的摘要说明
///</summary>
public class MailInfo
{
public MailInfo()
{

}

private string _Title;
public string Title
{
get { return _Title; }
set { _Title = value; }
}

private string _Content;

public string Content
{
get { return _Content; }
set { _Content = value; }
}
private string _Sender;

public string Sender
{
get { return _Sender; }
set { _Sender = value; }
}
private string _SenderPwd;

public string SenderPwd
{
get { return _SenderPwd; }
set { _SenderPwd = value; }
}
private string _StmpServer;

public string StmpServer
{
get { return _StmpServer; }
set { _StmpServer = value; }
}
private string _ReceiveAddress;

public string ReceiveAddress
{
get { return _ReceiveAddress; }
set { _ReceiveAddress = value; }
}
}

实例化对象:

  protected void btnSendMail_Click(object sender, EventArgs e)
{
//取出数据存入MailInfo对象
MailInfo info = new MailInfo();
info.Title = tbTitle.Text;
info.Content = tbContent.Text;
info.StmpServer = tbSmtpServer.Text;
info.Sender = tbSender.Text;
info.SenderPwd = tbSenderPwd.Text;
info.ReceiveAddress = tbReceive.Text;

if (info != null)
{
CreateQueue();
SendMessage(info);
}
}

创建并发送消息队列:

 ///<summary>
/// 通过Create方法创建使用指定路径的新消息队列
///</summary>
private void CreateQueue()
{
try
{
if (!MessageQueue.Exists(".\\myMailQueue"))
{
MessageQueue.Create(@".\private$\myMailQueue", true);
}
}
catch (MessageQueueException e)
{
this.tdError.InnerText = e.Message;
}
}

///<summary>
/// 连接消息队列并发送消息到队列
///</summary>
private void SendMessage(MailInfo info)
{
try
{
//连接到本地的队列
MessageQueue myQueue = new MessageQueue(".\\private$\\myMailQueue");

Message myMessage = new Message();
myMessage.Body = info;
myMessage.Formatter = new XmlMessageFormatter(new Type[] { typeof(MailInfo) });

MessageQueueTransaction myTransaction = new MessageQueueTransaction();
//启动事务
myTransaction.Begin();
//发送消息到队列中
myQueue.Send(myMessage, myTransaction);
//提交事务
myTransaction.Commit();
this.tdSucces.InnerText = "消息发送成功!";
}
catch (ArgumentException e)
{
this.tdError.InnerText = "发送失败,失败原因:" + e.Message;
}
}

      转到邮件管理端(MailServer.aspx),他负责从消息队列里读取出邮件信息并把此邮件发送到目标地址去。其实现很简单,说直接点他也就是完成了两项操作(从队列读消息、将消息发送到目的邮箱),从队列读取消息的代码如下:

 ///<summary>
/// 连接消息队列并从队列中接收消息
///</summary>
private MailInfo ReceiveMessage()
{
MailInfo info = null;
//连接到本地队列
MessageQueue myQueue = new MessageQueue(".\\private$\\myMailQueue");
myQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(MailInfo) });
try
{
if (myQueue.Transactional)
{
MessageQueueTransaction myTransaction = new MessageQueueTransaction();
//启动事务
myTransaction.Begin();
//从队列中接收消息
Message myMessage = myQueue.Receive(myTransaction);
info = myMessage.Body as MailInfo; //获取消息的内容
myTransaction.Commit();
return info;
}

}
catch (MessageQueueException e)
{
this.tdError.InnerText = e.Message;
}
return info;
}


该方法(ReceiveMessage)返回的是从队列里读取到的邮件信息,本示例中只是做了读一条信息的实现,如果要把队列里的全部信息读出并发送到目的邮箱,可以参考我前面所介绍的相关知识点来实现。得到了邮件的详细信息,我们就可以使用相应的技术将这信息发送到目标邮箱去,本示例中我采用的是.NET Framework里提供的SmtpClient类来完成的邮件发送,关于SmtpClient类的使用网上有相当丰富的资料介绍,这里我就不做详细的说明,核心代码如下:

 protected void Button2_Click(object sender, EventArgs e)
{
if (info != null)
{
SmtpClient client = new SmtpClient();
client.Host = info.StmpServer;
client.UseDefaultCredentials = false;
client.Credentials = new NetworkCredential(info.Sender, info.SenderPwd);
client.DeliveryMethod = SmtpDeliveryMethod.Network;

MailMessage message = new MailMessage(info.Sender, info.ReceiveAddress);
message.Subject = info.Title;
message.Body = info.Content;
message.BodyEncoding = Encoding.UTF8;
message.IsBodyHtml = true;

try
{
//发送邮件到目标地址
client.Send(message);
this.tdSucces.InnerText = "邮件已成功发送到目标地址:" + info.ReceiveAddress;
}
catch (Exception ex)
{
this.tdError.InnerText = "发送失败,失败原因:" + ex.Message;
}
}

运行后的效果图如下:                                 本文中的所有示例程序全部通过调试,能力有限,文中所介绍的不是很清楚,详细可直接下载源代码了解。本代码包里也包含有第一篇文章里的全部示例程序代码。

点击连接下载示例程序代码:示例程序代码下载

本文摘自博客园:benniao老兄的日记...

http://www.cnblogs.com/beniao/archive/2008/06/28/1230311.html

原文地址:https://www.cnblogs.com/zhijianliutang/p/2248708.html