MSMQ消息隊列,包括遠程訪問

    以前的項目用到了隊列,如今總結一下,下面有很是詳細的DEMO,但願能對有須要的人提供幫助。安全

    使用場景:在項目中,將一些無需即時返回且耗時的操做提取出來,進行了異步處理,而這種異步處理的方式大大的節省了服務器的請求響應時間,從而提升了系統的吞吐量。服務器

    個人需求很簡單,就是多個客戶端鏈接到個人一個小型的數據轉發服務器上,開始使用的是Socket通訊實現這個功能,一旦數據服務器接收到來自不一樣客戶端發來的消息,就對這些消息進行處理(我這裏是將數據接收到後再轉發到另外一個服務器上),但考慮到客戶端是每隔一個很短的時間週期向服務器發送信息,而且鏈接客服端數量比較多的時候,擔憂會產生併發訪問的問題,也但願避免 數據轉發服務器 頻繁地從多個不一樣線程獲取信息而出現其餘未知問題,因此在處理客戶端向數據轉發服務器發送信息的時候採起隊列的方式。併發

    通常狀況下,使用MSMQ,首先要安裝消息服務,跟安裝IIS一個套路,打開啓用或關閉Windows功能窗口,找到並勾選MSMQ消息服務,而後點擊肯定進行安裝,還不明白的百度一下;異步

    在VS裏添加 Messaging引用,就可使用MessageQueue這個類了;接下來就要思考清楚你的數據(消息)的流向問題,以前由於本身對隊列的錯誤認識,對到底在哪建立隊列,隊列的消息又由誰去發送和接收沒有弄清除,還有參考的一些寫得不是太清晰地博文,繞了好大一圈,因此今天在這裏以我本身的項目需求爲例子,說明 一、如何建立隊列 二、如何向隊列發送消息 三、 如何獲取隊列中的消息函數

   首先、建立隊列:根據個人需求,我要經過Socket通訊將信息發送至數據轉發服務器,所以爲了不併發訪問問題的產生,消息隊列應當創建在數據轉發服務器上;spa

        System.Messaging.MessageQueue myQuere = null;

        /// <summary>
        /// 這樣就在數據轉發服務器端建立了一個名爲queuedemo的消息隊列;
        /// 從客戶端要發送的消息就保存在這個隊列裏,
        /// 你能夠經過計算機管理->服務和應用下的消息隊列中看到你建立的queuedemo隊列,
        /// private$關鍵字是說明隊列爲專用隊列,
        /// 若是沒有這個關鍵字還要配置域服務器,仍是挺麻煩,這個仍是藉助百度吧,
        /// 前面的「.」表明建立的隊列目錄是本機
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnQueueInit_Click(object sender, EventArgs e)
        {
            // 注意這裏是$符號
            string queuePath = @".\private$\quereDemo";
            // 判斷消息隊列示例是否存在
            if (!System.Messaging.MessageQueue.Exists(queuePath))
            {
                // 不存在則建立一個消息隊列
                myQuere = System.Messaging.MessageQueue.Create(queuePath);
            }

            myQuere = new System.Messaging.MessageQueue(queuePath);

        }

 

    這樣就在數據轉發服務器端建立了一個名爲queuedemo的消息隊列;從客戶端要發送的消息就保存在這個隊列裏,你能夠經過計算機管理->服務和應用下的消息隊列中看到你建立的queuedemo隊列,private$關鍵字是說明隊列爲專用隊列,若是沒有這個關鍵字還要配置域服務器,仍是挺麻煩,這個仍是藉助百度吧,前面的「.」表明建立的隊列目錄是本機,這個隊列一旦建立成功,就是系統的事了,接下來要作的就是你怎麼去把消息寫進這個隊列,或者讀取隊列的值 線程

這裏要特別注意不要將queuepath路徑字符串寫成3d

string queuePath = @"FormatName:Direct=TCP:192.168.1.153\private$\quereDemo";

 

這樣寫的話是用於遠程計算機對這個隊列進行訪問的,由於MessageQueue的Create()和Exisit()方法是沒辦法去識別上述FormatName格式的,還有要確保Create()函數要被執行了以後再用MessageQueue實例去引用;這樣服務器端隊列的建立就完成了;code

     在客戶端中,向隊列發送信息;orm

    

        /// <summary>
        /// 寫入數據到消息隊列
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnWriteMessage_Click(object sender, EventArgs e)
        {
            // 要往隊列裏寫入的消息
            // 要求發送的對象要以序列化的方式寫進去,因此要設置formatter,這裏用的是XmlMessageFormatter 還有BinaryMessageFormatter等等
            string s = "客戶端往隊列裏發送的消息";
            // 實例化一個消息隊列Object
            System.Messaging.Message message = new System.Messaging.Message();
            message.Body = s;   // body 爲object類型
            message.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });    // 選擇xml的方式進行傳送消息

            // 建立輸送消息的隊列對象(在客戶端和消息隊列服務器分離的時候須要建立實例)
            // System.Messaging.MessageQueue myQuere = new System.Messaging.MessageQueue(@"FormatName:Direct=TCP:192.168.1.153\private$\queuedemo");
            myQuere.Send(message);

        }

 

在客戶端中,用一個MessageQueue實例指向服務器本機上建立的隊列路徑,這時,MessageQueue實例的構造函數裏的路徑就必定要用FormatName格式,指明是TCP通訊仍是HTTP仍是Machine如我上面代碼所示,而後調用Send()方法,將消息寫進隊列,這個要求發送的對象要以序列化的方式寫進去,因此要設置formatter,這裏用的是XmlMessageFormatter 還有BinaryMessageFormatter等等 注意保存你消息的 消息體Body是Object類型的 所以能夠將你寫的任何一個類的對象發送至消息隊列

    在服務器中接收消息隊列

        /// <summary>
        /// 開始讀取消息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnReadMessage_Click(object sender, EventArgs e)
        {
            // 實例化消息隊列
            System.Messaging.MessageQueue msgQuere = new System.Messaging.MessageQueue(@".\private$\quereDemo");
            // 指定寫入客戶端的序列化方式
            msgQuere.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
            // 開啓線程讀取
            // 此處也能夠寫成 Thread thread = new Thread(()=> { });
            System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(() =>
            {
                // 此處使用無限讀取,讀取完了立刻while,也能夠用一個時間來間隔
                while (true)
                {
                    //接收 System.Messaging.MessageQueue 引用的隊列中可用的第一條消息。此調用是同步的,在有可用消息前,它將一直阻止當前線程的執行。
                    System.Messaging.Message msg = msgQuere.Receive();
                    if (null != msg)
                    {
                        MessageBox.Show(msg.Body.ToString());
                    }
                }
            }));

            // 啓動線程
            thread.IsBackground = true; // 設置爲後臺線程
            thread.Start();
        }

 

在本機上能夠新建立一個隊列實例指向本機的隊列,而後按照以前約定的序列化格式反序列化消息體因此將新的隊列實例的foarmatter屬性賦值爲發送時的formatter屬性如代碼所示,這個時候就直接用Receive()獲得消息體,而後對消息體裏的信息作處理,我這裏是開啓一個線程顯示隊列的消息,只要有新的消息寫入,我就在消息框中輸出

 

    這個時候可能客戶端沒法向遠程服務器成功發送消息,緣由基本權限問題 服務器的消息隊列的權限沒有對未驗證的客戶端開放  你要在服務器隊列裏分配對應權限 若是你想讀取隊列的內容 還須要加系統變量

 

 問題解決辦法

1. 服務器端(dos:compmgmt.msc)

    • 服務器上消息隊列權限設置:給ANONYMOUS LOGON賦予全部權限;

    • 修改服務器的註冊表,容許非驗證客戶端訪問

 

這樣客戶端就能夠讀取服務器裏的隊列信息了 固然通常業務邏輯上不這麼作 由於他只負責發送消息 ,綜上,就是使用消息隊列 跨服務器讀寫的 最基本的用法    

相關文章
相關標籤/搜索