最近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
工廠模式的特性建立例項,因為這裡封裝的都是佇列,故而能提取出統一的規則來,因此定義瞭如下介面(這裡沒有考慮一些佇列相容的非同步方法請忽略):
1 /// <summary> 2 /// 佇列公共操作 3 /// </summary> 4 public interface IQueue : IDisposable 5 { 6 /// <summary> 7 /// 建立佇列 8 /// </summary> 9 void Create(); 10 11 /// <summary> 12 /// 總數 13 /// </summary> 14 /// <returns></returns> 15 int Total(); 16 17 /// <summary> 18 /// 讀取一個佇列 19 /// </summary> 20 /// <returns></returns> 21 Message Read(); 22 23 ///// <summary> 24 ///// 讀取多個佇列 25 ///// </summary> 26 ///// <returns></returns> 27 //List<Message> ReadAll(); 28 29 /// <summary> 30 /// 寫入佇列 31 /// </summary> 32 /// <returns></returns> 33 bool Write(string content, string name = ""); 34 }
. 配置檔案操作類ConfClass<T>
因為每個佇列的都有自己的配置資訊,因此封裝了統一管理的配置檔案讀取類ConfClass<T>,來讀取配置在同一個xml檔案中的配置資訊,如下封裝了自定義配置檔案的屬性和讀取方法:
1 #region 檔案操作類 2 /// <summary> 3 /// 配置檔案操作類 4 /// </summary> 5 /// <typeparam name="T"></typeparam> 6 public class ConfClass<T> where T : class,new() 7 { 8 9 public ConfClass() { 10 11 var apiNodeName = this.GetType().Name; 12 Reader(apiNodeName); 13 } 14 15 #region 單例模式 16 17 public static readonly object Singleton_Lock = new object(); 18 19 /// <summary> 20 /// 單例物件 21 /// </summary> 22 private static T t = default(T); 23 24 /// <summary> 25 /// 通過方法獲取單例 26 /// </summary> 27 /// <param name="t"></param> 28 /// <returns></returns> 29 public static T GetInstance(T t) 30 { 31 t = t ?? new T(); 32 return t; 33 } 34 35 /// <summary> 36 /// 通過屬性獲取單例(在繼承的時候使用) 37 /// </summary> 38 public static T Current 39 { 40 get 41 { 42 t = t ?? new T(); 43 return t; 44 } 45 } 46 47 #endregion 48 49 #region 配置檔案操作 50 51 #region 配置檔案屬性 52 /// <summary> 53 /// 配置檔案地址 54 /// </summary> 55 //public string ConfPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Conf", "ShenNiuApi.xml"); 56 public string ConfPath = @"C:\Conf\ShenNiuApi.xml"; 57 58 /// <summary> 59 /// 配置檔案父節點名稱 60 /// </summary> 61 public string ConfParentNodeName = "ShenNiuApi"; 62 63 /// <summary> 64 /// 配置檔案內容 65 /// </summary> 66 public string ConfContent { get; set; } 67 68 /// <summary> 69 /// 配置檔案文件doc物件 70 /// </summary> 71 public XmlDocument doc { get; set; } 72 73 74 /// <summary> 75 /// 賬號 76 /// </summary> 77 public string UserName { get; set; } 78 79 /// <summary> 80 /// 密碼 81 /// </summary> 82 public string UserPwd { get; set; } 83 84 /// <summary> 85 /// 介面地址 86 /// </summary> 87 public string ApiUrl { get; set; } 88 89 /// <summary> 90 /// 祕鑰 91 /// </summary> 92 public string ApiKey { get; set; } 93 94 #endregion 95 96 public ConfClass(string ConfPath, string ConfParentNodeName="") 97 { 98 99 this.ConfPath = string.IsNullOrWhiteSpace(ConfPath) ? this.ConfPath : ConfPath; 100 this.ConfParentNodeName = string.IsNullOrWhiteSpace(ConfParentNodeName) ? this.ConfParentNodeName : ConfParentNodeName; 101 102 var apiNodeName = this.GetType().Name; 103 Reader(apiNodeName); 104 } 105 106 /// <summary> 107 /// 讀取配置資訊 108 /// </summary> 109 /// <param name="apiNodeName"></param> 110 public void Reader(string apiNodeName) 111 { 112 try 113 { 114 if (string.IsNullOrWhiteSpace(ConfPath) || string.IsNullOrWhiteSpace(ConfParentNodeName)) 115 { 116 throw new Exception("配置檔案地址或者配置檔案父節點名稱不能為空"); 117 } 118 119 if (!File.Exists(ConfPath)) { return; } 120 121 //獲取配置檔案資訊 122 using (StreamReader reader = new StreamReader(ConfPath)) 123 { 124 this.ConfContent = reader.ReadToEndAsync().Result; 125 } 126 127 if (string.IsNullOrWhiteSpace(this.ConfContent)) { return; } 128 129 //加入doc中 130 this.doc = new XmlDocument(); 131 this.doc.LoadXml(this.ConfContent); 132 133 //解析 134 var parentNode = string.Format("{0}/{1}", this.ConfParentNodeName, apiNodeName); 135 var apiNode = this.doc.SelectSingleNode(parentNode); 136 if (apiNode == null) { throw new Exception("未能找到" + parentNode + "節點"); } 137 138 this.UserName = apiNode.SelectSingleNode("UserName").InnerText; 139 this.UserPwd = apiNode.SelectSingleNode("UserPwd").InnerText; 140 this.ApiUrl = apiNode.SelectSingleNode("ApiUrl").InnerText; 141 this.ApiKey = apiNode.SelectSingleNode("ApiKey").InnerText; 142 } 143 catch (Exception ex) 144 { 145 146 throw new Exception("載入配置檔案" + this.ConfPath + "異常:" + ex.Message); 147 } 148 } 149 #endregion 150 } 151 #endregion
這個配置檔案的類主要運用在佇列例項繼承上,只要繼承了預設就會讀取響應的配置節點資訊;配置xml檔案預設儲存的地址: C:\Conf\ShenNiuApi.xml ,最大父節點名稱預設:ShenNiuApi,格式如下所示:
1 <ShenNiuApi> 2 <QMsmq> 3 <UserName></UserName> 4 <UserPwd></UserPwd> 5 <ApiUrl>.\Private$\MyMsmq</ApiUrl> 6 <ApiKey></ApiKey> 7 </QMsmq> 8 </ShenNiuApi>
. 非安全單例建立佇列例項
由於工廠都是專門用來提供例項的存在,建立例項的模式也有很多這種,這裡我選擇的是非安全單例建立佇列例項,所有在ConfClass類中預設加入了單例模式:
1 #region 單例模式 2 3 public static readonly object Singleton_Lock = new object(); 4 5 /// <summary> 6 /// 單例物件 7 /// </summary> 8 private static T t = default(T); 9 10 /// <summary> 11 /// 通過方法獲取單例 12 /// </summary> 13 /// <param name="t"></param> 14 /// <returns></returns> 15 public static T GetInstance(T t) 16 { 17 t = t ?? new T(); 18 return t; 19 } 20 21 /// <summary> 22 /// 通過屬性獲取單例(在繼承的時候使用) 23 /// </summary> 24 public static T Current 25 { 26 get 27 { 28 t = t ?? new T(); 29 return t; 30 } 31 } 32 33 #endregion
因此這裡所說的工廠模式通過泛型傳遞型別,再建立例項的具體程式碼只有這麼點,簡短精煉:
1 /// <summary> 2 /// 佇列工廠 3 /// </summary> 4 public class QueueReposity<T> where T : class,IQueue, new() 5 { 6 public static IQueue Current 7 { 8 get 9 { 10 return PublicClass.ConfClass<T>.Current; 11 } 12 } 13 }
» 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>,此時具體的方法體如下:
1 public class QMsmq : PublicClass.ConfClass<QMsmq>, IQueue 2 { 3 4 5 private MessageQueue _msmq = null; 6 7 public void Create() 8 { 9 if (string.IsNullOrWhiteSpace(this.ApiUrl)) { throw new Exception("建立佇列需要指定佇列:地址"); } 10 11 _msmq = MessageQueue.Exists(this.ApiUrl) ? 12 new MessageQueue(this.ApiUrl) : 13 _msmq ?? MessageQueue.Create(this.ApiUrl); 14 //設定資料格式 15 _msmq.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) }); 16 } 17 18 public int Total() 19 { 20 if (_msmq == null) { throw new Exception("請先建立佇列"); } 21 return _msmq.GetAllMessages().Length; 22 } 23 24 public Message Read() 25 { 26 try 27 { 28 if (_msmq == null) { throw new Exception("請先建立佇列"); } 29 30 //60s超時 31 return _msmq.Receive(TimeSpan.FromSeconds(60)); 32 } 33 catch (Exception ex) 34 { 35 throw new Exception(ex.Message); 36 } 37 } 38 39 //public List<Message> ReadAll() 40 //{ 41 // try 42 // { 43 // if (_msmq == null) { throw new Exception("請先建立佇列"); } 44 45 // var messages = _msmq.GetAllMessages(); 46 // return messages.ToList(); 47 // } 48 // catch (Exception ex) 49 // { 50 // throw new Exception(ex.Message); 51 // } 52 //} 53 54 public bool Write(string content, string name = "") 55 { 56 try 57 { 58 if (_msmq == null) { throw new Exception("請先建立佇列"); } 59 if (string.IsNullOrWhiteSpace(content)) { throw new Exception("填充內容不能為空"); } 60 61 var message = new Message(); 62 message.Body = content; 63 message.Label = name; 64 _msmq.Send(message); 65 return true; 66 } 67 catch (Exception ex) 68 { 69 throw new Exception(ex.Message); 70 } 71 } 72 73 public void Dispose() 74 { 75 if (_msmq != null) 76 { 77 _msmq.Close(); 78 _msmq.Dispose(); 79 _msmq = null; 80 } 81 } 82 }
到這裡我們的MSMQ簡單封裝程式碼已經完成了,咋們再來通過控制檯呼叫下這個佇列客戶端程式碼:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Client(); 6 } 7 8 /// <summary> 9 /// 客戶端 10 /// </summary> 11 private static void Client() 12 { 13 //例項化QMsmq物件 14 var msmq = QueueReposity<QMsmq>.Current; 15 try 16 { 17 Console.WriteLine("建立:msmq"); 18 msmq.Create(); 19 20 while (true) 21 { 22 try 23 { 24 var result = msmq.Read(); 25 Console.WriteLine(string.Format("接受第{0}個:{1}", result.Label, result.Body)); 26 } 27 catch (Exception ex) 28 { Console.WriteLine("異常資訊:" + ex.Message); } 29 } 30 } 31 catch (Exception ex) 32 { 33 throw ex; 34 } 35 finally 36 { 37 Console.WriteLine("釋放。"); 38 msmq.Dispose(); 39 } 40 } 41 }
這裡能夠看出客戶端程式碼中使用MSMQ步驟主要有:QueueReposity<QMsmq>.Current工廠建立自定義佇列例項-》Create()建立-》Read()讀取-》Dispose()釋放mq,流程還算清晰吧;如下服務端程式碼:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Server(); 6 } 7 8 /// <summary> 9 /// 服務端 10 /// </summary> 11 private static void Server() 12 { 13 //例項化QMsmq物件 14 var msmq = QueueReposity<QMsmq>.Current; 15 16 try 17 { 18 Console.WriteLine("建立:msmq"); 19 msmq.Create(); 20 21 var num = 0; 22 do 23 { 24 Console.WriteLine("輸入迴圈數量(數字,0表示結束):"); 25 var readStr = Console.ReadLine(); 26 num = string.IsNullOrWhiteSpace(readStr) ? 0 : Convert.ToInt32(readStr); 27 28 Console.WriteLine("插入資料:"); 29 for (int i = 0; i < num; i++) 30 { 31 var str = "我的編號是:" + i; 32 msmq.Write(str, i.ToString()); 33 Console.WriteLine(str); 34 } 35 } while (num > 0); 36 } 37 catch (Exception ex) 38 { 39 } 40 finally 41 { 42 Console.WriteLine("釋放。"); 43 msmq.Dispose(); 44 } 45 Console.ReadLine(); 46 } 47 }
服務端的步驟幾乎和客戶端差不多,區別在於一個讀取一個寫入,服務端步驟:QueueReposity<QMsmq>.Current工廠建立自定義佇列例項-》Create()建立-》Write()寫入-》Dispose()釋放mq;以上對MSMQ的程式碼分享和環境搭建講解,希望能給您帶來好的幫助,謝謝閱讀;