C#实战Microsoft Messaging Queue(MSMQ)
C#实战Microsoft Messaging Queue(MSMQ)消息队列(干货)
前言
在使用MSMQ之前,我们需要自行安装消息队列组件!(具体安装方法大家自己搜一下吧)
采用MSMQ带来的好处是:由于是异步通信,无论是发送方还是接收方都不用等待对方返回成功消息,就可以执行余下的代码,因而大大地提高了事物处理的能力;当信息传送过程中,信息发送机制具有一定功能的故障恢复能力;MSMQ的消息传递机制使得消息通信的双方具有不同的物理平台成为可能。
MSMQ的基本使用
参考了PetShop里MSMQ的代码,为了考虑到在扩展中会有其他的数据数据对象会使用到MSMQ,因此定义了一个DTcmsQueue的基类,实现消息Receive和Send的基本操作,使用到MSMQ的数据对象需要继承DTcmsQueue基类,需要注意的是:在MSMQ中使用事务的话,需要创建事务性的专用消息队列,代码如下:
using System;
using System.Messaging;
using log4net; namespace DTcms.Web.UI
{
/// <summary>
/// 该类实现从消息对列中发送和接收消息的主要功能
/// </summary>
public class DTcmsQueue : IDisposable { private static ILog logger = LogManager.GetLogger(typeof(DTcmsQueue));
//指定消息队列事务的类型,Automatic 枚举值允许发送外部事务和从处部事务接收
protected MessageQueueTransactionType transactionType = MessageQueueTransactionType.Automatic;
protected MessageQueue queue;
protected TimeSpan timeout;
//实现构造函数
public DTcmsQueue(string queuePath, int timeoutSeconds) {
Createqueue(queuePath);
queue = new MessageQueue(queuePath);
timeout = TimeSpan.FromSeconds(Convert.ToDouble(timeoutSeconds)); //设置当应用程序向消息对列发送消息时默认情况下使用的消息属性值
queue.DefaultPropertiesToSend.AttachSenderId = false;
queue.DefaultPropertiesToSend.UseAuthentication = false;
queue.DefaultPropertiesToSend.UseEncryption = false;
queue.DefaultPropertiesToSend.AcknowledgeType = AcknowledgeTypes.None;
queue.DefaultPropertiesToSend.UseJournalQueue = false;
} /// <summary>
/// 继承类将从自身的Receive方法中调用以下方法,该方法用于实现消息接收
/// </summary>
public virtual object Receive() {
try
{
using (Message message = queue.Receive(timeout, transactionType))
return message;
}
catch (MessageQueueException mqex)
{
if (mqex.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout)
throw new TimeoutException(); throw;
}
} /// <summary>
/// 继承类将从自身的Send方法中调用以下方法,该方法用于实现消息发送
/// </summary>
public virtual void Send(object msg) {
queue.Send(msg, transactionType);
} /// <summary>
/// 通过Create方法创建使用指定路径的新消息队列
/// </summary>
/// <param name="queuePath"></param>
public static void Createqueue(string queuePath)
{
try
{
if (!MessageQueue.Exists(queuePath))
{
MessageQueue.Create(queuePath, true); //创建事务性的专用消息队列
logger.Debug("创建队列成功!");
}
}
catch (MessageQueueException e)
{
logger.Error(e.Message);
}
} #region 实现 IDisposable 接口成员
public void Dispose() {
queue.Dispose();
}
#endregion
}
}
MSMQ的具体实现方式
上面我们已经创建了DTcmsQueue基类,我们具体实现的时候需要继承此基类,使用消息队列的时候,传递的是一个对象,所以我们首先要创建这个对象,但是需要注意的一点:此对象是必须可序列化的,否则不能被插入到消息队列里,代码如下:
/// <summary>
/// 枚举,操作类型是增加还是删除
/// </summary>
public enum JobType { Add, Remove }
/// <summary>
/// 任务类,包括任务的Id ,操作的类型
/// </summary>
[Serializable]
public class IndexJob
{
public int Id { get; set; }
public JobType JobType { get; set; }
}
在具体的实现类里面,我们只需要继承此基类,然后重写基类的方法,具体代码如下:
using System;
using System.Configuration;
using System.Messaging; namespace DTcms.Web.UI
{ /// <summary>
/// 该类实现从消息队列中发送和接收订单消息
/// </summary>
public class OrderJob : DTcmsQueue { // 获取配置文件中有关消息队列路径的参数
private static readonly string queuePath = ConfigurationManager.AppSettings["OrderQueuePath"];
private static int queueTimeout = 20;
//实现构造函数
public OrderJob()
: base(queuePath, queueTimeout)
{
// 设置消息的序列化采用二进制方式
queue.Formatter = new BinaryMessageFormatter();
} /// <summary>
/// 调用PetShopQueue基类方法,实现从消息队列中接收订单消息
/// </summary>
/// <returns>订单对象 OrderInfo</returns>
public new IndexJob Receive()
{
// 指定消息队列事务的类型,Automatic枚举值允许发送发部事务和从外部事务接收
base.transactionType = MessageQueueTransactionType.Automatic;
return (IndexJob)((Message)base.Receive()).Body;
}
//该方法实现从消息队列中接收订单消息
public IndexJob Receive(int timeout)
{
base.timeout = TimeSpan.FromSeconds(Convert.ToDouble(timeout));
return Receive();
} /// <summary>
/// 调用PetShopQueue基类方法,实现从消息队列中发送订单消息
/// </summary>
/// <param name="orderMessage">订单对象 OrderInfo</param>
public void Send(IndexJob orderMessage)
{
// 指定消息队列事务的类型,Single枚举值用于单个内部事务的事务类型
base.transactionType = MessageQueueTransactionType.Single;
base.Send(orderMessage);
}
}
}
项目中MSMQ的具体应用
将任务添加到消息队列代码就很简单了,没啥好说的,直接上代码:
#region 任务添加
public void AddArticle(int artId)
{
OrderJob orderJob = new OrderJob();
IndexJob job = new IndexJob();
job.Id = artId;
job.JobType = JobType.Add;
logger.Debug(artId + "加入任务列表");
orderJob.Send(job);//把任务加入消息队列
} public void RemoveArticle(int artId)
{
OrderJob orderJob = new OrderJob();
IndexJob job = new IndexJob();
job.JobType = JobType.Remove;
job.Id = artId;
logger.Debug(artId + "加入删除任务列表");
orderJob.Send(job);//把任务加入消息队列
}
#endregion
接下来就是如下得到消息队列的任务,并将任务完成,因为消息队列是系统的一个组件跟我们的项目是完全分开的,我们可以完全独立的完成接收消息队列的任务并处理后来的动作,这样就做到了异步处理,例如做一个Windows Service,更重要的是MSMQ还是一种分布式处理技术,在本项目中,我们主要是开辟了多线程来接收消息队列的任务并处理后来的动作,具体代码如下:
public void CustomerStart()
{
log4net.Config.XmlConfigurator.Configure(); PanGu.Segment.Init(PanGuPath); //声明线程
Thread workTicketThread;
Thread[] workerThreads = new Thread[threadCount]; for (int i = 0; i < threadCount; i++)
{
//创建 Thread 实例
workTicketThread = new Thread(new ThreadStart(ProcessOrders)); // 设置线程在后台工作和线程启动前的单元状态(STA表示将创建并进入一个单线程单元 )
workTicketThread.IsBackground = true;
workTicketThread.SetApartmentState(ApartmentState.STA); //启动线程,将调用ThreadStart委托
workTicketThread.Start();
workerThreads[i] = workTicketThread;
} logger.Debug("进程已经开始启动. 按回车键停止.");
}
private static void ProcessOrders()
{ // 总事务处理时间(tsTimeout )就该超过批处理任务消息的总时间
TimeSpan tsTimeout = TimeSpan.FromSeconds(Convert.ToDouble(transactionTimeout * batchSize)); OrderJob orderJob = new OrderJob();
while (true)
{ // 消息队列花费时间
TimeSpan datetimeStarting = new TimeSpan(DateTime.Now.Ticks);
double elapsedTime = 0; int processedItems = 0; ArrayList queueOrders = new ArrayList(); using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required, tsTimeout))
{
// 接收来自消息队列的任务消息
for (int j = 0; j < batchSize; j++)
{ try
{
//如果有足够的时间,那么接收任务,并将任务存储在数组中
if ((elapsedTime + queueTimeout + transactionTimeout) < tsTimeout.TotalSeconds)
{
queueOrders.Add(orderJob.Receive(queueTimeout));
}
else
{
j = batchSize; // 结束循环
} //更新已占用时间
elapsedTime = new TimeSpan(DateTime.Now.Ticks).TotalSeconds - datetimeStarting.TotalSeconds;
}
catch (TimeoutException)
{ //结束循环因为没有可等待的任务消息
j = batchSize;
}
} //从数组中循环取出任务对象,并将任务插入到数据库中
for (int k = 0; k < queueOrders.Count; k++)
{
SearchHelper sh = new SearchHelper();
sh.IndexOn((IndexJob)queueOrders[k]);
processedItems++;
totalOrdersProcessed++;
} //指示范围中的所有操作都已成功完成
ts.Complete();
}
//完成后显示处理信息
logger.Debug("(线程 Id " + Thread.CurrentThread.ManagedThreadId + ") 批处理完成, " + processedItems + " 任务, 处理花费时间: " + elapsedTime.ToString() + " 秒.");
}
}
结束语
以上就是我在实际项目中使用MSMQ的一些心得,希望对各位看官有所帮助,MSMQ具体实现代码上面已经贴出来了,由于本项目是《动力起航》的源代码,一方面由于文件过大,另一方面不知道是不是会侵权,所有没有提供下载.如果有需要的朋友可以留下邮箱我将发给你,但仅供学习交流之用,误用做商业用途,以上如果有侵权等问题还请及时告知我,以便我及时更正!
另外此项目源代码已上传至搜索技术交流群:77570783,源代码已上传至群共享,需要的朋友,请自行下载!
如果觉得好的话请给个推荐哦~~~~亲
C#实战Microsoft Messaging Queue(MSMQ)的更多相关文章
- C#实战Microsoft Messaging Queue(MSMQ)消息队列(干货)
前言 在使用MSMQ之前,我们需要自行安装消息队列组件!(具体安装方法大家自己搜一下吧) 采用MSMQ带来的好处是:由于是异步通信,无论是发送方还是接收方都不用等待对方返回成功消息,就可以执行余下的代 ...
- C#实战Microsoft Messaging Queue(MSMQ)消息队列
前言 在使用MSMQ之前,我们需要自行安装消息队列组件!(具体安装方法大家自己搜一下吧) 采用MSMQ带来的好处是:由于是异步通信,无论是发送方还是接收方都不用等待对方返回成功消息,就可以执行余下的代 ...
- C#实战Microsoft Messaging Queue(MSMQ)消息队列(干货)<转>
前言 在使用MSMQ之前,我们需要自行安装消息队列组件!(具体安装方法大家自己搜一下吧) 采用MSMQ带来的好处是:由于是异步通信,无论是发送方还是接收方都不用等待对方返回成功消息,就可以执行余下的代 ...
- 微软消息队列-MicroSoft Message Queue(MSMQ)队列的C#使用
目录 定义的接口 接口实现 建立队列工厂 写入队列 获取消息 什么是MSMQ Message Queuing(MSMQ) 是微软开发的消息中间件,可应用于程序内部或程序之间的异步通信.主要的机制是:消 ...
- MSMQ(Microsoft Message Queue)
http://www.cnblogs.com/sk-net/archive/2011/11/25/2232341.html 利用 MSMQ(Microsoft Message Queue),应用程序开 ...
- [读书笔记]项目管理实战:Microsoft Project精髓与方法
<项目管理实战:Microsoft Project精髓与方法>是Bonnie Biafore 写的一本书.Bonnie Biafore 作为项目管理师(PMP),她有20余年为大中小型客户 ...
- Microsoft .NET Pet Shop 4: Migrating an ASP.NET 1.1 Application to 2.0
249 out of 297 rated this helpful - Rate this topic Gregory Leake Microsoft Corporation Alan Le, Ale ...
- 【c#】队列(Queue)和MSMQ(消息队列)的基础使用
首先我们知道队列是先进先出的机制,所以在处理并发是个不错的选择.然后就写两个队列的简单应用. Queue 命名空间 命名空间:System.Collections,不在这里做过多的理论解释,这个东西非 ...
- MSMQ 学习(1)
在 Windows Server 2008 or Windows Server 2008 R2 上安装消息队列 4.0 在服务器管理器中,单击“功能”. 在“功能摘要”下的右窗格中,单击“添加功能”. ...
随机推荐
- 谷歌、火狐浏览器下实现JS跨域iframe高度自适应的完美解决方法,跨域调用JS不再是难题!
谷歌.火狐浏览器下实现JS跨域iframe高度自适应的解决方法 导读:今天开发的时候遇到个iframe自适应高度的问题,相信大家对这个不陌生,但是一般我们都是在同一个项目使用iframe嵌套页面,这个 ...
- linux_cp_远程copy
1:远程copy [linux对linux 远程拷贝] scp 文件名 root@远程ip:/路径/ 将本地home目录下的test.tar的文件拷贝到远程主机192.168.1.23的 ...
- java OutOfMorryError (replaceAll)
最近在使用string类中的replaceAll函数时碰到这个错误,由于string长度比较长,文本文档9M多,可以增加jvm的内存大小解决. 下面是一篇对OutOfMorryError错误的一些处理 ...
- ZOJ 2724 Windows 消息队列 (优先队列)
链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2724 Message queue is the basic fund ...
- WebBrowser控件使用详解
原文:WebBrowser控件使用详解 方法 说明 GoBack 相当于IE的“后退”按钮,使你在当前历史列表中后退一项 GoForward 相当于IE的“前进”按钮,使你在当前历史列表中前进一项 G ...
- SQL Server调优系列基础篇(常用运算符总结)
原文:SQL Server调优系列基础篇(常用运算符总结) 前言 上一篇我们介绍了如何查看查询计划,本篇将介绍在我们查看的查询计划时的分析技巧,以及几种我们常用的运算符优化技巧,同样侧重基础知识的掌握 ...
- 当有多于64合乎逻辑的cpu时刻,Windows 下一个Oracle db 实例启动(startup)什么时候会hang(待定)
Bug 9772171 - Database startup hangs on Windows when machine has more than 64 cores [ID 9772171.8] 该 ...
- EasyMonkeyDevice vs MonkeyDevice&HierarchyViewer API Mapping Matrix
1. 前言 本来这次文章的title是写成和前几篇类似的<EasyMonkeyDevice API实践全记录>,内容也打算把每个API的实践和建议给记录下来,但后来想了下觉得这样子并不是最 ...
- Installshield获取安装包版本的系统变量是IFX_PRODUCT_VERSION
原文:Installshield获取安装包版本的系统变量是IFX_PRODUCT_VERSION Installshield获取安装包版本的系统变量为IFX_PRODUCT_VERSION 当笔记记下 ...
- 【UVA】10285-Longest Run on a Snowboard(动态规划)
这是一个简单的问题.你并不需要打印路径. 状态方程dp[i][j] = max(dp[i-1][j],dp[i][j-1],dp[i+1][j],dp[i][j+1]); 14003395 10285 ...