# 跟我一起学WCF(1)——MSMQ消息队列
## 一、引言
Windows Communication Foundation(WCF)是Microsoft为构建面向服务的应用程序而提供的统一编程模型,该服务模型提供了支持松散耦合和版本管理的序列化功能,并提供了与消息队列(MSMQ)、COM+、Asp.net Web服务、.NET Remoting等微软现有的分布式系统技术。利用WCF平台,开发人员可以很方便地构建面向服务的应用程序(SOA)。可以认为,WCF是对之前现有的分布式技术(指的是MSMQ、.NET Remoting和Web 服务等技术)的集成和扩展,既然这样,我们就有必要首先了解下之前分布式技术,只有这样才能更深刻地明白WCF所带来的好处。今天就分享下MSMQ这种分布式技术。
## 二、MSMQ的介绍
MSMQ全称是Microsoft Message Queue——微软消息队列。它是一种异步传输模式,可以在不同的应用之间实现相互通信,相互通信的应用可以分布在同一台机器上,也可以分布于相连的网络空间中的任一位置。
## 2.1 MSMQ 工作原理
MSMQ的实现原理是:消息的发送者把自己想要发送的信息放入一个容器,然后把它保存到一个系统公用空间的消息队列中,本地或异地的消息接收程序再从该队列中取出发给它的消息进行处理。
消息队列是一个公用存储空间,它可以存在于内存中或物理文件中,因此,消息以两种方式发送,即快递方式和可恢复模式。它们的区别是消息存储位置的不同,快递方式,为了消息的快速传递,所以把消息放置在内存中,而不放在物理磁盘上,以获得较高的处理能力;而可恢复模式在传送过程的每一步骤中,都把消息写入物理磁盘上,这样当保存消息队列的机器发生故障而重新启动后,可以把发送的消息恢复到故障发送之前的状态,以获得更好的消息恢复能力。消息队列可以放在发送方、接收方所在的机器上,也可以单独放置在另外一台机器上。另外,采用消息队列机制,发送方不必要担心接收方是否启动,是否发生故障等因素,只要消息成功发送出去,就可以认为处理完成,而实际上对方可能甚至未开机,或者实际消息传递到对方可能在第二天。MSMQ机制类似QQ消息传递机制。下图演示了MSMQ的实现原理。
![](https://box.kancloud.cn/2016-01-23_56a2eb4645e17.png)
MSMQ中主要有两个概念。
* 一个是消息Message:Message是通信双方需要传递的消息,它可以是文本、图片、视频等。消息包含发送和接收者的标识,只有指定的用户才能取得消息。
* 一个是队列Queue:用来保存消息的存储空间,MSMQ中主要包括以下几种队列类型:
* 公共队列:在整个消息队列网络中复制,有可能由网络连接的所有站点访问。路径格式为:机器名称\队列名称
* 专用队列(或叫私有队列):不在整个网络中发布,它们仅在所驻留的本地计算机上可用,专用队列只能由知道队列的完整路径名称或标签的应用程序访问。路径格式为:机器名称\Private$\队列名称
* 日志队列:包含确认在给定“消息队列中发送的消息回执消息”。路径格式为:机器名称\队列名称\Journal$
* 机器日志队列对应的格式为:机器名称\Journal$;
* 机器死信队列对应的格式为:机器名称\Deadletter$;
* 机器信道死信队列对应的格式为:机器名称\XactDeadletter$。
## 2.2 队列引用说明
当创建了一个MessageQueue实例之后,就应指明和哪个队列进行通信,在.NET中有3种访问指定消息队列的方法:
* 使用路径,消息队列的路径被机器名和队列名唯一确定,所以可以用消息队列路径来指明使用的消息队列。
* 使用格式名(format name),它是由MSMQ在消息队列创建时生成的唯一标识,个使命不由用户指定,而是由队列管理者自动生成的GUID。
* 使用标识名(label),它是消息队列创建时由消息管理者指定的带有意义的名字。
## 三、消息队列的优缺点
采用消息队列的好处是:由于是异步通信,无论是发送方还是接收方都不同等待对方返回成功消息,就可以执行余下的代码,大大提高了处理的能力;在信息传递过程中,具有故障恢复能力;MSMQ的消息传递机制使得通信的双方具有不同的物理平台成为可能。
消息队列缺点是:不适合Client需要Server端实时交互情况,大量请求时候,响应可能延迟。对于客户端,必须是Windows系统。可以通过连接器跟其他的非微软技术集成。
## 四、利用MSMQ开发分布式应用
## 4.1 环境准备
要想在.NET平台进行MSMQ的开发,需要安装消息队列,你需要打开控制面板->程序->打开或关闭Windows功能,勾选消息队列服务所有选项,具体操作如下图所示:
![](https://box.kancloud.cn/2016-01-23_56a2eb465d44b.png)
勾选完之后点击确定之后,可以在我的电脑->管理->服务和应用程序->消息队列 看到下面的图:
![](https://box.kancloud.cn/2016-01-23_56a2eb4676cd4.png)
看到上面这个图代表你已经成功配置了MSMQ的开发环境,下面就可以使用Visual Studio 进行开发。注意,对特定类型队列的操作代码,一定要成功安装对应的队列类型。
## 4.2 使用MSMQ开发分布式应用
首先,实现服务器端。创建一个控制台项目,添加System.Messaging引用,因为消息队列的类全部封装在System.Messaging.dll程序集里。具体服务端的代码如下:
```
1 using System;
2 using System.Messaging;
3
4 namespace MSMQServer
5 {
6 class Program
7 {
8 static void Main(string[] args)
9 {
10 // 创建一个公共队列,公共队列只能创建在域环境里
11 //if (!MessageQueue.Exists(@".\LearningHardMSMQ")) // 判断此路径下是否已经有该队列
12 //{
13 // using (MessageQueue mq = MessageQueue.Create(@".\LearningHardMSMQ"))
14 // {
15 // mq.Label = "LearningHardQueue"; // 设置队列标签
16 // Console.WriteLine("已经创建了一个公共队列");
17 // Console.WriteLine("路径为:{0}", mq.Path);
18 // Console.WriteLine("队列名字为:{0}", mq.QueueName);
19 // mq.Send("MSMQ Message", "Leaning Hard"); // 发送消息
20 // }
21 //}
22
23 //if (MessageQueue.Exists(@".\Private$\LearningHardMSMQ"))
24 //{
25 // 删除消息队列
26 // MessageQueue.Delete(@".\Private$\LearningHardMSMQ");
27 //}
28 // 创建一个私有消息队列
29 if (!MessageQueue.Exists(@".\Private$\LearningHardMSMQ"))
30 {
31 using (MessageQueue mq = MessageQueue.Create(@".\Private$\LearningHardMSMQ"))
32 {
33 mq.Label = "LearningHardPrivateQueue";
34 Console.WriteLine("已经创建了一个私有队列");
35 Console.WriteLine("路径为:{0}", mq.Path);
36 Console.WriteLine("私有队列名字为:{0}", mq.QueueName);
37 mq.Send("MSMQ Private Message", "Leaning Hard"); // 发送消息
38 }
39 }
40
41 // 遍历所有的公共消息队列
42 //foreach (MessageQueue mq in MessageQueue.GetPublicQueues())
43 //{
44 // mq.Send("Sending MSMQ public message" + DateTime.Now.ToLongDateString(), "Learning Hard");
45 // Console.WriteLine("Public Message is sent to {0}", mq.Path);
46 //}
47
48 if (MessageQueue.Exists(@".\Private$\LearningHardMSMQ"))
49 {
50 // 获得私有消息队列
51 MessageQueue mq = new MessageQueue(@".\Private$\LearningHardMSMQ");
52 mq.Send("Sending MSMQ private message" + DateTime.Now.ToLongDateString(), "Leaning Hard");
53 Console.WriteLine("Private Message is sent to {0}", mq.Path);
54 }
55
56 Console.Read();
57 }
58 }
59 }
```
服务器端代码需要注意的是,公共队列只能在域环境中创建,由于我的个人电脑没有加入域环境,所以不能创建公共队列,从开始的消息队列的截图也可以看出,在图中并没有安装公共队列。
实现完服务器端之后,自然就是完成客户端。MSMQ程序的原理主要是:服务器端把消息发送到共享的消息队列中,然后,客户端从这个共享的消息队列中取出消息进行处理。具体客户端的实现代码如下所示:
```
1 using System;
2 using System.Messaging; // 需要添加System.Messaging引用
3
4 namespace MSMQClient
5 {
6 class Program
7 {
8 static void Main(string[] args)
9 {
10 if (MessageQueue.Exists(@".\Private$\LearningHardMSMQ"))
11 {
12 // 创建消息队列对象
13 using (MessageQueue mq = new MessageQueue(@".\Private$\LearningHardMSMQ"))
14 {
15 // 设置消息队列的格式化器
16 mq.Formatter = new XmlMessageFormatter(new string[] { "System.String" });
17 foreach (Message msg in mq.GetAllMessages())
18 {
19 Console.WriteLine("Received Private Message is: {0}", msg.Body);
20 }
21
22 Message firstmsg = mq.Receive(); // 获得消息队列中第一条消息
23 Console.WriteLine("Received The first Private Message is: {0}", firstmsg.Body);
24 }
25 }
26 Console.Read();
27 }
28 }
29 }
```
## 4.3 运行演示
经过上面步骤,我们已经完成了MSMQ分布式程序的实现了,下面看看如何运行该程序来查看效果。
首先,自然要启动服务器,右键MSMQServer项目->调试->启动新实例来启动服务器,具体步骤如下图所示:
![](https://box.kancloud.cn/2016-01-23_56a2eb4688430.png)
运行成功之后,你将到服务器发送消息成功的控制台界面,效果图如下所示:
![](https://box.kancloud.cn/2016-01-23_56a2eb469aac9.png)
接下来运行客户端来从消息队列中取得消息并显示在控制台中,采用和服务器相同的方式来启动客户端,右键MSMQClient->调试->启动新实例,看到客户端的效果如下图所示:
![](https://box.kancloud.cn/2016-01-23_56a2eb46a9275.png)
从上图可以看出,客户端确实成功地取得了消息队列中的消息。
以上MSMQ程序需要特别注意是:[MessageQueue.Receive()](http://msdn.microsoft.com/en-us/library/system.messaging.messagequeue.receive(v=vs.110).aspx)是取出消息队列中队列中的第一条消息,并从消息队列中移除它(MSDN中文翻译上是错误,MSDN写的是不移除,而英文原文是移除),而实际结果也是移除的,如果你再运行一次客户端时,你会发现消息队列中只有一条消息,具体运行效果如下图所示:
![](https://box.kancloud.cn/2016-01-23_56a2eb46bb62e.png)
## 五、总结
到这里,MSMQ的内容就分享结束, 其MSMQ的实现原理也非常简单,一句话慨括就是服务器把消息放在一个公共的地方,这个地方叫做消息队列,而其他客户端可以从这个地方取出消息进行处理。下一章将分享.NET 平台上另外一种分布式技术——.NET Remoting。
本文的示例代码文件:[MSMQSample](http://files.cnblogs.com/zhili/MSMQSample.zip)。
- C# 基础知识系列
- C# 基础知识系列 专题一:深入解析委托——C#中为什么要引入委托
- C# 基础知识系列 专题二:委托的本质论
- C# 基础知识系列 专题三:如何用委托包装多个方法——委托链
- C# 基础知识系列 专题四:事件揭秘
- C# 基础知识系列 专题五:当点击按钮时触发Click事件背后发生的事情
- C# 基础知识系列 专题六:泛型基础篇——为什么引入泛型
- C# 基础知识系列 专题七: 泛型深入理解(一)
- C# 基础知识系列 专题八: 深入理解泛型(二)
- C# 基础知识系列 专题九: 深入理解泛型可变性
- C#基础知识系列 专题十:全面解析可空类型
- C# 基础知识系列 专题十一:匿名方法解析
- C#基础知识系列 专题十二:迭代器
- C#基础知识 专题十三:全面解析对象集合初始化器、匿名类型和隐式类型
- C# 基础知识系列 专题十四:深入理解Lambda表达式
- C# 基础知识系列 专题十五:全面解析扩展方法
- C# 基础知识系列 专题十六:Linq介绍
- C#基础知识系列 专题十七:深入理解动态类型
- 你必须知道的异步编程 C# 5.0 新特性——Async和Await使异步编程更简单
- 全面解析C#中参数传递
- C#基础知识系列 全面解析C#中静态与非静态
- C# 基础知识系列 C#中易混淆的知识点
- C#进阶系列
- C#进阶系列 专题一:深入解析深拷贝和浅拷贝
- C#进阶系列 专题二:你知道Dictionary查找速度为什么快吗?
- C# 开发技巧系列
- C# 开发技巧系列 使用C#操作Word和Excel程序
- C# 开发技巧系列 使用C#操作幻灯片
- C# 开发技巧系列 如何动态设置屏幕分辨率
- C# 开发技巧系列 C#如何实现图片查看器
- C# 开发技巧 如何防止程序多次运行
- C# 开发技巧 实现属于自己的截图工具
- C# 开发技巧 如何使不符合要求的元素等于离它最近的一个元素
- C# 线程处理系列
- C# 线程处理系列 专题一:线程基础
- C# 线程处理系列 专题二:线程池中的工作者线程
- C# 线程处理系列 专题三:线程池中的I/O线程
- C# 线程处理系列 专题四:线程同步
- C# 线程处理系列 专题五:线程同步——事件构造
- C# 线程处理系列 专题六:线程同步——信号量和互斥体
- C# 多线程处理系列专题七——对多线程的补充
- C#网络编程系列
- C# 网络编程系列 专题一:网络协议简介
- C# 网络编程系列 专题二:HTTP协议详解
- C# 网络编程系列 专题三:自定义Web服务器
- C# 网络编程系列 专题四:自定义Web浏览器
- C# 网络编程系列 专题五:TCP编程
- C# 网络编程系列 专题六:UDP编程
- C# 网络编程系列 专题七:UDP编程补充——UDP广播程序的实现
- C# 网络编程系列 专题八:P2P编程
- C# 网络编程系列 专题九:实现类似QQ的即时通信程序
- C# 网络编程系列 专题十:实现简单的邮件收发器
- C# 网络编程系列 专题十一:实现一个基于FTP协议的程序——文件上传下载器
- C# 网络编程系列 专题十二:实现一个简单的FTP服务器
- C# 互操作性入门系列
- C# 互操作性入门系列(一):C#中互操作性介绍
- C# 互操作性入门系列(二):使用平台调用调用Win32 函数
- C# 互操作性入门系列(三):平台调用中的数据封送处理
- C# 互操作性入门系列(四):在C# 中调用COM组件
- CLR
- 谈谈: String 和StringBuilder区别和选择
- 谈谈:程序集加载和反射
- 利用反射获得委托和事件以及创建委托实例和添加事件处理程序
- 谈谈:.Net中的序列化和反序列化
- C#设计模式
- UML类图符号 各种关系说明以及举例
- C#设计模式(1)——单例模式
- C#设计模式(2)——简单工厂模式
- C#设计模式(3)——工厂方法模式
- C#设计模式(4)——抽象工厂模式
- C#设计模式(5)——建造者模式(Builder Pattern)
- C#设计模式(6)——原型模式(Prototype Pattern)
- C#设计模式(7)——适配器模式(Adapter Pattern)
- C#设计模式(8)——桥接模式(Bridge Pattern)
- C#设计模式(9)——装饰者模式(Decorator Pattern)
- C#设计模式(10)——组合模式(Composite Pattern)
- C#设计模式(11)——外观模式(Facade Pattern)
- C#设计模式(12)——享元模式(Flyweight Pattern)
- C#设计模式(13)——代理模式(Proxy Pattern)
- C#设计模式(14)——模板方法模式(Template Method)
- C#设计模式(15)——命令模式(Command Pattern)
- C#设计模式(16)——迭代器模式(Iterator Pattern)
- C#设计模式(17)——观察者模式(Observer Pattern)
- C#设计模式(18)——中介者模式(Mediator Pattern)
- C#设计模式(19)——状态者模式(State Pattern)
- C#设计模式(20)——策略者模式(Stragety Pattern)
- C#设计模式(21)——责任链模式
- C#设计模式(22)——访问者模式(Vistor Pattern)
- C#设计模式(23)——备忘录模式(Memento Pattern)
- C#设计模式总结
- WPF快速入门系列
- WPF快速入门系列(1)——WPF布局概览
- WPF快速入门系列(2)——深入解析依赖属性
- WPF快速入门系列(3)——深入解析WPF事件机制
- WPF快速入门系列(4)——深入解析WPF绑定
- WPF快速入门系列(5)——深入解析WPF命令
- WPF快速入门系列(6)——WPF资源和样式
- WPF快速入门系列(7)——深入解析WPF模板
- WPF快速入门系列(8)——MVVM快速入门
- WPF快速入门系列(9)——WPF任务管理工具实现
- ASP.NET 开发
- ASP.NET 开发必备知识点(1):如何让Asp.net网站运行在自定义的Web服务器上
- ASP.NET 开发必备知识点(2):那些年追过的ASP.NET权限管理
- ASP.NET中实现回调
- 跟我一起学WCF
- 跟我一起学WCF(1)——MSMQ消息队列
- 跟我一起学WCF(2)——利用.NET Remoting技术开发分布式应用
- 跟我一起学WCF(3)——利用Web Services开发分布式应用
- 跟我一起学WCF(3)——利用Web Services开发分布式应用
- 跟我一起学WCF(4)——第一个WCF程序
- 跟我一起学WCF(5)——深入解析服务契约 上篇
- 跟我一起学WCF(6)——深入解析服务契约 下篇
- 跟我一起学WCF(7)——WCF数据契约与序列化详解
- 跟我一起学WCF(8)——WCF中Session、实例管理详解
- 跟我一起学WCF(9)——WCF回调操作的实现
- 跟我一起学WCF(10)——WCF中事务处理
- 跟我一起学WCF(11)——WCF中队列服务详解
- 跟我一起学WCF(12)——WCF中Rest服务入门
- 跟我一起学WCF(13)——WCF系列总结
- .NET领域驱动设计实战系列
- .NET领域驱动设计实战系列 专题一:前期准备之EF CodeFirst
- .NET领域驱动设计实战系列 专题二:结合领域驱动设计的面向服务架构来搭建网上书店
- .NET领域驱动设计实战系列 专题三:前期准备之规约模式(Specification Pattern)
- .NET领域驱动设计实战系列 专题四:前期准备之工作单元模式(Unit Of Work)
- .NET领域驱动设计实战系列 专题五:网上书店规约模式、工作单元模式的引入以及购物车的实现
- .NET领域驱动设计实战系列 专题六:DDD实践案例:网上书店订单功能的实现
- .NET领域驱动设计实战系列 专题七:DDD实践案例:引入事件驱动与中间件机制来实现后台管理功能
- .NET领域驱动设计实战系列 专题八:DDD案例:网上书店分布式消息队列和分布式缓存的实现
- .NET领域驱动设计实战系列 专题九:DDD案例:网上书店AOP和站点地图的实现
- .NET领域驱动设计实战系列 专题十:DDD扩展内容:全面剖析CQRS模式实现
- .NET领域驱动设计实战系列 专题十一:.NET 领域驱动设计实战系列总结