关于MSMQ使用的文章,网上一搜一大把,为什么还要写呢?因为别人的终究是别人的,看一遍,只是过眼云烟罢了,还是要自己动手实践一下,才能把别人的变成自己的。再者就是网上大都是在一台电脑上写的demo,我还是想知道在不同的电脑上是怎么使用的,毕竟要应用到项目中的话,消息中间件肯定是应用于分布式系统中传递消息了,所以,不管三七二十一,先来实践一把。
MSMQ简介:(摘自百度百科)
MicroSoft Message Queuing(微软消息队列)是在多个不同的应用之间实现相互通信的一种异步传输模式,相互通信的应用可以分布于同一台机器上,也可以分布于相连的网络空间中的任一位置。它的实现原理是:消息的发送者把自己想要发送的信息放入一个容器中(我们称之为Message),然后把它保存至一个系统公用空间的消息队列(Message Queue)中;本地或者是异地的消息接收程序再从该队列中取出发给它的消息进行处理。
第一步:安装,因为MSMQ是Windows自带的消息中间件,所以不需要下载,只要在Windows程序打开关闭新功能中启用MSMQ服务即可。
等待完成后,在计算机管理中,可以看到消息队列菜单选项
第二步,新建解决方案,三层,分别是对消息操作的封装,以及消息的消费者与消息的生产者
Common中需要引用System.Messaging.dll,值得注意的是MSMQProducer中也需要引用System.Messaging.dll,为什么呢?
因为QueueManger这个帮助类中SendMessage方法带了一个默认值为null(MessageQueueTransaction tran = null)的参数,所以也需要引用这个dll。当然可以改造这个方法,就不必引用了,只是尊重原作者,我就啥也没改,添加一下引用,能跑起来就行。
QueueManger这个类源码取自https://www.cnblogs.com/shaoshun/p/3800208.html这篇博文,写得很好,就直接拿来用了,感谢博主!
using System; using System.Collections.Generic; using System.Linq; using System.Messaging; using System.Text; using System.Threading.Tasks; namespace Common { public class QueueManger { /// <summary> /// 创建MSMQ队列 /// </summary> /// <param name="queuePath">队列路径</param> /// <param name="transactional">是否事务队列</param> public static void Createqueue(string queuePath, bool transactional = false) { try { //判断队列是否存在 if (!MessageQueue.Exists(queuePath)) { MessageQueue.Create(queuePath); Console.WriteLine(queuePath + "已成功创建!"); } else { Console.WriteLine(queuePath + "已经存在!"); } } catch (MessageQueueException e) { Console.WriteLine(e.Message); } } /// <summary> /// 删除队列 /// </summary> /// <param name="queuePath"></param> public static void Deletequeue(string queuePath) { try { //判断队列是否存在 if (MessageQueue.Exists(queuePath)) { MessageQueue.Delete(queuePath); Console.WriteLine(queuePath + "已删除!"); } else { Console.WriteLine(queuePath + "不存在!"); } } catch (MessageQueueException e) { Console.WriteLine(e.Message); } } /// <summary> /// 发送消息 /// </summary> /// <typeparam name="T">用户数据类型</typeparam> /// <param name="target">用户数据</param> /// <param name="queuePath">队列名称</param> /// <param name="tran"></param> /// <returns></returns> public static bool SendMessage<T>(T target, string queuePath, MessageQueueTransaction tran = null) { try { //连接到本地的队列 MessageQueue myQueue = new MessageQueue(queuePath); System.Messaging.Message myMessage = new System.Messaging.Message(); myMessage.Body = target; myMessage.Formatter = new XmlMessageFormatter(new Type[] { typeof(T) }); //发送消息到队列中 if (tran == null) { myQueue.Send(myMessage); } else { myQueue.Send(myMessage, tran); } Console.WriteLine("消息已成功发送到" + queuePath + "队列!"); return true; } catch (ArgumentException e) { Console.WriteLine(e.Message); return false; } } /// <summary> /// 接收消息 /// </summary> /// <typeparam name="T">用户的数据类型</typeparam> /// <param name="queuePath">消息路径</param> /// <returns>用户填充在消息当中的数据</returns> public static T ReceiveMessage<T>(string queuePath, MessageQueueTransaction tran = null) { //连接到本地队列 MessageQueue myQueue = new MessageQueue(queuePath); myQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(T) }); try { //从队列中接收消息 System.Messaging.Message myMessage = tran == null ? myQueue.Receive() : myQueue.Receive(tran); return (T)myMessage.Body; //获取消息的内容 } catch (MessageQueueException e) { Console.WriteLine(e.Message); } catch (InvalidCastException e) { Console.WriteLine(e.Message); } return default(T); } /// <summary> /// 采用Peek方法接收消息 /// </summary> /// <typeparam name="T">用户数据类型</typeparam> /// <param name="queuePath">队列路径</param> /// <returns>用户数据</returns> public static T ReceiveMessageByPeek<T>(string queuePath) { //连接到本地队列 MessageQueue myQueue = new MessageQueue(queuePath); myQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(T) }); try { //从队列中接收消息 System.Messaging.Message myMessage = myQueue.Peek(); return (T)myMessage.Body; //获取消息的内容 } catch (MessageQueueException e) { Console.WriteLine(e.Message); } catch (InvalidCastException e) { Console.WriteLine(e.Message); } return default(T); } /// <summary> /// 获取队列中的所有消息 /// </summary> /// <typeparam name="T">用户数据类型</typeparam> /// <param name="queuePath">队列路径</param> /// <returns>用户数据集合</returns> public static List<T> GetAllMessage<T>(string queuePath) { MessageQueue myQueue = new MessageQueue(queuePath); myQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(T) }); try { Message[] msgArr = myQueue.GetAllMessages(); List<T> list = new List<T>(); msgArr.ToList().ForEach((o) => { list.Add((T)o.Body); }); return list; } catch (Exception e) { Console.WriteLine(e.Message); } return null; } } }
生产者代码:
class Program { static void Main(string[] args) { string queuepath = @".\private$\myQueue"; QueueManger.Createqueue(queuepath); Student stu = new Student() { Name = "dengwei", Age = 18 }; QueueManger.SendMessage<Student>(stu, queuepath); Console.ReadKey(); } }
消费者代码:注意与生产者queuePath的区别,因为我需要模拟的是远程访问消息队列
TCP:FormatName:Direct=TCP:IP\\private$\\queueName
HTTP:FormatName:Direct=http://IP/msmq/private$/queueName(没测试,下一篇测试)
class Program { static void Main(string[] args) { string queuepath = "FormatName:Direct=TCP:10.101.98.197\\private$\\myQueue"; Student stu = QueueManger.ReceiveMessageByPeek<Student>(queuepath); Console.WriteLine(stu.Name); Console.ReadKey(); } }
编译完成之后,把消费者的可执行文件拷贝到另一台电脑,注意,这台电脑也必须启用MSMQ才能正常通讯,执行程序,抛出异常了,访问不了。
于是又查了一番资料,改注册表,分配权限等,都做了一遍,还是访问不了,最后才发现还要消息队列属性中去掉禁用未经身份验证的RPC调用。默认是勾上的
配置完成之后,再次调用,访问成功。
每天,进步一点点...