1. 程式人生 > >【架構之路(分散式三連篇)】--MQ

【架構之路(分散式三連篇)】--MQ

引言

        接下來的三篇文章是討論有關企業分散式開發的文章,這三篇文章籌劃了很長時間,文章的技術並不算新,但是文章中使用到的技術都是經過筆者研究實踐後總結的,正所謂站在巨人的肩膀上,筆者並不是巨人,但也希望這幾篇文章能夠幫助初涉企業分散式開發的一些童鞋。
        三篇文章將會從MessageQueue、Windows Services和WCF著手來討論企業分散式的開發,MQ是一種訊息中介軟體技術,該篇文章將會詳細討論。Windows Services在分散式開發中同樣起著重要的作用,將會在下篇文章中詳細討論,最後是使用MQ、WS並結合WCF做一個分散式的Demo來演示分散式的架構。在敲定寫這幾篇文章前還有一個重要的內容--ESB(Enterprise Service Bus,企業服務匯流排),它是傳統中介軟體技術與XML、Web服務等技術結合的產物,也就是集合了MQ和WS為一體的一種框架,但還沒有做詳細的研究所以這三篇文章就沒有包括ESB,等到有充足的時間瞭然後再去詳細的研究吧。
         

一、 論新技術的學習

         在使用到新技術時往往首先需要學習它,然後在專案中應用,這裡說到了學習那麼就來討論下技術學習的方法,也是筆者對新技術學習的一種總結。
         對於做舊了開發的人員來說在學習新技術時往往會比年輕的開發人員較快,這個問題有沒有想過?其實這個問題的答案相當的簡單,在學習新技術時對於不同的人都是站在同一個起跑線的,只不過對於有經驗的開發人員來說,在使用新技術時學習的並不是它是什麼東西,而是學習的如何使用它。這種不同的思想觀點就決定了誰會掌握的更快,可以這麼理解,開發是一個世界,剛踏入程式設計界的人來說就像是剛出生的一個嬰兒,這時候他們是要去認知這個世界,於是就會問很多問題,諸如:面向物件是什麼東西,為什麼要這樣編寫,最後把自己陷入到一個個泥潭中。但是對於經驗豐富的開發人員來說,他要認知不是這個世界,在開發中可以說已經是成年人,在學習新技術時就不會問很愚蠢的問題,而是會想這個東西也是面向物件的,那麼可以把方法封裝到一個基類中,子類繼承父類的方法,然後重寫來實現多型,所以這時候經驗就決定了學習新技術時的快慢。
         這就類似於生活中的幼兒和成年人在學習開電動車時的場景,想要幼兒使用電動車就會很困難,因為最簡單的腳踏車都沒有騎過,你讓他學習電動車,這不是作死的節奏嗎。但是成年人就不同了,成年人騎腳踏車相當的熟練,在換電動車的時候就會想這個和腳踏車是一樣的,而且可以不用每次腳踩,真是好用。這兩種思維方式就決定了幼兒在使用電動車時需要幾天的事件才能學會,但是成年人剛看到就能夠使用。
          學習新技術也是類似,新技術也是隻是一種新的實現方式,可能給它加上了一個電動的開關,做一個開關然後使用它裡面的功能自個兒執行就可以了,實際的內容還是沒有改變都是0和1的集合體。


二、MQ

        上文討論了學習的方法,接下來將會進入文章的正題,討論有關MQ的基本使用方法。首先來對MQ的基本內容進行分類,這裡從靜態和行為角度將MQ的內容分為兩大類,其中的靜態角度是指MQ所包含的類別以及在系統訊息佇列中的型別,行為角度是指MQ在通訊方面的型別,具體分類如下圖:

        MQ是一種通訊的機制,因為是一種中介軟體技術,所以它能夠支援多種型別的語言開發,同時也是跨平臺的通訊機制,也就是說MQ支援將資訊轉化為XML或者JSon等型別的資料儲存到訊息佇列中,然後可以使用不同的語言來處理訊息佇列中的訊息,這樣就很容易的做到了資訊的通訊,同時也為資訊的通訊起到了緩衝的作用,經常會在金融專案中使用這種通訊機制。
 

  2.1 理論積澱

     2.1.1 靜態

       訊息佇列是和網路相關聯的,所以根據網路的不同劃分為公共佇列、專用佇列,其中公共佇列是公佈到整個網路中,在整個網路中所有站點公開,相對的就是專用佇列,專用佇列只能由知道佇列完整路徑名或標籤的應用程式訪問。另外根據接收訊息的操作不同把訊息佇列劃分為管理佇列和響應佇列,管理佇列是指在整個訊息線路中所有已經發送和接收的訊息;響應佇列是指目標程式接收和迴應訊息。它們在訊息佇列中的型別,可以使用下圖來進行區分。


          另外對於系統生成的佇列這裡不再詳細的描述,可以上網檢視一些文章。

     2.1.2 行為

        在行為方面從佇列的通訊和訊息處理兩個大的方面把MQ分為兩大類,其中MQ的佇列通訊分為同步和非同步兩種型別,同步訊息是指請求的傳送方執行其他任務前,必須等待來自預定接收方的響應,具體等待的時間取決於接收方處理響應的時間。和同步相對應的是非同步訊息,傳送方不會等待接收方的任何迴應即可繼續其它的操作。

        另外在訊息佇列互動方面,訊息的互動同時也有資料完整性、資料一致性、穩定性等型別的,具體分為穩定性、訊息優先順序、離線能力、事務性和安全性等。


   2.2 MQ 詳解

       理論部分已經積累的很多了,接下來我們從實際的程式碼例項中來分析MQ的使用方法,另外在使用MQ前首先應該要安裝MQ才可以,這樣在本機的伺服器上才會有MQ的管理器,做訊息處理的時候可以檢視訊息的具體內容。

     2.2.1 MQ安裝

       開啟Control Panel-“Add/Remove Programs” – “Add/Remove Windows Components”步驟安裝MSMQ。

       MSMQ可以安裝為工作組模式或域模式。如果安裝程式沒有找到一臺執行提供目錄服務的訊息佇列的伺服器,則只可以安裝為工作組模式,此計算機上的“訊息佇列”只支援建立專用佇列和建立與其他執行“訊息佇列”的計算機的直接連線。

    2.2.2 配置MSMQ

      開啟Computer Management – Message Queuing,在Private Queues下建立MSMQDemo佇列


   2.2.3 MQ Demo

        在.NET中微軟對MQ做了封裝,把MQ有關的資訊封裝到了MessageQueue類中,在開發的時候可以直接引用該類,對佇列中的訊息做操作。
        在操作訊息前首先要為訊息指定儲存的佇列,所以在建立訊息時首先要在伺服器上建立一個佇列,然後為MessageQueue指定訊息佇列的路徑。具體MQ類的方法和屬性如下導圖:


        MQ類的主要使用方法已經在上面的導圖中列出,接下來分析類中的主要方法和屬性。
        (1)在Method中分為佇列管理和訊息管理兩類方法,其中的佇列管理的方法很簡單,通過呼叫方法就能夠建立和刪除佇列。另外就是訊息管理的方法,其中的方法分為同步和非同步兩種型別,可以根據實際的需求來確定使用的型別。 
        (2)在Property中主要用到的主要是Path和Label屬性,其中的Path指定訊息的佇列地址,Label能設定或獲取佇列描述資訊。

         接下來演示傳送資料和接收資料程式碼的編寫方法,下面的示例中使用的是私有的佇列型別來演示的操作。首先從傳送資料開始,在傳送資料時首先要建立我們的MQ,然後根據MQ的地址建立相應的佇列,呼叫佇列的send方法將資料資訊傳送到佇列中,如下程式碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Messaging;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Xml;
using System.Xml.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace MsMQTest
{
    class Program
    {
        static void Main(string[] args)
        {
            //declare the MQ Path
            string ekQ= ".\\Private$\\EKTestQueue";

            //create the MQ if the MQ is not exist
            if (!MessageQueue.Exists(ekQ))
                MessageQueue.Create(ekQ);

            //create a new queue
            var queue = new MessageQueue(ekQ);

            for (int i = 0; i < 2; i++)
            {
                //create the model that want to send
                Test test=new Test();
                test.Name = "fdsfd";
                test.Sex = "cvx";
                //serialize the model
                string str = Program.xmlSerial(test);
                //send the model data to queue
                queue.Send("Test" + str);
                Console.WriteLine("Message sent {0} \n--------------", "Test" +str);
            }

            Console.Read();

            // MessageQueue.Delete(ekQ);
        }

        public static string xmlSerial<T>(T serializeClass)
        {
            string xmlString = string.Empty;
            XmlWriterSettings settings = new XmlWriterSettings();
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            StringBuilder xmlStringBuilder = new StringBuilder();
            using (XmlWriter writer = XmlWriter.Create(xmlStringBuilder))
            {
                serializer.Serialize(writer, serializeClass);
                xmlString = xmlStringBuilder.ToString();
            }

            return xmlString;
        }
    }

    public class Test
    {
        public string Name { get; set; }
        public string Sex { get; set; }
    }
}

         對應的生成結果如下圖:

         執行上面的程式碼後MQ將會把訊息傳送到相應的佇列中,這裡採用的是專有佇列所以會將訊息傳送到本地的佇列中,檢視訊息如下圖所示:
  


          執行上面的程式碼後會把訊息傳送到相應的訊息佇列中,這樣在訊息的傳送方和呼叫方之間就構建了一個相互鬆耦合的橋樑,它就是訊息佇列,接下來演示如何接收訊息佇列。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Messaging;
using System.Text;
using System.Threading;
namespace MsqueueReaderTest
{
    class Program
    {
        static void Main(string[] args)
        {
            string ekQ = ".\\Private$\\EKTestQueue";
            
            using (var queue = new MessageQueue(ekQ))
            {
                queue.Formatter = new XmlMessageFormatter(new Type[] { typeof(String) });
                var exist = false;
                while (!MessageQueue.Exists(ekQ))
                {
                    Console.WriteLine("No existing queue");
                        
                }
                exist = true;
                while (exist)
                {
                    var m = queue.Receive();
                    Console.WriteLine("Message Received {0} \n--------------",(string)m.Body);
                    // Thread.Sleep(500);
                }
            }
            
        }
    }
}

       執行程式後控制檯輸出結果:



         在上例中我們使用的非同步Receive方法,而且該方法會將訊息佇列中的訊息取出並刪除,所以在操作完成後訊息佇列中的訊息會為空,所以執行後的佇列為下圖:



結語


         MQ是一種企業服務的訊息中間節技術,這種技術常常伴隨著企業服務匯流排相互使用,構成了企業分散式開發的一部分,如果考慮到訊息的傳送和傳送之間是可以相互不聯絡的並且需要分散式架構,則可以考慮使用MQ做訊息的中間價技術,MQ的功能已經足夠開發使用。但是對於分散式開發的技術只瞭解MQ是遠遠不足夠的,因為在系統中往往會實現訊息的自動傳送和獲取,如果想要實現訊息佇列的自動操作就不得不說說Windows服務了,下文將會對Windows服務做詳細的討論。