zoukankan      html  css  js  c++  java
  • 队列工厂

    队列工厂之(MSMQ)

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

  • 相关阅读:
    IDEA常用快捷键
    js读取Excel文件
    tensorBoard展示图片出错分析TensorBoard can’t find your event files.
    在乌班图系统中使用conda报错:from conda.cli import main ModuleNotFoundError: No module named 'conda'
    Event loop is closed 纪宇
    vue supermall蘑菇街API后端接口
    解决Uncaught (in promise) NavigationDuplicated: Avoided redundant navigation to current location问题
    Linux运行jar包的几种方式
    Linux下根据关键字搜索最后一条日志
    Linux命令之tcpdump
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/6551985.html
Copyright © 2011-2022 走看看