MSMQ消息隊列


message queue(微軟消息隊列)是在多個不同的應用之間實現相互通信的一種異步傳輸模式,相互通信的應用可以分布於同一台機器上,也可以分布於相連的網絡空間中的任一位置。它的實現原理是:消息的發送者把自己想要發送的信息放入一個容器中(我們稱之為message),然后把它保存至一個系統公用空間的消息隊列(message queue)中;本地或者是異地的消息接收程序再從該隊列中取出發給它的消息進行處理。

實質

  在消息傳遞機制中,有兩個比較重要的概念。一個是消息,一個是隊列。消息是由通信的雙方所需要傳遞的信息,它可以是各式各樣的媒體,如文本、聲音、圖象等等。消息最終的理解方式,為消息傳遞的雙方事先商定,這樣做的好處是,一是相當於對數據進行了簡單的加密,二則采用自己定義的格式可以節省通信的傳遞量。消息可以含有發送和接收者的標識,這樣只有指定的用戶才能看到只傳遞給他的信息和返回是否操作成功的回執。消息也可以含有時間戳,以便於接收方對某些與時間相關的應用進行處理。消息還可以含有到期時間,它表明如果在指定時間內消息還未到達則作廢,這主要應用與時間性關聯較為緊密的應用。

  消息隊列是發送和接收消息的公用存儲空間,它可以存在於內存中或者是物理文件中。消息可以以兩種方式發送,即快遞方式(express)和可恢復模式(recoverable),它們的區別在於,快遞方式為了消息的快速傳遞,把消息放置於內存中,而不放於物理磁盤上,以獲取較高的處理能力;可恢復模式在傳送過程的每一步驟中,都把消息寫入物理磁盤中,以得到較好的故障恢復能力。消息隊列可以放置在發送方、接收方所在的機器上,也可以單獨放置在另外一台機器上。正是由於消息隊列在放置方式上的靈活性,形成了消息傳送機制的可靠性。當保存消息隊列的機器發生故障而重新啟動以后,以可恢復模式發送的消息可以恢復到故障發生之前的狀態,而以快遞方式發送的消息則丟失了。另一方面,采用消息傳遞機制,發送方不必要再擔心接收方是否啟動、是否發生故障等等非必要因素,只要消息成功發送出去,就可以認為處理完成,而實際上對方可能甚至未曾開機,或者實際完成交易時可能已經是第二天了。

作用

  采用msmq帶來的好處是:由於是異步通信,無論是發送方還是接收方都不用等待對方返回成功消息,就可以執行余下的代碼,因而大大地提高了事物處理的能力;當信息傳送過程中,信息發送機制具有一定功能的故障恢復能力;msmq的消息傳遞機制使得消息通信的雙方具有不同的物理平台成為可能。

  在微軟的.net平台上利用其提供的msmq功能,可以輕松創建或者刪除消息隊列、發送或者接收消息、甚至於對消息隊列進行管理。

一、Windows 7安裝、管理消息隊列
1、安裝消息隊列

   執行用戶必須要有本地 Administrators 組中的成員身份,或等效身份。
   具體步驟:
   開始—》控制面板—》程序—》程序和功能—》打開或關閉Windows功能—》依次展開Microsoft Message Queue (MSMQ) 服務器、Microsoft Message Queue (MSMQ) 服務器核心—》確定
   如果系統提示您重新啟動計算機,請單擊“確定”以完成安裝。
2、管理消息隊列
   計算機—》右鍵—》管理—》服務和應用程序—》消息隊列。
二、Windows Server 2008安裝、管理消息隊列
1、安裝消息隊列

   開始—》控制面板—》管理工具—》服務器管理器—》功能—》添加功能—》依次展開MSM、MSMQ服務—》確定。
2、管理消息隊列
   計算機—》右鍵—》管理—》功能—》消息隊列。
三、創建、刪除和管理隊列
   要開發MSMQ程序就必須學習一個很重要的類(MessageQueue),該類位於名稱空間System.Messageing下。
常用方法:
   --Create()方法:創建使用指定路徑的新消息隊列。
   --Delete()方法:刪除現有的消息隊列。
   --Existe()方法:查看指定消息隊列是否存在。
   --GetAllMessages()方法:得到隊列中的所有消息。
   --GetPublicQueues()方法:在“消息隊列”網絡中定位消息隊列。
   --Peek()/BeginPeek()方法:查看某個特定隊列中的消息隊列,但不從該隊列中移出消息。
   --Receive()/BeginReceive()方法:檢索指定消息隊列中最前面的消息並將其從該隊列中移除。
   --Send()方法:發送消息到指定的消息隊列。
   --Purge()方法:清空指定隊列的消息。
常用屬性:
   --Priority:設置消息優先級,MessagePriority枚舉里全部進行了封裝,MessagePriority.High();
              AboveNormal:hight與Normal消息優先級之間;
              High:高級消息優先級;
              Highest:最高消息優先級;
              Low:低消息優先級;
              Lowest:最低消息優先級;
              Normal:普通消息優先級;
              VeryHigh:Highest和High消息優先級之間;
              VeryLow:Low和Lowest消息優先級之間;
四、發送和序列化消息
   MSMQ消息隊列中定義的消息由一個主體(body)和若干屬性構成。消息的主體可以由文本、二進制構成,根據需要還可以被加密。
   在MSMQ中消息的大小不能夠超過4MB。發送消息是通過Send方法來完成的,需要一個Message參數。
1、發送消息:
   步驟:連接隊列-->指定消息格式-->提供要發送的數據(主體)-->調用Send()方法將消息發送出去。詳細見后面的示例程序。
2、序列化消息:
   消息序列化可以通過.NET Framework附帶的三個預定義格式化程序來完成:
   --  XMLMessageFormatter對象----MessageQueue組件的默認格式化程序設置。
   --  BinaryMessageFormatter對象;
   --  ActiveXMessageFormatter對象;
   由於后兩者格式化后的消息通常不能為人閱讀,所以我們經常用到的是XMLMessageFormatter對象。該對象構造方法有三種重載:
   1、public XmlMessageFormatter();
   2、public XmlMessageFormatter(string[] targetTypeNames);
   3、public XmlMessageFormatter(Type[] targetTypes);
   如我們后面的示例程序中用到的序列化語句:
   //序列化為字符串
   XmlMessageFormatter formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
五、讀取和接收消息
1、讀取消息:
   也就是從指定隊列中獲取消息。
2、接收消息有兩種方式:
   --> 通過Receive()方法。
   --> 通過Peek()方法。

C# 添加引用System.Messaging,using System.Messaging;

簡單的小例子

創建程序MSMQ1及MSMQ2

using System;
using System.Messaging;
using System.Windows;

namespace MSMQ
{
    /// <summary>
    /// MainWindow.xaml 的交互邏輯
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            //新建消息循環隊列或連接到已有的消息隊列
            string path = ".\\private$\\killf";
            mq = MessageQueue.Exists(path) ? new MessageQueue(path) : MessageQueue.Create(path);
            mq.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
        }

        private MessageQueue mq;

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            mq.Send(this.textBox1.Text);
        }
    }
}

MSMQ2

using System;
using System.Messaging;
using System.Windows;

namespace MSMQ2
{
    /// <summary>
    /// MainWindow.xaml 的交互邏輯
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            //新建消息循環隊列或連接到已有的消息隊列
            string path = ".\\private$\\killf";
            mq = MessageQueue.Exists(path) ? new MessageQueue(path) : MessageQueue.Create(path);
            mq.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
            mq.ReceiveCompleted += mq_ReceiveCompleted;
            mq.BeginReceive();
        }
        MessageQueue mq;

        void mq_ReceiveCompleted(object sender, ReceiveCompletedEventArgs e)
        {
            //throw new NotImplementedException();
            MessageQueue mq = (MessageQueue)sender;
            System.Messaging.Message m = mq.EndReceive(e.AsyncResult);
            //處理消息
            string str = m.Body.ToString();
            this.Dispatcher.Invoke(new Action<string>(ShowMsg), str);

            //繼續下一條消息
            mq.BeginReceive();
        }
        private void ShowMsg(string msg)
        {
            this.textBlock1.Text += msg + Environment.NewLine;
            return;
        }
    }
}

運行結果

截圖20160929112602880截圖20160929112555409

 

其他前輩經驗

摘自 http://www.makaidong.com/%E5%8D%9A%E5%AE%A2%E5%9B%AD%E7%9F%A5%E8%AF%86%E5%BA%93/2779.shtml

發送:個人覺得發送問題比較重要的就是客戶端與遠程msmq服務器傳送msmq消息的問題,經過本人查資料發現遠程msmq通信必須配置ad和開放對應權限,因為比較麻煩,而且本人所在項目本身沒跨網絡的需求,所以沒有深入了解。其他os tcp等內網方式通信都ok, 不過還有一點值得注意的就是:與虛擬機的msmq通信未能成功

     接收: 這里先貼一下本人測試的接收代碼,這里是客戶端發送和服務器接收分開做的測試,服務器接收端放到各種場景(虛擬機、外網、內網本機、內網其他機器)測試

class program
    {
// define static class members.
static manualresetevent signal = new manualresetevent(false);
static int count = 0;
static void main(string[] args)
        {
string serverformatname = "formatname:direct=os:vis-pc\\private$\\msmqtest";
            console.writeline("msmq測試:");
try
            {
                messagequeue queue = new messagequeue(serverformatname,queueaccessmode.receive);
                queue.receivecompleted += new receivecompletedeventhandler(queue_receivecompleted);
                queue.beginreceive();
                signal.waitone();
            }
catch (messagequeueexception e)
            {
                console.writeline(e.tostring());
            }
return;
        }
//判斷成功接收后可更新隊列表狀態-需要隊列表提供接口
protected static void queue_receivecompleted(object source, receivecompletedeventargs e)
        {
try
            {
string result = string.empty;
if (source.equals(null))
                {
return;
                }        
                messagequeue mq = (messagequeue)source;
                message m = mq.endreceive(e.asyncresult);
//var query = m.body;
//result = (string)m.body;
                count += 1;
if (count == 10)
                {
                    signal.set();
                }
//可更新系統的隊列表狀態-證明信息接收成功
string filepath = @"e:\log.txt";
using (streamwriter sw = new streamwriter(filepath, true))
                {
                    string logmsg = string.format("[{0}]{1}", datetime.now.tostring(), "接收成功");
                    sw.writeline(logmsg);
                }
                mq.beginreceive();
            }
catch (messagequeueexception _mqe)
            {
                console.writeline(_mqe.tostring());
            }
return;
        }
    }

  這里參考了微軟官方的示例,加了線程等待等,而且模擬了監聽功能,但是接收中出現了接收不到主體信息內容等(描述信息能收到),需要強調的是發送和接收在本機測試都沒問題 把接收程序放在其他機器上會引發message對象boby屬性的:system.invalidoperationexception異常 經分析可能是權限問題

  同事找到msdn上這樣一篇文章:啟用安全的遠程讀取 http://technet.microsoft.com/zh-cn/library/cc737783(ws.10).aspx 在這里做過測試,但是未能成功,不知道是不是其他權限沒有給足:馬開東博客也有人碰到過類似問題 http://www.cnblogs.com/yjmyzz/archive/2007/12/04/982440.html 和給出解決思路,這里就不細說了。

     消息隊列是微軟對消息服務領域的開創性嘗試。它采用了特殊的通信機制,對改善和提供系統的可擴展性和高可用性具有重要意義:

     (1)對異步的消息發送方式和離線通信方式的支持

     (2)消息發送方和消息處理方可以完全分離

     (3)可靠的消息傳輸,消息隊列通過特殊的傳輸機制,比如消息確認、超時處理、消息日志以及死信隊列等,從分保證了消息的可靠傳輸

     (4)事物的支持,提供對本地事物和分布式事物的支持,可以可以把一個消息隊列的操作和一個基於sqlserver的操作納入同一事物中

     消息隊列按照可訪問性可分為兩種類型的隊列:

     (1)公共消息隊列,公共消息隊列發布於活動目錄ad並被復制windows域。因為可以在不知道隊列所在機器名稱的情況下對公共隊列進行檢索,因而將公共隊列從一台計算機移到另一台上,並不會對客戶端應用造成任何影響

     (2)私有消息隊列,私有消息隊列一般在沒有ad的工作組環境中使用,它們不支持身份驗證,並且需要隊列所在的計算機名稱方能定位

     最終拋棄了framework的msmq操作,使用wcf的msmq操作測試成功。wcf的可靠性會話是基於ws-rm標准的,而msmq只是ms自家的

     wcf下基於消息隊列的url具有net.msmq前綴。net.msmq地址中必須要指明隊列的類型


 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM