队列工厂之(MSMQ)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了队列工厂之(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

工厂模式的特性创建实例,因为这里封装的都是队列,故而能提取出统一的规则来,因此定义了如下接口(这里没有考虑一些队列兼容的异步方法请忽略):

/// <summary>
    /// 队列公共操作
    /// </summary>
    public interface IQueue : IDisposable
    {
        /// <summary>
        /// 创建队列
        /// </summary>
        void Create();

        /// <summary>
        /// 总数
        /// </summary>
        /// <returns></returns>
        int Total();

        /// <summary>
        /// 读取一个队列
        /// </summary>
        /// <returns></returns>
        Message Read();

        ///// <summary>
        ///// 读取多个队列
        ///// </summary>
        ///// <returns></returns>
        //List<Message> ReadAll();

        /// <summary>
        /// 写入队列
        /// </summary>
        /// <returns></returns>
        bool Write(string content, string name = "");
    }

. 配置文件操作类ConfClass<T>

因为每个队列的都有自己的配置信息,因此封装了统一管理的配置文件读取类ConfClass<T>,来读取配置在同一个xml文件中的配置信息,如下封装了自定义配置文件的属性和读取方法:

#region 文件操作类
        /// <summary>
        /// 配置文件操作类
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public class ConfClass<T> where T : class,new()
        {

            public ConfClass() {

                var apiNodeName = this.GetType().Name;
                Reader(apiNodeName);
            }

            #region 单例模式

            public static readonly object Singleton_Lock = new object();

            /// <summary>
            /// 单例对象
            /// </summary>
            private static T t = default(T);

            /// <summary>
            /// 通过方法获取单例
            /// </summary>
            /// <param name="t"></param>
            /// <returns></returns>
            public static T GetInstance(T t)
            {
                t = t ?? new T();
                return t;
            }

            /// <summary>
            /// 通过属性获取单例(在继承的时候使用)
            /// </summary>
            public static T Current
            {
                get
                {
                    t = t ?? new T();
                    return t;
                }
            }

            #endregion

            #region 配置文件操作

            #region  配置文件属性
            /// <summary>
            /// 配置文件地址
            /// </summary>
            //public string ConfPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Conf", "ShenNiuApi.xml");
            public string ConfPath = @"C:\Conf\ShenNiuApi.xml";

            /// <summary>
            /// 配置文件父节点名称
            /// </summary>
            public string ConfParentNodeName = "ShenNiuApi";

            /// <summary>
            /// 配置文件内容
            /// </summary>
            public string ConfContent { get; set; }

            /// <summary>
            /// 配置文件文档doc对象
            /// </summary>
            public XmlDocument doc { get; set; }


            /// <summary>
            /// 账号
            /// </summary>
            public string UserName { get; set; }

            /// <summary>
            /// 密码
            /// </summary>
            public string UserPwd { get; set; }

            /// <summary>
            /// 接口地址
            /// </summary>
            public string ApiUrl { get; set; }

            /// <summary>
            /// 秘钥
            /// </summary>
            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);
            }

            /// <summary>
            /// 读取配置信息
            /// </summary>
            /// <param name="apiNodeName"></param>
            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,格式如下所示:

<ShenNiuApi>
    <QMsmq>
        <UserName></UserName>
        <UserPwd></UserPwd>
        <ApiUrl>.\Private$\MyMsmq</ApiUrl>
        <ApiKey></ApiKey>
    </QMsmq>
</ShenNiuApi>

. 非安全单例创建队列实例

由于工厂都是专门用来提供实例的存在,创建实例的模式也有很多这种,这里我选择的是非安全单例创建队列实例,所有在ConfClass类中默认加入了单例模式:

#region 单例模式

            public static readonly object Singleton_Lock = new object();

            /// <summary>
            /// 单例对象
            /// </summary>
            private static T t = default(T);

            /// <summary>
            /// 通过方法获取单例
            /// </summary>
            /// <param name="t"></param>
            /// <returns></returns>
            public static T GetInstance(T t)
            {
                t = t ?? new T();
                return t;
            }

            /// <summary>
            /// 通过属性获取单例(在继承的时候使用)
            /// </summary>
            public static T Current
            {
                get
                {
                    t = t ?? new T();
                    return t;
                }
            }

            #endregion

因此这里所说的工厂模式通过泛型传递类型,再创建实例的具体代码只有这么点,简短精炼:

/// <summary>
    /// 队列工厂
    /// </summary>
    public class QueueReposity<T> where T : class,IQueue, new()
    {
        public static IQueue Current
        {
            get
            {
                return PublicClass.ConfClass<T>.Current;
            }
        }
    }


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>,此时具体的方法体如下:

public class QMsmq : PublicClass.ConfClass<QMsmq>, 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<Message> 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();
        }

        /// <summary>
        /// 客户端
        /// </summary>
        private static void Client()
        {
            //实例化QMsmq对象
            var msmq = QueueReposity<QMsmq>.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<QMsmq>.Current工厂创建自定义队列实例-》Create()创建-》Read()读取-》Dispose()释放mq,流程还算清晰吧;如下服务端代码:

class Program
    {
        static void Main(string[] args)
        {
            Server();
        }

        /// <summary>
        /// 服务端
        /// </summary>
        private static void Server()
        {
            //实例化QMsmq对象
            var msmq = QueueReposity<QMsmq>.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<QMsmq>.Current工厂创建自定义队列实例-》Create()创建-》Write()写入-》Dispose()释放mq;以上对MSMQ的代码分享和环境搭建讲解,希望能给您带来好的帮助,谢谢阅读;

本文出自 “神牛步行3博客” 博客,请务必保留此出处http://shenniu003.blog.51cto.com/3316359/1923484

以上是关于队列工厂之(MSMQ)的主要内容,如果未能解决你的问题,请参考以下文章

C# 消息队列之MSMQ

MQ消息队列之MSMQ

C# 消息队列之MSMQ

初识Message Queue之--基础篇

MSMQ 单(队列)对多(侦听器)方案

MSMQ - 此计算机上尚未安装消息队列