前言

在使用MSMQ之前,我们需要自行安装消息队列组件!(具体安装方法大家自己搜一下吧)

采用MSMQ带来的好处是:由于是异步通信,无论是发送方还是接收方都不用等待对方返回成功消息,就可以执行余下的代码,因而大大地提高了事物处理的能力;当信息传送过程中,信息发送机制具有一定功能的故障恢复能力;MSMQ的消息传递机制使得消息通信的双方具有不同的物理平台成为可能。

       在微软的.net平台上利用其提供的MSMQ功能,可以轻松创建或者删除消息队列、发送或者接收消息、甚至于对消息队列进行管理
 
       MSMQ队列是一个可持久的队列,因此不必担心不间断地插入队列会导致数据的丢失,在网站系统中不用担心网站挂掉后数据丢失问题
 
       MSMQ缺点:不适合于Client需要Server端实时交互情况.大量请求时候,响应延迟,另外这是微软的东西,你懂得........
 
       下面主要讲一讲我在实战MSMQ的一些心得和体会跟大家分享一下:主要是在我的完整的站内搜索Demo(Lucene.Net+盘古分词)项目的基础上进行改造,然后使用了MSMQ,也让大家能够在真正的项目中感受到MSMQ该如何使用和起到的作用!

MSMQ的基本使用

参考了PetShop里MSMQ的代码,为了考虑到在扩展中会有其他的数据数据对象会使用到MSMQ,因此定义了一个DTcmsQueue的基类,实现消息Receive和Send的基本操作,使用到MSMQ的数据对象需要继承DTcmsQueue基类,需要注意的是:在MSMQ中使用事务的话,需要创建事务性的专用消息队列,代码如下:

  1. using System;
  2. using System.Messaging;
  3. using log4net;
  4.  
  5. namespace DTcms.Web.UI
  6. {
  7. /// <summary>
  8. /// 该类实现从消息对列中发送和接收消息的主要功能
  9. /// </summary>
  10. public class DTcmsQueue : IDisposable {
  11.  
  12. private static ILog logger = LogManager.GetLogger(typeof(DTcmsQueue));
  13. //指定消息队列事务的类型,Automatic 枚举值允许发送外部事务和从处部事务接收
  14. protected MessageQueueTransactionType transactionType = MessageQueueTransactionType.Automatic;
  15. protected MessageQueue queue;
  16. protected TimeSpan timeout;
  17. //实现构造函数
  18. public DTcmsQueue(string queuePath, int timeoutSeconds) {
  19. Createqueue(queuePath);
  20. queue = new MessageQueue(queuePath);
  21. timeout = TimeSpan.FromSeconds(Convert.ToDouble(timeoutSeconds));
  22.  
  23. //设置当应用程序向消息对列发送消息时默认情况下使用的消息属性值
  24. queue.DefaultPropertiesToSend.AttachSenderId = false;
  25. queue.DefaultPropertiesToSend.UseAuthentication = false;
  26. queue.DefaultPropertiesToSend.UseEncryption = false;
  27. queue.DefaultPropertiesToSend.AcknowledgeType = AcknowledgeTypes.None;
  28. queue.DefaultPropertiesToSend.UseJournalQueue = false;
  29. }
  30.  
  31. /// <summary>
  32. /// 继承类将从自身的Receive方法中调用以下方法,该方法用于实现消息接收
  33. /// </summary>
  34. public virtual object Receive() {
  35. try
  36. {
  37. using (Message message = queue.Receive(timeout, transactionType))
  38. return message;
  39. }
  40. catch (MessageQueueException mqex)
  41. {
  42. if (mqex.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout)
  43. throw new TimeoutException();
  44.  
  45. throw;
  46. }
  47. }
  48.  
  49. /// <summary>
  50. /// 继承类将从自身的Send方法中调用以下方法,该方法用于实现消息发送
  51. /// </summary>
  52. public virtual void Send(object msg) {
  53. queue.Send(msg, transactionType);
  54. }
  55.  
  56. /// <summary>
  57. /// 通过Create方法创建使用指定路径的新消息队列
  58. /// </summary>
  59. /// <param name="queuePath"></param>
  60. public static void Createqueue(string queuePath)
  61. {
  62. try
  63. {
  64. if (!MessageQueue.Exists(queuePath))
  65. {
  66. MessageQueue.Create(queuePath, true); //创建事务性的专用消息队列
  67. logger.Debug("创建队列成功!");
  68. }
  69. }
  70. catch (MessageQueueException e)
  71. {
  72. logger.Error(e.Message);
  73. }
  74. }
  75.  
  76. #region 实现 IDisposable 接口成员
  77. public void Dispose() {
  78. queue.Dispose();
  79. }
  80. #endregion
  81. }
  82. }

MSMQ的具体实现方式

上面我们已经创建了DTcmsQueue基类,我们具体实现的时候需要继承此基类,使用消息队列的时候,传递的是一个对象,所以我们首先要创建这个对象,但是需要注意的一点:此对象是必须可序列化的,否则不能被插入到消息队列里,代码如下:

  1. /// <summary>
  2. /// 枚举,操作类型是增加还是删除
  3. /// </summary>
  4. public enum JobType { Add, Remove }
  5. /// <summary>
  6. /// 任务类,包括任务的Id ,操作的类型
  7. /// </summary>
  8. [Serializable]
  9. public class IndexJob
  10. {
  11. public int Id { get; set; }
  12. public JobType JobType { get; set; }
  13. }

在具体的实现类里面,我们只需要继承此基类,然后重写基类的方法,具体代码如下:

  1. using System;
  2. using System.Configuration;
  3. using System.Messaging;
  4.  
  5. namespace DTcms.Web.UI
  6. {
  7.  
  8. /// <summary>
  9. /// 该类实现从消息队列中发送和接收订单消息
  10. /// </summary>
  11. public class OrderJob : DTcmsQueue {
  12.  
  13. // 获取配置文件中有关消息队列路径的参数
  14. private static readonly string queuePath = ConfigurationManager.AppSettings["OrderQueuePath"];
  15. private static int queueTimeout = 20;
  16. //实现构造函数
  17. public OrderJob()
  18. : base(queuePath, queueTimeout)
  19. {
  20. // 设置消息的序列化采用二进制方式
  21. queue.Formatter = new BinaryMessageFormatter();
  22. }
  23.  
  24. /// <summary>
  25. /// 调用PetShopQueue基类方法,实现从消息队列中接收订单消息
  26. /// </summary>
  27. /// <returns>订单对象 OrderInfo</returns>
  28. public new IndexJob Receive()
  29. {
  30. // 指定消息队列事务的类型,Automatic枚举值允许发送发部事务和从外部事务接收
  31. base.transactionType = MessageQueueTransactionType.Automatic;
  32. return (IndexJob)((Message)base.Receive()).Body;
  33. }
  34. //该方法实现从消息队列中接收订单消息
  35. public IndexJob Receive(int timeout)
  36. {
  37. base.timeout = TimeSpan.FromSeconds(Convert.ToDouble(timeout));
  38. return Receive();
  39. }
  40.  
  41. /// <summary>
  42. /// 调用PetShopQueue基类方法,实现从消息队列中发送订单消息
  43. /// </summary>
  44. /// <param name="orderMessage">订单对象 OrderInfo</param>
  45. public void Send(IndexJob orderMessage)
  46. {
  47. // 指定消息队列事务的类型,Single枚举值用于单个内部事务的事务类型
  48. base.transactionType = MessageQueueTransactionType.Single;
  49. base.Send(orderMessage);
  50. }
  51. }
  52. }

项目中MSMQ的具体应用

将任务添加到消息队列代码就很简单了,没啥好说的,直接上代码:

  1. #region 任务添加
  2. public void AddArticle(int artId)
  3. {
  4. OrderJob orderJob = new OrderJob();
  5. IndexJob job = new IndexJob();
  6. job.Id = artId;
  7. job.JobType = JobType.Add;
  8. logger.Debug(artId + "加入任务列表");
  9. orderJob.Send(job);//把任务加入消息队列
  10. }
  11.  
  12. public void RemoveArticle(int artId)
  13. {
  14. OrderJob orderJob = new OrderJob();
  15. IndexJob job = new IndexJob();
  16. job.JobType = JobType.Remove;
  17. job.Id = artId;
  18. logger.Debug(artId + "加入删除任务列表");
  19. orderJob.Send(job);//把任务加入消息队列
  20. }
  21. #endregion

接下来就是如下得到消息队列的任务,并将任务完成,因为消息队列是系统的一个组件跟我们的项目是完全分开的,我们可以完全独立的完成接收消息队列的任务并处理后来的动作,这样就做到了异步处理,例如做一个Windows Service,更重要的是MSMQ还是一种分布式处理技术,在本项目中,我们主要是开辟了多线程来接收消息队列的任务并处理后来的动作,具体代码如下:

  1. public void CustomerStart()
  2. {
  3. log4net.Config.XmlConfigurator.Configure();
  4.  
  5. PanGu.Segment.Init(PanGuPath);
  6.  
  7. //声明线程
  8. Thread workTicketThread;
  9. Thread[] workerThreads = new Thread[threadCount];
  10.  
  11. for (int i = 0; i < threadCount; i++)
  12. {
  13. //创建 Thread 实例
  14. workTicketThread = new Thread(new ThreadStart(ProcessOrders));
  15.  
  16. // 设置线程在后台工作和线程启动前的单元状态(STA表示将创建并进入一个单线程单元 )
  17. workTicketThread.IsBackground = true;
  18. workTicketThread.SetApartmentState(ApartmentState.STA);
  19.  
  20. //启动线程,将调用ThreadStart委托
  21. workTicketThread.Start();
  22. workerThreads[i] = workTicketThread;
  23. }
  24.  
  25. logger.Debug("进程已经开始启动. 按回车键停止.");
  26. }
  27. private static void ProcessOrders()
  28. {
  29.  
  30. // 总事务处理时间(tsTimeout )就该超过批处理任务消息的总时间
  31. TimeSpan tsTimeout = TimeSpan.FromSeconds(Convert.ToDouble(transactionTimeout * batchSize));
  32.  
  33. OrderJob orderJob = new OrderJob();
  34. while (true)
  35. {
  36.  
  37. // 消息队列花费时间
  38. TimeSpan datetimeStarting = new TimeSpan(DateTime.Now.Ticks);
  39. double elapsedTime = 0;
  40.  
  41. int processedItems = 0;
  42.  
  43. ArrayList queueOrders = new ArrayList();
  44.  
  45. using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required, tsTimeout))
  46. {
  47. // 接收来自消息队列的任务消息
  48. for (int j = 0; j < batchSize; j++)
  49. {
  50.  
  51. try
  52. {
  53. //如果有足够的时间,那么接收任务,并将任务存储在数组中
  54. if ((elapsedTime + queueTimeout + transactionTimeout) < tsTimeout.TotalSeconds)
  55. {
  56. queueOrders.Add(orderJob.Receive(queueTimeout));
  57. }
  58. else
  59. {
  60. j = batchSize; // 结束循环
  61. }
  62.  
  63. //更新已占用时间
  64. elapsedTime = new TimeSpan(DateTime.Now.Ticks).TotalSeconds - datetimeStarting.TotalSeconds;
  65. }
  66. catch (TimeoutException)
  67. {
  68.  
  69. //结束循环因为没有可等待的任务消息
  70. j = batchSize;
  71. }
  72. }
  73.  
  74. //从数组中循环取出任务对象,并将任务插入到数据库中
  75. for (int k = 0; k < queueOrders.Count; k++)
  76. {
  77. SearchHelper sh = new SearchHelper();
  78. sh.IndexOn((IndexJob)queueOrders[k]);
  79. processedItems++;
  80. totalOrdersProcessed++;
  81. }
  82.  
  83. //指示范围中的所有操作都已成功完成
  84. ts.Complete();
  85. }
  86. //完成后显示处理信息
  87. logger.Debug("(线程 Id " + Thread.CurrentThread.ManagedThreadId + ") 批处理完成, " + processedItems + " 任务, 处理花费时间: " + elapsedTime.ToString() + " 秒.");
  88. }
  89. }

C#实战Microsoft Messaging Queue(MSMQ)消息队列(干货)的更多相关文章

  1. C#实战Microsoft Messaging Queue(MSMQ)消息队列(干货)<转>

    前言 在使用MSMQ之前,我们需要自行安装消息队列组件!(具体安装方法大家自己搜一下吧) 采用MSMQ带来的好处是:由于是异步通信,无论是发送方还是接收方都不用等待对方返回成功消息,就可以执行余下的代 ...

  2. C#实战Microsoft Messaging Queue(MSMQ)消息队列

    前言 在使用MSMQ之前,我们需要自行安装消息队列组件!(具体安装方法大家自己搜一下吧) 采用MSMQ带来的好处是:由于是异步通信,无论是发送方还是接收方都不用等待对方返回成功消息,就可以执行余下的代 ...

  3. C#实战Microsoft Messaging Queue(MSMQ)

    C#实战Microsoft Messaging Queue(MSMQ)消息队列(干货) 前言 在使用MSMQ之前,我们需要自行安装消息队列组件!(具体安装方法大家自己搜一下吧) 采用MSMQ带来的好处 ...

  4. 跟我一起学WCF(1)——MSMQ消息队列

    一.引言 Windows Communication Foundation(WCF)是Microsoft为构建面向服务的应用程序而提供的统一编程模型,该服务模型提供了支持松散耦合和版本管理的序列化功能 ...

  5. msmq消息队列使用场景

    MSMQ全称是Microsoft Message Queue——微软消息队列. MSMQ是一种通信的机制,因为是一种中间件技术,所以它能够支持多种类型的语言开发,同时也是跨平台的通信机制,也就是说MQ ...

  6. 【转】MSMQ消息队列安装

    一.Windows 7安装.管理消息队列1.安装消息队列   执行用户必须要有本地 Administrators 组中的成员身份,或等效身份.   具体步骤:    开始—>控制面板—>程 ...

  7. MSMQ消息队列安装

    一.Windows 7安装.管理消息队列1.安装消息队列   执行用户必须要有本地 Administrators 组中的成员身份,或等效身份.   具体步骤:    开始—>控制面板—>程 ...

  8. 【6】.net msmq消息队列实例

    1.msmq消息队列windows环境安装 控制面板---->程序和功能---->启用或关闭Windows程序---->Microsoft Message Queue(MSMQ)服务 ...

  9. MSMQ消息队列的安装、启用

    最近研究消息队列,先从微软自带的MSMQ开始,百度如何安装,方式如下: 控制面板---程序和功能--启用和关闭windows功能--Microsoft Message Queue(MSMQ)服务器 默 ...

随机推荐

  1. oracle 判断中文函数

    create or replace function func_chinese(  p_str     in varchar2,     -- 输入的字符串  p_code    in varchar ...

  2. int跟byte[]数组互转的方法,整数 + 浮点型

    整数: int转byte数组 public static byte[] intToBytes2(int n){ ]; ;i < ;i++) { b[i]=(-i*)); } return b; ...

  3. oracle密码错误验证延迟

    补充从10g升级到11g之后需要注意的几个密码方面问题: 1. 11g默认开始密码区分大小写,可以通过把参数设置为SEC_CASE_SENSITIVE_LOGON =FALSE 屏蔽 2. 11g密码 ...

  4. 查看iOS模拟器应用的沙箱文件

    iOS 升级到8.3 以后就不能用iFunBox 这样的工具看沙箱里的文件了(非共享的), 而开发时我们的数据库文件又不在共享目录里.关于这个问题,我们可以看模拟器里的沙箱文件, iOS8.0 以后, ...

  5. MD5 加密的两种方法

    System.Security.Cryptography.MD5CryptoServiceProvider md5 = new System.Security.Cryptography.MD5Cryp ...

  6. GCC 4.8.2 编译安装

      https://my.oschina.net/u/728245/blog/184550 摘要: GCC 4.8.2 在 CentOS 6.5 下编译安装小记,遇到一些问题并解决. 以前从没有升级过 ...

  7. Asp.net Request方法获取客户端的信息

    Response.Write("客户端计算机名:" + Request.UserHostName + "<BR />"); Response.Wri ...

  8. IT项目经理成长手记

    1.流程化,项目化,工业化: 2.启动,规划,实施,监控,收尾.五个过程 3.需求管理,项目策划,项目监控,集成项目管理,定量项目管理,供应商协议管理,风险管理. 4.项目经理是熬出来的,伟大都是熬出 ...

  9. POJ 2524 Ubiquitous Religions

    Ubiquitous Religions Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 20668   Accepted:  ...

  10. nginx 常用配置说明

    一.location 配置 1.1 语法规则: location [=|~|~*|^~] /uri/ { … }= 开头表示精确匹配^~ 开头表示uri以某个常规字符串开头,理解为匹配 url路径即可 ...