使用VMware桥接模式组建局域网测试MSMQ(二)

上一篇讲了搭建VMware虚拟机实现与宿主机相互通信,环境已经就绪,现在就可以做MSMQ的分布式开发了。

本篇准备分四点介绍MSMQ:

1.MSMQ简介

2.MSMQ的安装

3.MSMQ编程开发

4.Demo下载

一、MSMQ简介

MSMQ本质上是一种消息传递协议,它允许在单独的服务端/客户端运行的应用程序间已可靠的方式通信。队列用来临时存储消息,服务器端向队列发送消息,客户端从队列读取消息。这就使得即使服务器端和客户端不在同一网络中,不能直接访问彼此,也能通过队列进行通信。相比之下,sockets和其他网络协议就不能做到这一点,它们只能在服务器端和客户端能够直接相互通信的情况下才有效。

MSMQ诞生于1997年,它已经被广泛使用在企业级软件中。Microsoft已经把MSMQ合并到它的消息技术框架WCF当中。在WCF中,MSMQ被用于提供安全,可靠的传输和一个统一的编程模型,同时还兼容通信标准。

MSMQ负责企业内部和外部应用程序之间可靠地传递信息。MSMQ会把发送失败的消息放置在一个队列里面,一旦目标可以访问,MSMQ会重新发送消息到目标中。它也支持消息的安全性和优先级机制。如果要消息超时或者因为其他原因失败,可以创建死信队列进行查看。

MSMQ也支持事务。它允许对多个队列的多个操作在一个事务中进行,从而保证要么全不执行,要么全部不执行。微软分布式事务处理协调器(MSDTC)支持MSMQ和其他资源的事务性访问。

微软消息队列使用以下端口:

  • TCP: 1801
  • RPC: 135, 2101*, 2103*, 2105*
  • UDP: 3527, 1801
  • 消息队列初始化的时候,如果RPC端口正在使用,那么这些端口号有可能被增加11。查询端口135发现该2XXX端口。

各个操作系统中使用的MSMQ版本如下:

  • Version 1.0 (May 1997). Supports Windows 95, Windows NT 4.0 SP3, Windows 98 and Windows Me.
  • Version 2.0, included with Windows 2000.
  • Version 3.0, included with Windows XP (Professional, not Home Edition) and Windows Server 2003.
  • Version 4.0, part of Windows Vista and Windows Server 2008.
  • Version 5.0, part of Windows 7 and Windows Server 2008 R2.
  • Version 6.0, part of Windows 8 and Windows Server 2012.
  • Version 6.3, part of Windows Server 2012 R2.

二、MSMQ的安装

本篇MSMQ部署在Windows Server 2008 R2中。win7/Windows Server 2008 R2 支持的MSMQ的Feature看表格:

Feature (Windows 7 / Windows Server 2008 R2) Description
Microsoft Message Queue (MSMQ) Server Core / MSMQ Services This is the core service used for sending and receiving messages.
MSMQ Active Directory Domain Services Integration / Directory Service Integration This feature enables publishing of queue properties to Active Directory Domain Services (AD DS), default authentication and encryption of messages using certificates registered in AD DS, and routing of messages across Windows sites. MSMQ Active Directory Integration requires the computer to be joined to a domain.
MSMQ HTTP Support / HTTP Support This feature enables the sending of messages over Hypertext Transfer Protocol (HTTP). MSMQ HTTP support requires that Internet Information Services (IIS) be installed on the local computer.
MSMQ Triggers / Message Queuing Triggers This feature enables the invocation of a Component Object Model (COM) component or an executable file, depending on the filters that you define for the incoming messages in a given queue.
MSMQ DCOM Proxy / Message Queuing DCOM Proxy This feature enables Message Queuing applications to use a MSMQ COM application programming interface (API) to connect to a remote Message Queuing server.
Multicasting Support / Multicasting Support This feature supports multicasting messages to a multicast IP address and associating a queue with a multicast IP address.
Routing Service
This feature routes messages between different sites and within a site.

(note:The Routing Service feature is only available on Windows Server 2008 R2.)

MSMQ作为功能组件被集成在系统当中,在Window Server 2008 R2 安装MSMQ非常简单,点击【开始\控制面板\管理工具\服务器管理器\功能\添加功能\依次展开MSM\MSMQ服务】。注意:要使用【路由服务】,服务器必须加入域。如图:

三、MSMQ编程开发

在讲MSMQ编程开发之前,有必要先了解一下MSMQ的队列。在MSMQ中,队列用来临时存储不同类型的消息。队列在逻辑上被划分为两组:应用程序队列和系统队列。应用程序队列可以被应用程序创建,系统队列由MSMQ创建。

应用程序队列包括消息队列,管理队列,响应队列和报告队列。

消息队列,应用程序间可以通过消息队列里面的消息进行通信,应用程序可以通过消息队列发送和接收消息。注意:应用程序创建队列后,在计算机管理单元中的消息队列中总是显示小写。然而,在MSMQ中,消息队列的名称是大小写敏感的,所以在代码中使用队列名称的时候要十分注意。例如,应用程序创建了一个名为MYQUEUE的队列,在计算机的管理单元中显示为myqueue。在应用程序中访问这个队列时,名称一定要使用大写的MYQUEUE,如果使用小写myqueue,就会抛出一个异常。

管理队列,在发送消息的应用程序中可以指定管理队列,管理队列存储MSMQ发送的确认消息,确认消息标识应用程序发送的消息是否成功。

响应队列,发送消息的应用程序可以指定响应队列,接收消息的应用程序发送响应消息到响应队列。

报告队列,消息每次通过MSMQ路由服务器传递,MSMQ会发送一条报告消息进行跟踪,报告消息存储在报告队列中。报告队列由发送程序指定和启用。

系统队列由MSMQ或者MSMQ管理员创建,包括日志队列和死信队列。

日志队列,当应用程序队列被创建后,MSMQ自动创建一条日志消息跟踪被读取的消息。

死信队列,保存未能被正确发送的消息。MSMQ提供两种死信队列,一种是非事务死信队列,一种是事务性死信队列。

队列的基本概念讲完了,下面就开始讲讲具体的使用。园子里已经有很多很好的文章介绍MSMQ消息队列的创建,消息的发送和消息的接收,我就不再重复造轮子了。但是在真实使用场景中,如果消息发送不成功,而消息又非常重要每个消息都必须处理,这种情况该怎么使用MSMQ,园子里介绍的文章倒不是很多,所以这里我就重点介绍一下管理队列和死信队列的用法。

  1. 新建消息发送控制台应用程序TestAck,黏贴一下代码:

public class MyNewQueue
    {
        static void Main(string[] args)
        {
            // Create a new instance of the class.
            MyNewQueue myNewQueue = new MyNewQueue();

            // Create new queues.
            CreateQueue(".\\private$\\myQueue");
            CreateQueue(".\\private$\\myAdministrationQueue");

            // Send messages to a queue.
            myNewQueue.SendMessage();
            Console.ReadLine();
        }

        //**************************************************
        // Creates a new queue.
        //**************************************************

        public static void CreateQueue(string queuePath)
        {
            try
            {
                if (!MessageQueue.Exists(queuePath))
                {
                    MessageQueue.Create(queuePath);
                }
                else
                {
                    Console.WriteLine(queuePath + " already exists.");
                }
            }
            catch (MessageQueueException e)
            {
                Console.WriteLine(e.Message);
            }

        }

        //**************************************************
        // Sends a string message to a queue.
        //**************************************************

        public void SendMessage()
        {

            // Connect to a queue on the local computer.
            MessageQueue myQueue = new MessageQueue($".\\private$\\myQueue");;
            myQueue.Label = "label1";
            // Create a new message.
            Message myMessage = new Message("Original Message");

            myMessage.AdministrationQueue = new MessageQueue(".\\private$\\myAdministrationQueue");
            myMessage.AcknowledgeType = AcknowledgeTypes.NegativeReceive;
            myMessage.UseDeadLetterQueue = true;
            myMessage.TimeToBeReceived = TimeSpan.FromSeconds(2);
            myMessage.Label = "label1";

            // Send the Order to the queue.
            Thread.Sleep(TimeSpan.FromSeconds(3));
            myQueue.Send(myMessage);

            return;
        }
    }

myMessage.AdministrationQueue = new MessageQueue(".\\private$\\myAdministrationQueue");

指定消息的管理队列,管理队列的创建很普通消息队列是一样的。

myMessage.UseDeadLetterQueue = true;

指定消息使用死信队列,消息发送不成功时,系统会发送消息的副本到死信队列。

myMessage.AcknowledgeType = AcknowledgeTypes.NegativeReceive;

指定当消息队列未能接收消息时,发送确认消息到管理队列。AcknowledgeTypes是一个枚举类型,枚举值:


PositiveArrival


一个掩码,用于在原始消息到达队列时请求肯定确认。


PositiveReceive


一个掩码,用于在成功从队列检索到原始消息时请求肯定确认。


NegativeReceive


一个掩码,用于当未能从队列接收原始消息时请求否定确认。


None


一个掩码,用于请求不发送任何确认消息(无论是肯定的还是否定的)。


NotAcknowledgeReachQueue


一个掩码,用于在原始消息不能到达队列时请求否定确认。当到达队列时间计时器过期时或不能对消息进行身份验证时,可能请求否定确认。


NotAcknowledgeReceive


一个掩码,用于当发生错误时请求否定确认,防止在其接收时间计时器过期前从队列接收原始消息。


FullReachQueue


一个掩码,用于在原始消息到达队列时请求肯定确认,或者用于到达队列时间计时器过期后请求否定确认,或者用于不能对原始消息进行身份验证时请求否定确认。


FullReceive


一个掩码,用于在接收时间计时器过期前从队列收到原始消息时请求肯定确认,否则请求否定确认。

备注:确认消息是系统生成的非事务性消息,它们标识由应用程序发送的消息是否成功发送到目标队列,也可以标识是消息否被应用程序成功读取。

myMessage.TimeToBeReceived = TimeSpan.FromSeconds(2);

Thread.Sleep(TimeSpan.FromSeconds(3));

myQueue.Send(myMessage);

模拟消息发送超时,消息队列未能接收消息的情况。消息的TimeToBeReceived属性指定队列接收消息的超时时间。

代码执行后,查看计算机管理单元中的消息队列情况,由于发送消息超时,所以消息队列myQueue中没有接收到消息;管理队列被指定为当消息发送不成功时接收确认消息,所以管理队列中由系统自动生成了一条确认消;消息指定使用死信队列,所以系统同时发送消息的副本到死信队列。如下图:

2.新建接收消息控制台应用程序AckClient,黏贴以下代码

class ReceiveClient
    {
        static void Main(string[] args)
        {
            new ReceiveClient().ReceiveAckMessage();
            new ReceiveClient().ReceiveDeadLetterMessage();
            Console.ReadLine();
        }

        private void CreateQueue(string path)
        {
            try
            {
                if (!MessageQueue.Exists(path))
                {
                    MessageQueue.Create(path);
                }
                else
                {
                    Console.WriteLine("该队列已存在!");
                }
            }
            catch (MessageQueueException ex)
            {
                throw;
            }
        }

        private void ReceiveAckMessage()
        {

            MessageQueue myQueue = new MessageQueue(".\\private$\\myAdministrationQueue");
            myQueue.MessageReadPropertyFilter.CorrelationId = true;
            myQueue.MessageReadPropertyFilter.Acknowledgment = true;
            myQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });

            MessageQueue mySendQueue = new MessageQueue(".\\private$\\myQueue");
            mySendQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
            try
            {

                Message myMessage = myQueue.Receive(TimeSpan.FromSeconds(3));
                Console.WriteLine("_______________________________");
                Console.WriteLine("Ack Message body: " + myMessage.Body.ToString());
                Console.WriteLine("Ack Message Id: " + myMessage.Id);
                Console.WriteLine("Correlation Id: " + myMessage.CorrelationId);
                Console.WriteLine("Acknowledgment Type: " + myMessage.Acknowledgment.ToString());
                Console.WriteLine("_______________________________");

            }
            catch (MessageQueueException ex)
            {
                throw;
            }
        }

        private void ReceiveDeadLetterMessage()
        {
            MessageQueue myQueue = new MessageQueue("FormatName:Direct=os:.\\System$;DEADLETTER");
            myQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
            myQueue.MessageReadPropertyFilter.CorrelationId = true;
            try
            {
                Message myMessage = myQueue.Receive(TimeSpan.FromSeconds(3));
                Console.WriteLine("__________________________________");
                Console.WriteLine("Dead Letter Id: " + myMessage.Id);
                Console.WriteLine("Dead Letter Body: " + myMessage.Body.ToString());
                Console.WriteLine("__________________________________");
            }
            catch (MessageQueueException ex)
            {
                throw;
            }
        }
    }

ReceiveAckMessage()方法从管理队列接收确认消息。
myMessage.CorrelationId: 确认消息的CorrelationId属性标识原始消息的消息ID
myMessage.Acknowledgment: 确认消息的Acknowledgment属性标识确认消息的类型。

ReceiveDeadLetterMessage()方法从死信队列接收死信消息。
myMessage.Body: 死信消息时原始消息的副本,死信消息的body属性值与原始消息的body属性值相同。
myMessage.Id: 死信消息的消息ID与原始消息的消息ID相同。

执行效果如图:

四、Demo下载
TestAck.zip总结:有了上面的知识,我们就可以处理未发送成功的消息。解决方案是在发送消息的程序中使用死信队列;在读取消息的程序中,读取死信队列的死信消息,然后重新处理死信消息。

结尾:示例代码是在本机创建专用队列,可以正常执行。如果在远程计算机中,不能直接使用Create方法创建队列,也不能使用Exsits方法判断队列是否存在,我的做法是在远程计算机中手动创建队列,然后配置队列的读取权限。

结束语:关于MSMQ的知识点,感觉有好多要讲,比如队列的访问方式,事务队列的使用,日志队列的使用。如果以后有时间,我计划重新整理本篇,做成一个系列,把每个知识点都讲一讲。好了,就像写程序一样,不可能第一版就做的很完美,需要一个迭代的过程,不断的去重构,不断的去优化。

时间: 2024-10-13 22:25:49

使用VMware桥接模式组建局域网测试MSMQ(二)的相关文章

VMware桥接模式连接局域网和互联网

第一步:确认本地网关地址 第二步选择桥接模式: 我比较幸运,桥接到"自动",就已经连接成功.不用逐个试错. 修改 ifcfg-ens33 和 新建 ifcfg-br0 [[email protected] network-scripts]# pwd /etc/sysconfig/network-scripts [[email protected] network-scripts]# cat ifcfg-br0 DEVICE="br0" NM_CONTROLLED=&

使用VMVare组建局域网测试MSMQ(一)

桥接模式搭建VMVare虚拟机 MSMQ与客户端在同一服务器的情况下使用非常简单,由于要分布式部署,而测试机有限,所以在本机使用VMware搭建局域网测试MSMQ的使用.本篇主要分两部分,第一部分讲解VMware虚拟机的搭建,第二部分讲解远程MSMQ的使用.原理性的知识请需要的同学自行搜索园内相关文章,这里着重描述一下使用方法. 项目的使用场景是分布式部署,并且是在局内网内,为了模拟生产环境,使用桥接模式搭建VMware虚拟机,虚拟机操作系统为WINDOWS SERVER 2008 R2.为什么

浅说解决VMware桥接模式连接主机

内容概要: 1: 搞懂VMware的桥接,NAT,HostOnly三种模式的意义. 2: 分别示范,CentOS 6,CentOS 7的手动分配固定虚拟机的IP地址. 3: 使用桥接方法ping通主机,并用Xshell远程控制. 解决的问题: 1: 虚拟机上不了网. 2: 无法ping通主机或远程连接. 3: 网卡启动总是失败,报错. 写作目的: 初装linux后,我们会习惯性的ping一下主机,看看虚拟机是否和主机连接正常,若正常便会迫不及待的用Xshell远程连接虚拟机,进行验证并操作.可安

vmware桥接模式-无法内网通-克隆机要删除的文件

网卡太多自动模式有时候无法正常通信需要绑定外部网卡 rm /etc/udev/rules.d/70-persistent-ipoib.rules 原文地址:https://www.cnblogs.com/zsl-find/p/10894788.html

vmware桥接模式创建ubuntu虚拟机

创建新的虚拟机时,可以采用 克隆之前的虚拟机,比单独创建速度更快. 生成的两个虚拟机的ipv4地址分别为 查看linux的本地ipv4地址的命令是ifconfig 192.168.100.25 192.168.100.30 只有最后一位不一样. 后来遇到一个问题,我在本地用ssh工具无法连接ubuntu虚拟机,别人的电脑也无法连接.但是ping可以ping通,错误信息如下 C:\windows\system32>telnet 192.168.100.30正在连接192.168.100.30...

CentOS下VMware用桥接模式,静态ip上外网

15年的时候,写过一篇博客:VMware中网络设置之Bridged  也是关于linux下vmware桥接模式.静态ip上外网的配置,不过当时更多的是用图形界面来实现的,通用性不强.生产环境,极少有linux安装图形界面的.索性原博客也不修改了,重新来一篇得了.这里我用的是CentOS6.5的最小系统来演示.当前了,因为CentOS属于红帽系列,所以本文所讲的方法对于redhat系列的linux网络配置同样适用. 一.桥接模式之VMware设置: VMware->[Edit]->[Virtua

VMware网络连接模式——桥接模式、NAT模式以及仅主机模式的介绍和区别

在使用VMware Workstation(以下简称:VMware)创建虚拟机的过程中,配置虚拟机的网络连接是非常重要的一环,当我们为虚拟机配置网络连接时,我们可以看到如下图所示的几种网络连接模式:桥接模式.NAT模式.仅主机模式.自定义网络连接模式.如下图 在VMware中,虚拟机的网络连接主要是由VMware创建的虚拟交换机(也叫做虚拟网络)负责实现的,VMware可以根据需要创建多个虚拟网络.在Windows系统的主机上,VMware最多可以创建20个虚拟网络,每个虚拟网络可以连接任意数量

桥接模式-多台虚拟机配置(重要)

15年的时候,写过一篇博客:VMware中网络设置之Bridged  也是关于linux下vmware桥接模式.静态ip上外网的配置,不过当时更多的是用图形界面来实现的,通用性不强.生产环境,极少有linux安装图形界面的.索性原博客也不修改了,重新来一篇得了.这里我用的是CentOS6.5的最小系统来演示.当前了,因为CentOS属于红帽系列,所以本文所讲的方法对于redhat系列的linux网络配置同样适用. 一.桥接模式之VMware设置: VMware->[Edit]->[Virtua

VMware NAT模式 Cent OS IP配置

1:首先VMware 桥接模式 CentOS ip 配置,关键点,ip的网关和DNS1设置成宿主机的网关和DNS 原理:桥接的模式就是通过物理网卡实现的. 2:以图展示VMware NAT模式 Cent OS ip 配置: 找网关ip NAT设置 ip配置: ping 外网: