最近vs2017神器正式版发布让人很是激动,vs2017支持了很多语言的开发,从前端-后端-底层的支持,堪称是工具中的神器;netcore我喜爱的架构之一也得到了大力的宣传,应群友的邀请将在队列工厂(msmq,redis,rabbitmq)一些列文章过后,继续增加.netcore方面的文章,只为.netcore发展更好贡献一份微弱的力量;本章内容分享的是队列(msmq,redis,rabbitmq)封装的队列工厂之MSMQ希望大家能够喜欢,也希望各位多多"扫码支持"和"推荐"谢谢!
创建队列工厂QueueReposity
. 队列公共操作接口IQueue
. 配置文件操作类ConfClass
. 非安全单例创建队列实例
Win7和Server2008安装MSMQ支持
MSMQ测试用例(服务端+客户端)
下面一步一个脚印的来分享:
创建队列工厂QueueReposity
首先,因为这里需要统一封装几个常用的队列方式的用法,因此采用了简单工厂模式,所以有了QueueReposity
. 队列公共操作接口IQueue
工厂模式的特性创建实例,因为这里封装的都是队列,故而能提取出统一的规则来,因此定义了如下接口(这里没有考虑一些队列兼容的异步方法请忽略):
////// 队列公共操作 /// public interface IQueue : IDisposable { ////// 创建队列 /// void Create(); ////// 总数 /// ///int Total(); /// /// 读取一个队列 /// ///Message Read(); ///// ///// 读取多个队列 ///// ///////List ReadAll(); /// /// 写入队列 /// ///bool Write(string content, string name = ""); }
. 配置文件操作类ConfClass
因为每个队列的都有自己的配置信息,因此封装了统一管理的配置文件读取类ConfClass
#region 文件操作类 ////// 配置文件操作类 /// ///public class ConfClass where T : class,new() { public ConfClass() { var apiNodeName = this.GetType().Name; Reader(apiNodeName); } #region 单例模式 public static readonly object Singleton_Lock = new object(); /// /// 单例对象 /// private static T t = default(T); ////// 通过方法获取单例 /// /// ///public static T GetInstance(T t) { t = t ?? new T(); return t; } /// /// 通过属性获取单例(在继承的时候使用) /// public static T Current { get { t = t ?? new T(); return t; } } #endregion #region 配置文件操作 #region 配置文件属性 ////// 配置文件地址 /// //public string ConfPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Conf", "ShenNiuApi.xml"); public string ConfPath = @"C:\Conf\ShenNiuApi.xml"; ////// 配置文件父节点名称 /// public string ConfParentNodeName = "ShenNiuApi"; ////// 配置文件内容 /// public string ConfContent { get; set; } ////// 配置文件文档doc对象 /// public XmlDocument doc { get; set; } ////// 账号 /// public string UserName { get; set; } ////// 密码 /// public string UserPwd { get; set; } ////// 接口地址 /// public string ApiUrl { get; set; } ////// 秘钥 /// 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); } ////// 读取配置信息 /// /// 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,格式如下所示:
.\Private$\MyMsmq
. 非安全单例创建队列实例
由于工厂都是专门用来提供实例的存在,创建实例的模式也有很多这种,这里我选择的是非安全单例创建队列实例,所有在ConfClass类中默认加入了单例模式:
#region 单例模式 public static readonly object Singleton_Lock = new object(); ////// 单例对象 /// private static T t = default(T); ////// 通过方法获取单例 /// /// ///public static T GetInstance(T t) { t = t ?? new T(); return t; } /// /// 通过属性获取单例(在继承的时候使用) /// public static T Current { get { t = t ?? new T(); return t; } } #endregion
因此这里所说的工厂模式通过泛型传递类型,再创建实例的具体代码只有这么点,简短精炼:
////// 队列工厂 /// public class QueueRepositywhere T : class,IQueue, new() { public static IQueue Current { get { return PublicClass.ConfClass .Current; } } }
Win7和Server2008安装MSMQ支持
上面分享的是队列工厂的结构,到这里就要开始我们的第一个MSMQ队列的安装和封装分享了;首先来看Win7测试环境上怎么安装MSMQ的支持:开始菜单-》控制面板-》程序和功能:
-》打开或关闭Windows功能-》勾选如图所示队列安装组件:
-》确定等待安装完成;到此win7安装msmq就完成了,因为msmq是系统默认的所以安装起来很方便,当然server2008也差不多,按照如下操作安装(这里我使用租的阿里云Server2008R2服务器为例):开始-》控制面板-》程序(下面的打开或关闭Window功能)->功能-》添加功能-》消息队列:
在server上安装的步骤基本没啥变化,是不是很简单;安装完成后这样你的电脑或服务器就支持msmq了,此刻的你是不是很兴奋,觉得又能学到新东西了呵呵;
MSMQ测试用例(服务端+客户端)
首先,这里我用控制台程序做测试用例,我分为客户端和服务端,用服务端通过分装的插入队列方法插入数据,然后通过客户端读取队列信息,先来上个图撑撑场面吧:
这里我创建了MSMQ的分装类 public class QMsmq : PublicClass.ConfClass
public class QMsmq : PublicClass.ConfClass, 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 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(); } ////// 客户端 /// private static void Client() { //实例化QMsmq对象 var msmq = QueueReposity.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
class Program { static void Main(string[] args) { Server(); } ////// 服务端 /// private static void Server() { //实例化QMsmq对象 var msmq = QueueReposity.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