1. 程式人生 > >隊列工廠之(MSMQ)

隊列工廠之(MSMQ)

msmq 隊列 reposity rabbitmq redis

最近vs2017神器正式版發布讓人很是激動,vs2017支持了很多語言的開發,從前端-後端-底層的支持,堪稱是工具中的神器;netcore我喜愛的架構之一也得到了大力的宣傳,應群友的邀請將在隊列工廠(msmq,redis,rabbitmq)一些列文章過後,繼續增加.netcore方面的文章,只為.netcore發展更好貢獻一份微弱的力量;本章內容分享的是隊列(msmq,redis,rabbitmq)封裝的隊列工廠之MSMQ希望大家能夠喜歡,也希望各位多多"掃碼支持"和"推薦"謝謝!

創建隊列工廠QueueReposity<T>

  . 隊列公共操作接口IQueue

  . 配置文件操作類ConfClass<T>

  . 非安全單例創建隊列實例

Win7和Server2008安裝MSMQ支持

MSMQ測試用例(服務端+客戶端)

下面一步一個腳印的來分享:

創建隊列工廠QueueReposity<T>

首先,因為這裏需要統一封裝幾個常用的隊列方式的用法,因此采用了簡單工廠模式,所以有了QueueReposity<T>

. 隊列公共操作接口IQueue

工廠模式的特性創建實例,因為這裏封裝的都是隊列,故而能提取出統一的規則來,因此定義了如下接口(這裏沒有考慮一些隊列兼容的異步方法請忽略):

/// <summary>
    /// 隊列公共操作
    /// </summary>
    public interface IQueue : IDisposable
    {
        /// <summary>
        /// 創建隊列
        /// </summary>
        void Create();

        /// <summary>
        /// 總數
        /// </summary>
        /// <returns></returns>
        int Total();

        /// <summary>
        /// 讀取一個隊列
        /// </summary>
        /// <returns></returns>
        Message Read();

        ///// <summary>
        ///// 讀取多個隊列
        ///// </summary>
        ///// <returns></returns>
        //List<Message> ReadAll();

        /// <summary>
        /// 寫入隊列
        /// </summary>
        /// <returns></returns>
        bool Write(string content, string name = "");
    }

. 配置文件操作類ConfClass<T>

因為每個隊列的都有自己的配置信息,因此封裝了統一管理的配置文件讀取類ConfClass<T>,來讀取配置在同一個xml文件中的配置信息,如下封裝了自定義配置文件的屬性和讀取方法:

#region 文件操作類
        /// <summary>
        /// 配置文件操作類
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public class ConfClass<T> where T : class,new()
        {

            public ConfClass() {

                var apiNodeName = this.GetType().Name;
                Reader(apiNodeName);
            }

            #region 單例模式

            public static readonly object Singleton_Lock = new object();

            /// <summary>
            /// 單例對象
            /// </summary>
            private static T t = default(T);

            /// <summary>
            /// 通過方法獲取單例
            /// </summary>
            /// <param name="t"></param>
            /// <returns></returns>
            public static T GetInstance(T t)
            {
                t = t ?? new T();
                return t;
            }

            /// <summary>
            /// 通過屬性獲取單例(在繼承的時候使用)
            /// </summary>
            public static T Current
            {
                get
                {
                    t = t ?? new T();
                    return t;
                }
            }

            #endregion

            #region 配置文件操作

            #region  配置文件屬性
            /// <summary>
            /// 配置文件地址
            /// </summary>
            //public string ConfPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Conf", "ShenNiuApi.xml");
            public string ConfPath = @"C:\Conf\ShenNiuApi.xml";

            /// <summary>
            /// 配置文件父節點名稱
            /// </summary>
            public string ConfParentNodeName = "ShenNiuApi";

            /// <summary>
            /// 配置文件內容
            /// </summary>
            public string ConfContent { get; set; }

            /// <summary>
            /// 配置文件文檔doc對象
            /// </summary>
            public XmlDocument doc { get; set; }


            /// <summary>
            /// 賬號
            /// </summary>
            public string UserName { get; set; }

            /// <summary>
            /// 密碼
            /// </summary>
            public string UserPwd { get; set; }

            /// <summary>
            /// 接口地址
            /// </summary>
            public string ApiUrl { get; set; }

            /// <summary>
            /// 秘鑰
            /// </summary>
            public string ApiKey { get; set; }

            #endregion

            public ConfClass(string ConfPath, string ConfParentNodeName="")
            {

                this.ConfPath = string.IsNullOrWhiteSpace(ConfPath) ? this.ConfPath : ConfPath;
                this.ConfParentNodeName = string.IsNullOrWhiteSpace(ConfParentNodeName) ? this.ConfParentNodeName : ConfParentNodeName;

                var apiNodeName = this.GetType().Name;
                Reader(apiNodeName);
            }

            /// <summary>
            /// 讀取配置信息
            /// </summary>
            /// <param name="apiNodeName"></param>
            public void Reader(string apiNodeName)
            {
                try
                {
                    if (string.IsNullOrWhiteSpace(ConfPath) || string.IsNullOrWhiteSpace(ConfParentNodeName))
                    {
                        throw new Exception("配置文件地址或者配置文件父節點名稱不能為空");
                    }

                    if (!File.Exists(ConfPath)) { return; }

                    //獲取配置文件信息
                    using (StreamReader reader = new StreamReader(ConfPath))
                    {
                        this.ConfContent = reader.ReadToEndAsync().Result;
                    }

                    if (string.IsNullOrWhiteSpace(this.ConfContent)) { return; }

                    //加入doc中
                    this.doc = new XmlDocument();
                    this.doc.LoadXml(this.ConfContent);

                    //解析
                    var parentNode = string.Format("{0}/{1}", this.ConfParentNodeName, apiNodeName);
                    var apiNode = this.doc.SelectSingleNode(parentNode);
                    if (apiNode == null) { throw new Exception("未能找到" + parentNode + "節點"); }

                    this.UserName = apiNode.SelectSingleNode("UserName").InnerText;
                    this.UserPwd = apiNode.SelectSingleNode("UserPwd").InnerText;
                    this.ApiUrl = apiNode.SelectSingleNode("ApiUrl").InnerText;
                    this.ApiKey = apiNode.SelectSingleNode("ApiKey").InnerText;
                }
                catch (Exception ex)
                {

                    throw new Exception("加載配置文件" + this.ConfPath + "異常:" + ex.Message);
                }
            }
            #endregion
        }
        #endregion

這個配置文件的類主要運用在隊列實例繼承上,只要繼承了默認就會讀取響應的配置節點信息;配置xml文件默認存儲的地址: C:\Conf\ShenNiuApi.xml ,最大父節點名稱默認:ShenNiuApi,格式如下所示:

<ShenNiuApi>
    <QMsmq>
        <UserName></UserName>
        <UserPwd></UserPwd>
        <ApiUrl>.\Private$\MyMsmq</ApiUrl>
        <ApiKey></ApiKey>
    </QMsmq>
</ShenNiuApi>

. 非安全單例創建隊列實例

由於工廠都是專門用來提供實例的存在,創建實例的模式也有很多這種,這裏我選擇的是非安全單例創建隊列實例,所有在ConfClass類中默認加入了單例模式:

#region 單例模式

            public static readonly object Singleton_Lock = new object();

            /// <summary>
            /// 單例對象
            /// </summary>
            private static T t = default(T);

            /// <summary>
            /// 通過方法獲取單例
            /// </summary>
            /// <param name="t"></param>
            /// <returns></returns>
            public static T GetInstance(T t)
            {
                t = t ?? new T();
                return t;
            }

            /// <summary>
            /// 通過屬性獲取單例(在繼承的時候使用)
            /// </summary>
            public static T Current
            {
                get
                {
                    t = t ?? new T();
                    return t;
                }
            }

            #endregion

因此這裏所說的工廠模式通過泛型傳遞類型,再創建實例的具體代碼只有這麽點,簡短精煉:

/// <summary>
    /// 隊列工廠
    /// </summary>
    public class QueueReposity<T> where T : class,IQueue, new()
    {
        public static IQueue Current
        {
            get
            {
                return PublicClass.ConfClass<T>.Current;
            }
        }
    }


Win7和Server2008安裝MSMQ支持

上面分享的是隊列工廠的結構,到這裏就要開始我們的第一個MSMQ隊列的安裝和封裝分享了;首先來看Win7測試環境上怎麽安裝MSMQ的支持:開始菜單-》控制面板-》程序和功能:

技術分享

-》打開或關閉Windows功能-》勾選如圖所示隊列安裝組件:

技術分享

-》確定等待安裝完成;到此win7安裝msmq就完成了,因為msmq是系統默認的所以安裝起來很方便,當然server2008也差不多,按照如下操作安裝(這裏我使用租的阿裏雲Server2008R2服務器為例):開始-》控制面板-》程序(下面的打開或關閉Window功能)->功能-》添加功能-》消息隊列:

技術分享

在server上安裝的步驟基本沒啥變化,是不是很簡單;安裝完成後這樣你的電腦或服務器就支持msmq了,此刻的你是不是很興奮,覺得又能學到新東西了呵呵;

MSMQ測試用例(服務端+客戶端)

首先,這裏我用控制臺程序做測試用例,我分為客戶端和服務端,用服務端通過分裝的插入隊列方法插入數據,然後通過客戶端讀取隊列信息,先來上個圖撐撐場面吧:

技術分享

這裏我創建了MSMQ的分裝類 public class QMsmq : PublicClass.ConfClass<QMsmq>, IQueue 實現了隊列接口IQueue和繼承配置文件類ConfClass<QMsmq>,此時具體的方法體如下:

public class QMsmq : PublicClass.ConfClass<QMsmq>, IQueue
    {


        private MessageQueue _msmq = null;

        public void Create()
        {
            if (string.IsNullOrWhiteSpace(this.ApiUrl)) { throw new Exception("創建隊列需要指定隊列:地址"); }

            _msmq = MessageQueue.Exists(this.ApiUrl) ?
                new MessageQueue(this.ApiUrl) :
                _msmq ?? MessageQueue.Create(this.ApiUrl);
            //設置數據格式
            _msmq.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
        }

        public int Total()
        {
            if (_msmq == null) { throw new Exception("請先創建隊列"); }
            return _msmq.GetAllMessages().Length; 
        }

        public Message Read()
        {
            try
            {
                if (_msmq == null) { throw new Exception("請先創建隊列"); }

                //60s超時
                return _msmq.Receive(TimeSpan.FromSeconds(60));
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        //public List<Message> ReadAll()
        //{
        //    try
        //    {
        //        if (_msmq == null) { throw new Exception("請先創建隊列"); }

        //        var messages = _msmq.GetAllMessages();
        //        return messages.ToList();
        //    }
        //    catch (Exception ex)
        //    {
        //        throw new Exception(ex.Message);
        //    }
        //}

        public bool Write(string content, string name = "")
        {
            try
            {
                if (_msmq == null) { throw new Exception("請先創建隊列"); }
                if (string.IsNullOrWhiteSpace(content)) { throw new Exception("填充內容不能為空"); }

                var message = new Message();
                message.Body = content;
                message.Label = name;
                _msmq.Send(message);
                return true;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        public void Dispose()
        {
            if (_msmq != null)
            {
                _msmq.Close();
                _msmq.Dispose();
                _msmq = null;
            }
        }
    }

到這裏我們的MSMQ簡單封裝代碼已經完成了,咋們再來通過控制臺調用下這個隊列客戶端代碼

class Program
    {
        static void Main(string[] args)
        {
            Client();
        }

        /// <summary>
        /// 客戶端
        /// </summary>
        private static void Client()
        {
            //實例化QMsmq對象
            var msmq = QueueReposity<QMsmq>.Current;
            try
            {
                Console.WriteLine("創建:msmq");
                msmq.Create();

                while (true)
                {
                    try
                    {
                        var result = msmq.Read();
                        Console.WriteLine(string.Format("接受第{0}個:{1}", result.Label, result.Body));
                    }
                    catch (Exception ex)
                    { Console.WriteLine("異常信息:" + ex.Message); }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                Console.WriteLine("釋放。");
                msmq.Dispose();
            }
        }
    }

這裏能夠看出客戶端代碼中使用MSMQ步驟主要有:QueueReposity<QMsmq>.Current工廠創建自定義隊列實例-》Create()創建-》Read()讀取-》Dispose()釋放mq,流程還算清晰吧;如下服務端代碼:

class Program
    {
        static void Main(string[] args)
        {
            Server();
        }

        /// <summary>
        /// 服務端
        /// </summary>
        private static void Server()
        {
            //實例化QMsmq對象
            var msmq = QueueReposity<QMsmq>.Current;

            try
            {
                Console.WriteLine("創建:msmq");
                msmq.Create();

                var num = 0;
                do
                {
                    Console.WriteLine("輸入循環數量(數字,0表示結束):");
                    var readStr = Console.ReadLine();
                    num = string.IsNullOrWhiteSpace(readStr) ? 0 : Convert.ToInt32(readStr);

                    Console.WriteLine("插入數據:");
                    for (int i = 0; i < num; i++)
                    {
                        var str = "我的編號是:" + i;
                        msmq.Write(str, i.ToString());
                        Console.WriteLine(str);
                    }
                } while (num > 0);
            }
            catch (Exception ex)
            {
            }
            finally
            {
                Console.WriteLine("釋放。");
                msmq.Dispose();
            }
            Console.ReadLine();
        }
    }

服務端的步驟幾乎和客戶端差不多,區別在於一個讀取一個寫入,服務端步驟:QueueReposity<QMsmq>.Current工廠創建自定義隊列實例-》Create()創建-》Write()寫入-》Dispose()釋放mq;以上對MSMQ的代碼分享和環境搭建講解,希望能給您帶來好的幫助,謝謝閱讀;

本文出自 “神牛步行3博客” 博客,請務必保留此出處http://shenniu003.blog.51cto.com/3316359/1923484

隊列工廠之(MSMQ)