ASP.NET中进行消息处理(MSMQ) 二
在我上一篇文章《ASP.NET中進行消息處理(MSMQ)一》里對MSMQ做了個通俗的介紹,最后以發送普通文本消息和復雜的對象消息為例介紹了消息隊列的使用。?本文在此基礎上繼續介紹MSMQ的相關知識點,最后還是通過一個示例程序來分析MSMQ在實際項目開發中的應用。
建議:如果你對MSMQ不夠了解,在你閱讀本文前請先閱讀第一部分:《ASP.NET中進行消息處理(MSMQ)一》。一、消息傳遞的優先級
????? 在MSMQ中消息在隊列里傳輸是分有優先級的,這里我就以實例的形式介紹下關于優先級的使用,優先級一共有七種,MessagePriority枚舉里全部進行了封裝。因這里只作程序演示就不一一列舉出,僅用了Highest和Normal兩種類型,關于消息隊列上進行消息傳輸的七種優先級大家可以參考我下面提供的MessagePriority枚舉源代碼定義。
????? 那么在發送消息的時候怎么指定消息的優先級呢?在Message對象里封裝有一個屬性Priority,接受一個枚舉MessagePriority類型的值來設置消息傳輸的優先級。如下:
2message.Priority?=?MessagePriority.Highest;??//最高消息優先級
??????下面來看看一個在消息傳輸中使用優先級的示例程序,通過示例程序會學習得更明白。示例程序界面:
??????根據界面可知,提供了消息名字,消息優先級和消息內容三個輸入項,前面曾經說過,這里為了方便演示就僅用了Highest和Normal兩種類型,當點擊發送消息的時候就通過是否選擇優先級來設置消息的優先級,代碼如下: ?1private?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)一》里已經作了詳細的介紹,這里就不在多說,看看下面的代碼: ?1private?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。
1private?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//?????指定消息隊列在消息傳遞到隊列的過程中應用于該消息的優先級,以及指定何時將消息插入目標隊列。
?3public?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//創建普通的專用消息隊列
2MessageQueue?myMessage?=?MessageQueue.Create(@".\private$\myQueue");
3//創建事務性的專用消息隊列
4MessageQueue?myTranMessage?=MessageQueue.Create(@".\private$\myQueueTrans",?true);
????? 啟動了事務,那么在發送和接收消息的時候肯定是與原來有一定的差別的,這里我就不做詳細介紹,下面給出示意性代碼,有興趣的朋友可以直接下載本文示例程序代碼了解更多。
普通的消息發送示意性代碼:
2MessageQueue?myQueue?=?new?MessageQueue(".\\private$\\myQueue");
3Message?myMessage?=?new?Message();
4myMessage.Body?=?"消息內容";
5myMessage.Formatter?=?new?XmlMessageFormatter(new?Type[]?{?typeof(string)?});
6//發送消息到隊列中
7myQueue.Send(myMessage);
啟動了事務后的消息發送示意性代碼:
?1//連接到本地的隊列?2MessageQueue?myQueue?=?new?MessageQueue(".\\private$\\myQueueTrans");
?3
?4Message?myMessage?=?new?Message();
?5myMessage.Body?=?"消息內容";
?6myMessage.Formatter?=?new?XmlMessageFormatter(new?Type[]?{?typeof(string)?});
?7
?8MessageQueueTransaction?myTransaction?=?new?MessageQueueTransaction();
?9//啟動事務
10myTransaction.Begin();
11//發送消息到隊列中
12myQueue.Send(myMessage,?myTransaction);??//加了事務
13//提交事務
14myTransaction.Commit();
15Console.WriteLine("消息發送成功!");
讀取消息示意性代碼:
?2MessageQueue?myQueue?=?new?MessageQueue(".\\private$\\myQueueTrans");
?3myQueue.Formatter?=?new?XmlMessageFormatter(new?Type[]?{?typeof(string)?});
?4if?(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類的結構。下面提供了一個示例,創建隊列和發送消息到隊列在前面我們已經使用多次了這里就不貼代碼了,發送消息這里與第一篇文章中介紹如何發送一個復雜的類型信息到隊列比,只是多了事務處理,詳細如下:
?2///?發送消息到隊列
?3///?</summary>
?4private?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}
異步接收消息:
?2///?異步接收消息
?3///?</summary>
?4private?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} ?1private?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????{
19????????Console.WriteLine("異步接收出錯,原因:"?+?me.Message);
20
21????}
22}
四、MSMQ在郵件發送程序中的應用
??? ?在郵件程序的應用中,實現發送郵件的方法有很多種,很多朋友都比較喜歡郵件發送組件(如:JMail),在.NET Framework里也給我們提供了發送郵件的類StmpClient,位于System.Net下。我想很多朋友都曾用到過此類,如果說是在一個小型的郵件應用里,完全沒有使用MSMQ的必要,因為數據量不大,直接處理就OK,這里我以郵件程序來說只是出于對MSMQ應用的介紹。說實在的,我也不知道怎么才能把這個東東給介紹清楚,先看看一張圖吧!
????? 現在的需求是這樣的,有一個郵件發送客戶端(SendMail.aspx,界面效果如上圖所示)和一個郵件發送管理的服務端(MailServer.aspx),當在SendMail.aspx里發送郵件的時候,我們不直接將其發到目標地址去,而是將其發送到消息隊列,然后由MailServer.aspx來負責從消息隊列里讀取出郵件信息在將其發送到目標地址。其實SendMail.aspx的職責就是完成把郵件信息發送到消息隊列,示意性代碼如下: ?1protected?void?btnSendMail_Click(object?sender,?EventArgs?e)
?2{
?3????//取出數據存入MailInfo對象
?4????MailInfo?info?=?new?MailInfo();
?5????info.Title?=?tbTitle.Text;
?6????info.Content?=?tbContent.Text;
?7????info.StmpServer?=?tbSmtpServer.Text;
?8????info.Sender?=?tbSender.Text;
?9????info.SenderPwd?=?tbSenderPwd.Text;
10????info.ReceiveAddress?=?tbReceive.Text;
11
12????if?(info?!=?null)
13????{
14????????CreateQueue();
15????????SendMessage(info);
16????}
17}
???? CreateQueue和SendMessage這兩個方法完成消息隊列的創建和發送信息到隊列,MailInfo類封裝的是郵件的詳細信息(既郵件的屬性),詳細定義如下:?
封裝了郵件的屬性
????? 轉到郵件管理端(MailServer.aspx),他負責從消息隊列里讀取出郵件信息并把此郵件發送到目標地址去。其實現很簡單,說直接點他也就是完成了兩項操作(從隊列讀消息、將消息發送到目的郵箱),從隊列讀取消息的代碼如下:
?2///?連接消息隊列并從隊列中接收消息
?3///?</summary>
?4private?MailInfo?ReceiveMessage()
?5{
?6????MailInfo?info?=?null;
?7????//連接到本地隊列
?8????MessageQueue?myQueue?=?new?MessageQueue(".\\private$\\myMailQueue");
?9????myQueue.Formatter?=?new?XmlMessageFormatter(new?Type[]?{?typeof(MailInfo)?});
10????try
11????{
12????????if?(myQueue.Transactional)
13????????{
14????????????MessageQueueTransaction?myTransaction?=?new?MessageQueueTransaction();
15????????????//啟動事務
16????????????myTransaction.Begin();
17????????????//從隊列中接收消息
18????????????Message?myMessage?=?myQueue.Receive(myTransaction);
19????????????info?=?myMessage.Body?as?MailInfo;?//獲取消息的內容
20????????????myTransaction.Commit();
21????????????return?info;
22????????}
23
24????}
25????catch?(MessageQueueException?e)
26????{
27????????this.tdError.InnerText?=?e.Message;
28????}
29????return?info;
30}
????? 該方法(ReceiveMessage)返回的是從隊列里讀取到的郵件信息,本示例中只是做了讀一條信息的實現,如果要把隊列里的全部信息讀出并發送到目的郵箱,可以參考我前面所介紹的相關知識點來實現。得到了郵件的詳細信息,我們就可以使用相應的技術將這信息發送到目標郵箱去,本示例中我采用的是.NET Framework里提供的SmtpClient類來完成的郵件發送,關于SmtpClient類的使用網上有相當豐富的資料介紹,這里我就不做詳細的說明,核心代碼如下:
?2{
?3????if?(info?!=?null)
?4????{
?5????????SmtpClient?client?=?new?SmtpClient();
?6????????client.Host?=?info.StmpServer;
?7????????client.UseDefaultCredentials?=?false;
?8????????client.Credentials?=?new?NetworkCredential(info.Sender,?info.SenderPwd);
?9????????client.DeliveryMethod?=?SmtpDeliveryMethod.Network;
10
11????????MailMessage?message?=?new?MailMessage(info.Sender,?info.ReceiveAddress);
12????????message.Subject?=?info.Title;
13????????message.Body?=?info.Content;
14????????message.BodyEncoding?=?Encoding.UTF8;
15????????message.IsBodyHtml?=?true;
16
17????????try
18????????{
19????????????//發送郵件到目標地址
20????????????client.Send(message);
21????????????this.tdSucces.InnerText?=?"郵件已成功發送到目標地址:"?+?info.ReceiveAddress;
22????????}
23????????catch?(Exception?ex)
24????????{
25????????????this.tdError.InnerText?=?"發送失敗,失敗原因:"?+?ex.Message;
26????????}
27????}
28}
運行后的效果圖如下:
????? 本文中的所有示例程序全部通過調試,能力有限,文中所介紹的不是很清楚,詳細可直接下載源代碼了解。本代碼包里也包含有第一篇文章里的全部示例程序代碼。
點擊連接下載示例程序代碼:示例程序代碼下載
總結
以上是生活随笔為你收集整理的ASP.NET中进行消息处理(MSMQ) 二的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 几点汽车常见故障判断和自查!
- 下一篇: ASP.NET中进行消息处理(MSMQ)