队列工厂之(MSMQ)

简介: 最近vs2017神器正式版发布让人很是激动,vs2017支持了很多语言的开发,从前端-后端-底层的支持,堪称是工具中的神器;netcore我喜爱的架构之一也得到了大力的宣传,应群友的邀请将在队列工厂(msmq,redis,rabbitmq)一些列文章过后,继续增加.

最近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的代码分享和环境搭建讲解,希望能给您带来好的帮助,谢谢阅读;

相关实践学习
消息队列RocketMQ版:基础消息收发功能体验
本实验场景介绍消息队列RocketMQ版的基础消息收发功能,涵盖实例创建、Topic、Group资源创建以及消息收发体验等基础功能模块。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
目录
相关文章
|
12月前
|
消息中间件 存储 负载均衡
Rabbitmq direct模式保证一个队列只对应一个消费者
Rabbitmq direct模式保证一个队列只对应一个消费者
267 0
|
2月前
|
消息中间件 JSON Java
玩转RabbitMQ声明队列交换机、消息转换器
玩转RabbitMQ声明队列交换机、消息转换器
66 0
|
6月前
|
Java
用java实现生产者和消费者模式
用java实现生产者和消费者模式
54 1
|
6月前
|
消息中间件
rabbitmq动态创建队列
rabbitmq动态创建队列
85 0
|
C++
队列类(C++)
构造函数:初始化队列:将初始尺寸保存到 size,将队首 front 和队尾 rear 均置为 0,为动态数组分配内存并将起始地址保存到 element。Dequeue 函数:若队列不空,则从队列中取出元素并保存到 value中,函数值为 true;size 为动态数组的尺寸,front 为队首元素的下标,rear 为队尾元素下一位置的下标,element 为动态数组的起始地址。Clear 函数:清空队列,将 front 和 rear 重置为 0,操作成功,函数值为 true。首先定义数据元素类型。
145 0
|
消息中间件 Java Spring
RabbitMq交换机/队列的创建源码分析
RabbitMq交换机/队列的创建源码分析
232 0
|
XML JSON 负载均衡
消费者访问生产者的三种远程调用方式
1.FeignClient接口,不能使用@GettingMapping之类的组合注解 2.FeignClient接口中,如果使用到@PathVariable必须指定其value 3.只要参数是复杂对象,即使指定了是GET方法,feign依然会以POST方法进行发送请求,同时生产者必须支持POST请求并给参数添加@RequestBody注解 建议使用公共vo+@RequestBody方式 4.springcloud中feign访问其他服务并传参数出现错误的问题:status 405 reading LogisticsOrderService#get
|
消息中间件
译MassTransit 创建消息消费者
创建消息消费者一个消息消费者是一个 可以消费一个或多个消息类型的类,指定IConsumer接口,T为消息类型 public class UpdateCustomerConsumer : IConsumer { public async Task Consume(ConsumeContext context) { await Console.
1634 0
|
Java Android开发
Binder机制中的收发消息及线程池
在阅读《深入理解android内核设计思想》的有关Binder章节的时候,发现书中有部分问题没有很清晰的描述清楚,所以这篇文章主要是针对收发消息的过程和线程池这两个知识点详细展开一下。注意本篇文章并不是介绍Binder机制,而是针对它的两个小细节深入探讨一下,所以建议大家先详细的阅读《深入理解android内核设计思想》中有关Binder章节后对照阅读本篇文章。
358 0
|
消息中间件
rabbitmq创建多队列以及监听多队列和起别名解决方案
rabbitmq创建多队列以及监听多队列和起别名解决方案
403 0
rabbitmq创建多队列以及监听多队列和起别名解决方案