MSMQ的基本理論知識
MSMQ(MicroSoft Message Queue,微軟訊息佇列)是在多個不同的應用之間實現相互連信的一種非同步傳輸模式,相互連信的應用可以分佈於同一台機器上,也可以分佈於相連的網路空間中的任一位置。
它的實現原理是:訊息的寄件者把自己想要發送的資訊放入一個容器中(我們稱之為Message),然後把它儲存至一個系統公用空間的訊息佇列(Message Queue)中;本地或者是異地的訊息接收程式再從該隊列中取出發給它的訊息進行處理。
在訊息傳遞機制中,有兩個比較重要的概念。一個是訊息,一個是隊列。訊息是由通訊的雙方所需要傳遞的資訊,它可以是各式各樣的媒體,如文本、聲音、圖象等等。訊息最終的理解方式,為訊息傳遞的雙方事先商定,這樣做的好處是,一是相當於對資料進行了簡單的加密,二則採用自己定義的格式可以節省通訊的傳遞量。隊列是發送和接收訊息的公用儲存空間,它可以存在於記憶體中或者是物理檔案中。
隊列類型(Queue Type)
由您或網路中的其他使用者建立的隊列和系統隊列。使用者建立的隊列可能是以下任何一種隊列:
“公用隊列”在整個“訊息佇列”網路中複製,並且有可能由網路連接的所有網站訪問。
“專用隊列”不在整個網路中發布。相反,它們僅在所駐留的本機電腦上可用。專用隊列只能由知道隊列的完整路徑名或標籤的應用程式訪問。
“管理隊列”包含確認在給定“訊息佇列”網路中發送的訊息回執的訊息。指定希望 MessageQueue 組件使用的管理隊列(如果有的話)。
“響應隊列”包含目標應用程式接收到訊息時返回給發送應用程式的響應訊息。指定希望 MessageQueue 組件使用的響應隊列(如果有的話)。
系統產生的隊列一般分為以下幾類:
“日記隊列”可選地儲存發送訊息的副本和從隊列中移除的訊息副本。每個“訊息佇列”用戶端上的單個日記佇列儲存體從該電腦發送的訊息副本。在伺服器上為每個隊列建立了一個單獨的日記隊列。此日記跟蹤從該隊列中移除的訊息。
“無效信件佇列”儲存無法傳遞或已到期的訊息的副本。如果到期或無法傳遞的
訊息是異動訊息,則被儲存在一種特殊的無效信件佇列中,稱為“交易式無法傳送的信件佇列”。
死信儲存在到期訊息所在的電腦上。有關逾時期限和到期訊息的更多資訊,請參見預設訊息屬性。
“報告隊列”包含指示訊息到達目標所經過的路由的訊息,還可以包含測試訊息。
每台電腦上只能有一個報告隊列。
“專用系統隊列”是一系列儲存系統執行訊息處理操作所需的管理和通知訊息的專用隊列。 在應用程式中進行的大多數工作都涉及訪問公用隊列及其訊息。但是,根據應用程式的日記記錄、確認和其他特殊處理需要,在日常操作中很可能要使用幾種不同的系統隊列。
採用MSMQ帶來的好處是:由於是非同步通訊,無論是發送方還是接收方都不用等待對方返回成功訊息,就可以執行餘下的代碼,因而大大地提高了事物處理的能力;當資訊傳送過程中,資訊發送機制具有一定功能的故障恢複能力;MSMQ的訊息傳遞機制使得訊息通訊的雙方具有不同的物理平台成為可能。在微軟的.net平台上利用其提供的MSMQ功能,可以輕鬆建立或者刪除訊息佇列、發送或者接收訊息、甚至於對訊息佇列進行管理。
一、MSMQ介紹和安裝訊息佇列
使用訊息佇列的優點:穩定、訊息優先順序、離線能力以及安全性。
我是Windows XP,看所示(myQueue為自己建立的訊息佇列,msmqtriggersnotifiations為通用隊列):
對訊息佇列有了簡單的瞭解後,使用MSMQ進行軟體開發需要安裝MSMQ,安裝完後就該進入實際的開發階段。具體的安裝過程就是在控制台裡“添加/刪除程式”下“添加/刪除Windows組件”,完成添加就OK。安裝完成後就可以通過互動界添加新的訊息佇列,詳細如:
除了上面這種互動介面來建立MSMQ外,也可以通過編程來完成,.NET架構裡的MessageQueue類下有一靜態方法Create,用來完成訊息佇列的建立,其定義如下:
1//
2// 摘要:
3// 在指定的路徑中建立非事務性“訊息佇列”隊列。
4//
5// 參數:
6// path:
7// 要建立的隊列的路徑。
8//
9// 返回結果:
10// 表示新隊列的 System.Messaging.MessageQueue。
11public static MessageQueue Create(string path);
12//
13// 摘要:
14// 在指定的路徑中建立事務性或非事務性“訊息佇列”隊列。
15//
16// 參數:
17// transactional:
18// 如果建立交易式佇列,為 true;如果建立非交易式佇列,則為 false。
19//
20// path:
21// 要建立的隊列的路徑。
22//
23// 返回結果:
24// 表示新隊列的 System.Messaging.MessageQueue。
25public static MessageQueue.Create(string path, bool transactional);
使用程式建立簡單的訊息佇列如下:
using System.Messsagin
string queuename =@".\private$\myQueue" //隊列名稱
MessageQueue q;
if (MessageQueue.Exists(queuename))
{
q = new System.Messaging.MessageQueue(queuename);
}
else
{
q = MessageQueue.Create(queuename);
}
Message m = new Message();
m.Label = "MessageLabel"; //描述訊息的字串
m.Body = "MessageBody"; //訊息的主體
q.Send(m); //發送我們執行這段代碼,就可以在電腦管理->服務和應用程式->訊息佇列中多了一個“queuename”隊列,這個就是我們用程式建立的訊息.
二、建立、刪除和管理隊列
要開發MSMQ程式就必須學習一個很重要的類(MessageQueue),該類位於名稱空間System.Messageing下。其中有幾個常用的方法必須掌握:
--Create方法:建立使用指定路徑的新訊息佇列。
--Delete方法:刪除現有的訊息佇列。
--Existe方法:查看指定訊息佇列是否存在。
--GetAllMessages()方法:得到隊列中的所有訊息。
--GetPublicQueues方法:在“訊息佇列”網路中定位訊息佇列。
--Peek/BeginPeek方法:查看某個特定隊列中的訊息佇列,但不從該隊列中移出訊息。
--Receive/BeginReceive方法:檢索指定訊息佇列中最前面的訊息並將其從該隊列中移除。
--Send方法:發送訊息到指定的訊息佇列。
--Purge方法:清空指定隊列的訊息。
三、發送和序列化訊息
MSMQ訊息佇列中定義的訊息由一個主體(body)和若干屬性構成。訊息的主體可以由文本、二進位構成,根據需要還可以被加密。在MSMQ 中訊息的大小不能夠超過4MB。發送訊息是通過Send方法來完成的,需要一個Message參數。
1、發送訊息:
步驟:串連隊列-->指定訊息格式-->提供要發送的資料(主體)-->調用Send()方法將訊息發送出去。詳細見後面的樣本程式。
2、序列化訊息:
訊息序列化可以通過.NET Framework附帶的三個預定義格式化程式來完成:
-- XMLMessageFormatter對象----MessageQueue組件的預設格式化程式設定。
-- BinaryMessageFormatter對象;
-- ActiveXMessageFormatter對象;
由於後兩者格式化後的訊息通常不能為人閱讀,所以我們經常用到的是XMLMessageFormatter對象。該物件建構方法有三種重載:
1public XmlMessageFormatter(); 2public XmlMessageFormatter(string[] targetTypeNames); 3public XmlMessageFormatter(Type[] targetTypes); |
如我們後面的樣本程式中用到的序列化語句:
1//序列化為字串 2XmlMessageFormatter formatter = new XmlMessageFormatter(new Type[] { typeof(string) }); |
四、讀取和接收訊息
1、讀取訊息:
也就是從指定隊列中擷取訊息,詳細請查看本文前面的關於訊息作業的方法介紹。
2、接收訊息有兩種方式:
--> 通過Receive方法
--> 通過Peek方法
五、訊息使用執行個體
通過上面一系列的介紹,瞭解了MessageQueue類和常用的方法後,下面我們通過一個簡單的樣本程式來分析訊息佇列的建立、發送訊息以及接收訊息等相關知識點:
1、通過Create方法建立使用指定路徑的新訊息佇列
1/**//// <summary> 2/// 通過Create方法建立使用指定路徑的新訊息佇列 3/// </summary> 4/// <param name="queuePath"></param> 5public static void Createqueue(string queuePath) 6{ 7 try 8 { 9 if (!MessageQueue.Exists(queuePath)) 10 { 11 MessageQueue.Create(@".\private$\myQueue"); 12 } 13 else 14 { 15 Console.WriteLine(queuePath + "已經存在!"); 16 } 17 } 18 catch (MessageQueueException e) 19 { 20 Console.WriteLine(e.Message); 21 } 22} |
2、串連訊息佇列並發送訊息到隊列
1/**//// <summary> 2/// 串連消 息隊列並發送訊息到隊列 3/// </summary> 4public static void SendMessage() 5{ 6 try 7 { 8 //串連到本地的隊列 9 MessageQueue myQueue = new MessageQueue(".\\private$\\myQueue"); 10 11 Message myMessage = new Message(); 12 myMessage.Body = "訊息內容"; 13 myMessage.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) }); 14 //發送訊息到隊列中 15 myQueue.Send(myMessage); 16 } 17 catch (ArgumentException e) 18 { 19 Console.WriteLine(e.Message); 20 } 21} |
3、串連訊息佇列並從訊息佇列中接收訊息
1/**//// <summary> 2/// 串連訊息佇列並從隊列中接收訊息 3/// </summary> 4public static void ReceiveMessage() 5{ 6 //串連到本地隊列 7 MessageQueue myQueue = new MessageQueue(".\\private$\\myQueue"); 8 myQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) }); 9 try 10 { 11 //從隊列中接收訊息 12 Message myMessage = myQueue.Receive(); 13 string context = (string)myMessage.Body; //擷取訊息的內容 14 Console.WriteLine("訊息內容為:" + context); 15 } 16 catch (MessageQueueException e) 17 { 18 Console.WriteLine(e.Message); 19 } 20 catch (InvalidCastException e) 21 { 22 Console.WriteLine(e.Message); 23 } 24} |
4、串連隊列並清空隊列的全部訊息
1/**//// <summary> 2/// 清空指定隊列的訊息 3/// </summary> 4public static void ClearMessage() 5{ 6 MessageQueue myQueue = new MessageQueue(".\\private$\\myQueue"); 7 myQueue.Purge(); 8} |
5、串連隊列並擷取隊列的全部訊息
1/**//// <summary> 2/// 串連隊列並擷取隊列的全部訊息 3/// </summary> 4public static void GetAllMessage() 5{ 6 //串連到本地隊列 7 MessageQueue myQueue = new MessageQueue(".\\private$\\myQueue"); 8 Message[] message = myQueue.GetAllMessages(); 9 XmlMessageFormatter formatter = new XmlMessageFormatter(new Type[] { typeof(string) }); 10 for (int i = 0; i < message.Length; i++) 11 { 12 message[i].Formatter = formatter; 13 Console.WriteLine(message[i].Body.ToString()); 14 } 15} |
六、複雜訊息發送執行個體
通過上面一系列的介紹,對於簡單訊息的發送和接收及訊息的管理應該都不會有什麼問題了,下面我在介紹一下關於複雜的訊息處理,現在有這樣一個需求,要求通過訊息佇列將一本圖書資訊發送到隊列裡,然後從訊息佇列裡讀取出來。圖書的基本資料包括圖書編號、圖書名稱、圖書作者以及圖書定價,這樣的一個複雜的物件類型怎麼來傳輸呢?詳細如下:
1namespace MSMQ.App 2{ 3 public class MsgQueue 4 { 5 /**//// <summary> 6 /// 通過Create方法建立使用指定路徑的新訊息佇列 7 /// </summary> 8 /// <param name="queuePath"></param> 9 public static void Createqueue(string queuePath) 10 { 11 try 12 { 13 if (!MessageQueue.Exists(queuePath)) 14 { 15 MessageQueue.Create(@".\private$\myQueue"); 16 MessageBox.Show("建立隊列成功!"); 17 } 18 else 19 { 20 MessageBox.Show(queuePath + "已經存在!"); 21 } 22 } 23 catch (MessageQueueException e) 24 { 25 MessageBox.Show(e.Message); 26 } 27 } 28 29 /**//// <summary> 30 /// 串連訊息佇列並發送訊息到隊列 31 /// </summary> 32 public static bool SendMessage(Book book) 33 { 34 bool flag = false; 35 try 36 { 37 //串連到本地的隊列 38 MessageQueue myQueue = new MessageQueue(".\\private$\\myQueue"); 39 40 System.Messaging.Message myMessage = new System.Messaging.Message(); 41 myMessage.Body = book; 42 myMessage.Formatter = new XmlMessageFormatter(new Type[] { typeof(MSMQ.App.Book) }); 43 //發送訊息到隊列中 44 myQueue.Send(myMessage); 45 flag = true; 46 } 47 catch (ArgumentException e) 48 { 49 MessageBox.Show(e.Message); 50 } 51 return flag; 52 } 53 54 /**//// <summary> 55 /// 串連訊息佇列並從隊列中接收訊息 56 /// </summary> 57 public static string ReceiveMessage() 58 { 59 //串連到本地隊列 60 MessageQueue myQueue = new MessageQueue(".\\private$\\myQueue"); 61 myQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(MSMQ.App.Book) }); 62 try 63 { 64 //從隊列中接收訊息 65 System.Messaging.Message myMessage = myQueue.Receive(); 66 Book book = (Book)myMessage.Body; //擷取訊息的內容 67 return string.Format("編號:{0},書名:{1},作者:{2},定價:{3}", 68 book.BookId, 69 book.BookName, 70 book.BookAuthor, 71 book.BookPrice); 72 } 73 catch (MessageQueueException e) 74 { 75 MessageBox.Show(e.Message); 76 } 77 catch (InvalidCastException e) 78 { 79 MessageBox.Show(e.Message); 80 } 81 return null; 82 } 83 } 84} |
其實發送複雜的訊息也就是在訊息序列化上有些差別,別的地方與發送普通簡訊沒什麼大的變化,上面類裡提供了建立隊列,發送訊息到隊列,從隊列擷取訊息三個方法,測試結果如下:
去噹噹網買書,價格更低,送貨上門。