隊列工廠之(MSMQ)

最近vs2017神器正式版發佈讓人非常激動,vs2017支持了不少語言的開發,從前端-後端-底層的支持,堪稱是工具中的神器;netcore我喜好的架構之一也獲得了大力的宣傳,應羣友的邀請將在隊列工廠(msmq,redis,rabbitmq)一些列文章事後,繼續增長.netcore方面的文章,只爲.netcore發展更好貢獻一份微弱的力量;本章內容分享的是隊列(msmq,redis,rabbitmq)封裝的隊列工廠之MSMQ但願你們可以喜歡,也但願各位多多"掃碼支持"和"推薦"謝謝!前端

 

建立隊列工廠QueueReposity<T>redis

  . 隊列公共操做接口IQueuec#

  . 配置文件操做類ConfClass<T>後端

  . 非安全單例建立隊列實例api

Win7和Server2008安裝MSMQ支持安全

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

 

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

建立隊列工廠QueueReposity<T>異步

首先,由於這裏須要統一封裝幾個經常使用的隊列方式的用法,所以採用了簡單工廠模式,因此有了QueueReposity<T>ide

. 隊列公共操做接口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的代碼分享和環境搭建講解,但願能給您帶來好的幫助,謝謝閱讀;

相關文章
相關標籤/搜索