文章出處

最近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的代碼分享和環境搭建講解,希望能給您帶來好的幫助,謝謝閱讀;


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


文章標籤
全站熱搜
創作者介紹
創作者 大師兄 的頭像
大師兄

IT工程師數位筆記本

大師兄 發表在 痞客邦 留言(0) 人氣(44)