先说一下MSMQ

MSMQ全称MicroSoft Message Queue,微软消息队列,是在多个不同的应用之间实现相互通信的一种异步传输模式,相互通信的应用可以分布于同一台机器上,也可以分布于相连的网络空间中的任一位置。它的实现原理是:消息的发送者把自己想要发送的信息放入一个容器中(我们称之为Message),然后把它保存至一个系统公用空间的消息队列(Message Queue)中;本地或者是异地的消息接收程序再从该队列中取出发给它的消息进行处理。

消息Message是由通信的双方所需要传递的信息。

队列的类型主要包括一下几种:

“公共队列”在整个“消息队列”网络中复制,并且有可能由网络连接的所有站点访问。

“专用队列”不在整个网络中发布。相反,它们仅在所驻留的本地计算机上可用。专用队列只能由知道队列的完整路径名或标签的应用程序访问。

“管理队列”包含确认在给定“消息队列”网络中发送的消息回执的消息。指定希望 MessageQueue 组件使用的管理队列(如果有的话)。

“响应队列”包含目标应用程序接收到消息时返回给发送应用程序的响应消息。指定希望 MessageQueue 组件使用的响应队列(如果有的话)。

优点:稳定、消息优先级、脱机能力以及安全性,有保障的消息传递和执行许多业务处理的可靠的防故障机制。

缺点:MSMQ不适合于Client需要Server端实时交互情况.大量请求时候,响应延迟.


下面是代码

消息队列管理器

using System;
using System.Collections.Generic;
using System.Text;
using System.Messaging;
using System.Configuration;

namespace MSMQTest.Model
{    
    /// <summary>
    /// 消息队列管理器
    /// 作者:心海巨澜 xinhaijulan@gmail.com
    /// </summary>
    public class MSMQManager : IDisposable
    {
        #region 字段与属性
        private MessageQueue _msmq = null;
        private string _path;
     
        private static MSMQManager _instanceLocalComputer = new MSMQManager(true);
        /// <summary>
        /// 本机消息队列实例
        /// </summary>
        public static MSMQManager InstanceLocalComputer
        {
            get { return MSMQManager._instanceLocalComputer; }
        }

        private static MSMQManager _instance = new MSMQManager(false);
        /// <summary>
        /// 远程消息队列实例
        /// </summary>
        public static MSMQManager Instance
        {
            get { return MSMQManager._instance; }
        }
        #endregion

        /// <summary>
        /// 创建队列
        /// </summary>
        /// <param name="transactional">是否启用事务</param>
        /// <returns></returns>
        public bool Create(bool transactional)
        {
            if (MessageQueue.Exists(@".\private$\" + (ConfigurationManager.AppSettings["MSMQName"] ?? "CSMSMQ")))
            {
                return true;
            }
            else
            {
                if (MessageQueue.Create(@".\private$\" + (ConfigurationManager.AppSettings["MSMQName"] ?? "CSMSMQ"), transactional) != null)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// 实例化消息队列
        /// </summary>
        /// <param name="isLocalComputer">是否为本机</param>
        public MSMQManager(bool isLocalComputer)
        {
            if (isLocalComputer)
            {
                _path = @".\private$\" + (ConfigurationManager.AppSettings["MSMQName"] ?? "CSMSMQ");
            }
            else
            {
                _path = @"FormatName:DIRECT=TCP:192.168.1.125\private$\" + (ConfigurationManager.AppSettings["MSMQName"] ?? "CSMSMQ");
            }

            _msmq = new MessageQueue(_path);
        }


        /// <summary>
        /// 发送消息队列
        /// </summary>
        /// <param name="msmqIndex">消息队列实体</param>
        /// <returns></returns>
        public void Send(MSMQIndex msmqIndex)
        {
            _msmq.Send(new Message(msmqIndex, new BinaryMessageFormatter()));
        }

        /// <summary>
        /// 接收消息队列,删除队列
        /// </summary>
        /// <returns></returns>
        public MSMQIndex ReceiveAndRemove()
        {
            MSMQIndex msmqIndex = null;
            _msmq.Formatter = new BinaryMessageFormatter();
            Message msg = null;
            try
            {
                msg = _msmq.Receive(new TimeSpan(0, 0, 1));
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            if (msg != null)
            {
                msmqIndex = msg.Body as MSMQIndex;
            }
            return msmqIndex;
        }

        #region IDisposable Members

        public void Dispose()
        {
            if (_msmq != null)
            {
                _msmq.Close();
                _msmq.Dispose();
                _msmq = null;
            }
        }

        #endregion
    }
}




下面再看看WCF对MSMQ的支持(来自老徐的)


全文结构是:【1】MSMQ基本概念【2】WCF消息队列MSMQ的优势【3】WCF 消息队列MSMQ通信框架【4】安装配置注意事项【5】示例代码 【总结】

 【1】MSMQ基本概念:

    MSMQ全称MicroSoft Message Queue,微软消息队列,是在多个不同的应用之间实现相互通信的一种异步传输模式,相互通信的应用可以分布于同一台机器上,也可以分布于相连的网络空间中的任一位置。它的实现原理是:消息的发送者把自己想要发送的信息放入一个容器中(我们称之为Message),然后把它保存至一个系统公用空间的消息队列(Message Queue)中;本地或者是异地的消息接收程序再从该队列中取出发给它的消息进行处理。

【2】WCF消息队列MSMQ的优势:

      消息队列MSMQ的优点:稳定、消息优先级、脱机能力以及安全性,有保障的消息传递和执行许多业务处理的可靠的防故障机制。 因此消息队列是实现SOA面向服务架构的重要组件之一。WCF框架提供了和MSMQ集成与扩展的能力。这一点也是WCF在特性中明确指出的。MSMQ支持离线消息模式,而且在WCF框架下,提供了基于http桥的internet网络队列服务的调用扩展。和MSMQ框架的结合和扩展,使得WCF服务具有的新的特点:

【2.1】Availabiliy:可用性。这个是MSMQ离线消息的一种体现。客户单和服务端不需要实时进行连接,然后进行消息的交互.WCF 客户端可以发送请求到离线服务端,服务上线以后在相应客户端请求。

【2.2】Disjoint:分解。可以讲工作分解为多个操作,一次放入队列。改善系统的可用性和吞吐量。

【2.3】Compensating:补偿。对于多业务事务,可以提供单独的事物提供其它事务失败的善后处理。

【2.4】Load Leveling:负载平衡。可以把过载的客户端请求放入队列,空闲的时候进行处理,平衡系统的吞吐量,改善性能。

【3】WCF 消息队列MSMQ通信框架:

       WCF使用NetMsmqBinding来支持消息队列通信。当客户端调用服务时,客户端消息会被封装为MSMQ消息,发送懂到特定的消息队列。服务端宿主在运行转台下会,启动通道侦听器,来检测消息队列消息,如果发现对应的消息,会从队列里取出消息,使用分发器转发给对应的服务。具体的通信架构如图:

    如果宿主离线,消息会被放入队列,等待下一次宿主联机时,在执行消息分发处理,给指定的WCF服务。

【4】安装配置注意事项:

    MSMQ队列几种常见的类型就是:

1.公共队列:在整个消息队列网络中复制,并且有可能由网络连接的所有站点访问。 
2.专用队列:不在整个网络中发布。相反,它们仅在所驻留的本地计算机上可用。专用队列只能由知道队列的完整路径名或标签的应用程序访问。 
3.管理队列:包含确认在给定“消息队列”网络中发送的消息回执的消息。指定希望 MessageQueue 组件使用的管理队列(如果有的话)。 
4.响应队列:包含目标应用程序接收到消息时返回给发送应用程序的响应消息。指定希望 MessageQueue 组件使用的响应队列(如果有的话)。

   这里有几个问题要注意,以前很多人也在配置MSMQ开发环境的时候遇到这个问题。Xp环境下作MSMQ配置开发有很多限制。这里算是做个总结供大家参考:

1.公共队列需要域控制器DC Domain Controller; 
2.私有队列与托管的机器同属本地,不需要DC,成为工作组安装; 
3.私有队列需要禁用安全模式:工作组安装与安全,安全设置需要客户端提供证书,MSMQ传输安全需要使用Windows安全,这里需要使用AD活动目录。

【5】示例代码 :

    今天的示例DEMO程序代码,主要演示的是WCF如何配置和开发一个MSMQ服务程序,实现WCF离线操作,MSMQ事务的部分由于内容较多,这里暂时不涉及。我们只讨论WCF离线操作。

(1)WCF服务代码:

    值得注意的WCF的操作要被定义为单向操作,因为本质上其符合单向操作的特征。异步,离线。无返回值。配置操作契约的时候要添加IsOneWay属性。代码如下:

  // 1.服务契约
    [ServiceContract(Namespace  =   " http://www.cnblogs.com/frank_xl/ " )]
    
public   interface  IWCFMSMQService
    {
        
// 操作契约,必须为单向操作
        [OperationContract(IsOneWay  =   true )]
        
void  SayHelloMSMQ( string  name);

    }
    
// 2.服务类,继承接口。实现服务契约定义的操作
     public   class  WCFMSMQService : IWCFMSMQService
    {
        
public  WCFMSMQService()
        {
            Console.WriteLine(
" WCF MSMQ Service instance was created at:{0} " ,  DateTime.Now);
        }
        
// 实现接口定义的方法
         public   void  SayHelloMSMQ( string  name)
        {
            Console.WriteLine(
" Hello! {0},Calling WCF MSMQ Service Operation at:{1} " , name,DateTime.Now);
            
        }
    }

(2)宿主:

    我的开发环境为XP专业版,工作组模式。这里有个问题要注意,就是安全。MSMQ默认的安全模式是需要证书支持。我们必须在宿主配置文件里给配置为none,简化操作,因为证书需要MSMQ域管理器。MSMQ传输安全需要Windows安全,这个又需要AD活动目录支持,工作组模式下不支持,因此这里我们都设置为none。代码如下:

 

< services >
      
< service behaviorConfiguration = " WCFService.WCFServiceBehavior "
        name
= " WCFService.WCFMSMQService " >
        
< endpoint  
          address
= " net.msmq://localhost/Private/FrankWCFMSMQ "  
          binding
= " netMsmqBinding "          
          contract
= " WCFService.IWCFMSMQService "  bindingConfiguration = " msmq "   >
        
</ endpoint >
       
        
< endpoint address = " mex "  binding = " mexHttpBinding "  contract = " IMetadataExchange "   />
        
< host >
          
< baseAddresses >
            
< add baseAddress = " http://localhost:8001/ " />
          
</ baseAddresses >
        
</ host >
      
</ service >
    
</ services >
    
< behaviors >
      
< serviceBehaviors >
        
< behavior name = " WCFService.WCFServiceBehavior " >
          
< serviceMetadata httpGetEnabled = " true "   />
          
< serviceDebug includeExceptionDetailInFaults = " false "   />
        
</ behavior >
      
      
</ serviceBehaviors >
    
</ behaviors >
    
< bindings >
      
< netMsmqBinding >
        
< binding name = " msmq "  durable = " false "  exactlyOnce = " false " >
          
< security mode = " None " >
            
< transport msmqProtectionLevel = " None " />
            
< message clientCredentialType = " None " />
          
</ security >
        
</ binding >
      
</ netMsmqBinding >
    
</ bindings >

(3)客户端:

    运行客户端,添加服务引用,这里客户端的配置文件要做修改,安全模式也修改和宿主对应,设置为none。客户端测试方案是每个2秒调用一次服务请求。我们这里在宿主端打印了时间。客户端发送消息完毕以后,宿主暂时不启动。等待一段时间。在启动宿主,观察宿主打印的消息。代码如下:

 

             // HTTP NetMsmqBinding_IWCFMSMQService
            WCFMSMQServiceClient wcfServiceProxy  =   new  WCFMSMQServiceClient( " NetMsmqBinding_IWCFMSMQService " );
            
// 通过代理调用SayHello服务,这里及时服务调用服务失败,消息会发送到队列里进行缓存。
            Console.WriteLine( " WCF First Call at:{0} " ,DateTime.Now);
            wcfServiceProxy.SayHelloMSMQ(
" Frank " );
            Thread.Sleep(
2000 ); // 客户端休眠两秒,继续下一次调用
            Console.WriteLine( " WCF Second Call at:{0} " , DateTime.Now);
            wcfServiceProxy.SayHelloMSMQ(
" Frank Xu " );
            Thread.Sleep(
2000 ); // 客户端休眠两秒,继续下一次调用
            Console.WriteLine( " WCF Last Call at:{0} " , DateTime.Now);
            wcfServiceProxy.SayHelloMSMQ(
" Frank Xu Lei " );

(4)运行结果:

   首先是客户端发送到的消息,我们可以再计算机-管理-服务-专用队列里查看到,如图:

    以前的服务要求我们必须启动宿主,不然会出现连接错误。这里为了测试,停留约4分钟后,我们启动宿主。观察宿主打印的消息:

    宿主在联机以后响应了客户端的调用操作。

【总结】

     本节文章主要讲解的是WCF 如何使用MSMQ开发离线服务操作。这里除了回顾了MSMQ的基本概念,还介绍了WCF服务使用MSMQ的通信框架的优势和特点。最后给出了基于WCF 消息队列的离线服务调用实现的过程。

    WCF 对MSMQ消息队列的支持和扩展,大大提高了WCF服务调用的伸缩性和灵活性。本文由于开发环境的关系,没有给出公共队列的开发实现过程。另外在WCF 消息队列开发过程中,大家要做注意。

(1)在服务宿主端启动宿主,要做队列存在的判断,以免出现读取错误。

(2)队列服务涉及到安全的问题,认证和消息加密,签名,需要证书的配置。条件限制这里就没有给出具体的实现过程。

(3)WCF队列服务的另外一个重要概念就是对离线事务的支持。这一特性会在后续文章里给出。

(4)运行环境:Xp pro,.net 3.0以上,vs2008,至少安装MSMQ队列的私有队列组件服务。

     最后给出本文的参考代码:/Files/frank_xl/WCCFServiceMSMQFrankXuLei.rar


转自

http://www.cnblogs.com/tenghoo/archive/2009/11/05/1596456.html

http://www.cnblogs.com/xinhaijulan/archive/2010/08/22/1805768.html

http://blog.csdn.net/frankxulei/article/details/4735893



Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐