MSMQ消息队列,包括远程访问

    以前的项目用到了队列,如今总结一下,下面有很是详细的DEMO,但愿能对有须要的人提供帮助。安全

    使用场景:在项目中,将一些无需即时返回且耗时的操做提取出来,进行了异步处理,而这种异步处理的方式大大的节省了服务器的请求响应时间,从而提升了系统的吞吐量。服务器

    个人需求很简单,就是多个客户端链接到个人一个小型的数据转发服务器上,开始使用的是Socket通讯实现这个功能,一旦数据服务器接收到来自不一样客户端发来的消息,就对这些消息进行处理(我这里是将数据接收到后再转发到另外一个服务器上),但考虑到客户端是每隔一个很短的时间周期向服务器发送信息,而且链接客服端数量比较多的时候,担忧会产生并发访问的问题,也但愿避免 数据转发服务器 频繁地从多个不一样线程获取信息而出现其余未知问题,因此在处理客户端向数据转发服务器发送信息的时候采起队列的方式。并发

    通常状况下,使用MSMQ,首先要安装消息服务,跟安装IIS一个套路,打开启用或关闭Windows功能窗口,找到并勾选MSMQ消息服务,而后点击肯定进行安装,还不明白的百度一下;异步

    在VS里添加 Messaging引用,就可使用MessageQueue这个类了;接下来就要思考清楚你的数据(消息)的流向问题,以前由于本身对队列的错误认识,对到底在哪建立队列,队列的消息又由谁去发送和接收没有弄清除,还有参考的一些写得不是太清晰地博文,绕了好大一圈,因此今天在这里以我本身的项目需求为例子,说明 一、如何建立队列 二、如何向队列发送消息 三、 如何获取队列中的消息函数

   首先、建立队列:根据个人需求,我要经过Socket通讯将信息发送至数据转发服务器,所以为了不并发访问问题的产生,消息队列应当创建在数据转发服务器上;spa

        System.Messaging.MessageQueue myQuere = null;

        /// <summary>
        /// 这样就在数据转发服务器端建立了一个名为queuedemo的消息队列;
        /// 从客户端要发送的消息就保存在这个队列里,
        /// 你能够经过计算机管理->服务和应用下的消息队列中看到你建立的queuedemo队列,
        /// private$关键字是说明队列为专用队列,
        /// 若是没有这个关键字还要配置域服务器,仍是挺麻烦,这个仍是借助百度吧,
        /// 前面的“.”表明建立的队列目录是本机
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnQueueInit_Click(object sender, EventArgs e)
        {
            // 注意这里是$符号
            string queuePath = @".\private$\quereDemo";
            // 判断消息队列示例是否存在
            if (!System.Messaging.MessageQueue.Exists(queuePath))
            {
                // 不存在则建立一个消息队列
                myQuere = System.Messaging.MessageQueue.Create(queuePath);
            }

            myQuere = new System.Messaging.MessageQueue(queuePath);

        }

 

    这样就在数据转发服务器端建立了一个名为queuedemo的消息队列;从客户端要发送的消息就保存在这个队列里,你能够经过计算机管理->服务和应用下的消息队列中看到你建立的queuedemo队列,private$关键字是说明队列为专用队列,若是没有这个关键字还要配置域服务器,仍是挺麻烦,这个仍是借助百度吧,前面的“.”表明建立的队列目录是本机,这个队列一旦建立成功,就是系统的事了,接下来要作的就是你怎么去把消息写进这个队列,或者读取队列的值 线程

这里要特别注意不要将queuepath路径字符串写成3d

string queuePath = @"FormatName:Direct=TCP:192.168.1.153\private$\quereDemo";

 

这样写的话是用于远程计算机对这个队列进行访问的,由于MessageQueue的Create()和Exisit()方法是没办法去识别上述FormatName格式的,还有要确保Create()函数要被执行了以后再用MessageQueue实例去引用;这样服务器端队列的建立就完成了;code

     在客户端中,向队列发送信息;orm

    

        /// <summary>
        /// 写入数据到消息队列
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnWriteMessage_Click(object sender, EventArgs e)
        {
            // 要往队列里写入的消息
            // 要求发送的对象要以序列化的方式写进去,因此要设置formatter,这里用的是XmlMessageFormatter 还有BinaryMessageFormatter等等
            string s = "客户端往队列里发送的消息";
            // 实例化一个消息队列Object
            System.Messaging.Message message = new System.Messaging.Message();
            message.Body = s;   // body 为object类型
            message.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });    // 选择xml的方式进行传送消息

            // 建立输送消息的队列对象(在客户端和消息队列服务器分离的时候须要建立实例)
            // System.Messaging.MessageQueue myQuere = new System.Messaging.MessageQueue(@"FormatName:Direct=TCP:192.168.1.153\private$\queuedemo");
            myQuere.Send(message);

        }

 

在客户端中,用一个MessageQueue实例指向服务器本机上建立的队列路径,这时,MessageQueue实例的构造函数里的路径就必定要用FormatName格式,指明是TCP通讯仍是HTTP仍是Machine如我上面代码所示,而后调用Send()方法,将消息写进队列,这个要求发送的对象要以序列化的方式写进去,因此要设置formatter,这里用的是XmlMessageFormatter 还有BinaryMessageFormatter等等 注意保存你消息的 消息体Body是Object类型的 所以能够将你写的任何一个类的对象发送至消息队列

    在服务器中接收消息队列

        /// <summary>
        /// 开始读取消息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnReadMessage_Click(object sender, EventArgs e)
        {
            // 实例化消息队列
            System.Messaging.MessageQueue msgQuere = new System.Messaging.MessageQueue(@".\private$\quereDemo");
            // 指定写入客户端的序列化方式
            msgQuere.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
            // 开启线程读取
            // 此处也能够写成 Thread thread = new Thread(()=> { });
            System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(() =>
            {
                // 此处使用无限读取,读取完了立刻while,也能够用一个时间来间隔
                while (true)
                {
                    //接收 System.Messaging.MessageQueue 引用的队列中可用的第一条消息。此调用是同步的,在有可用消息前,它将一直阻止当前线程的执行。
                    System.Messaging.Message msg = msgQuere.Receive();
                    if (null != msg)
                    {
                        MessageBox.Show(msg.Body.ToString());
                    }
                }
            }));

            // 启动线程
            thread.IsBackground = true; // 设置为后台线程
            thread.Start();
        }

 

在本机上能够新建立一个队列实例指向本机的队列,而后按照以前约定的序列化格式反序列化消息体因此将新的队列实例的foarmatter属性赋值为发送时的formatter属性如代码所示,这个时候就直接用Receive()获得消息体,而后对消息体里的信息作处理,我这里是开启一个线程显示队列的消息,只要有新的消息写入,我就在消息框中输出

 

    这个时候可能客户端没法向远程服务器成功发送消息,缘由基本权限问题 服务器的消息队列的权限没有对未验证的客户端开放  你要在服务器队列里分配对应权限 若是你想读取队列的内容 还须要加系统变量

 

 问题解决办法

1. 服务器端(dos:compmgmt.msc)

    • 服务器上消息队列权限设置:给ANONYMOUS LOGON赋予全部权限;

    • 修改服务器的注册表,容许非验证客户端访问

 

这样客户端就能够读取服务器里的队列信息了 固然通常业务逻辑上不这么作 由于他只负责发送消息 ,综上,就是使用消息队列 跨服务器读写的 最基本的用法    

相关文章
相关标签/搜索