半同步半异步模式的实现 - MSMQ实现
半同步半异步模式的实现 - MSMQ实现
所谓半同步半异步是指,在某个方法调用中,有些代码行是同步执行方式,有些代码行是异步执行方式,下面我们来举个例子,还是以经典的PlaceOrder来说,哈哈。
PlaceOrder的主要逻辑:

public bool PlaceOrder(OrderInfo order)
{
//验证Order合法性 //OrderInfo增加到仓储 //生成order的pdf //通知客户,email方式
}

我们假设做出如下决定:

public bool PlaceOrder(OrderInfo order)
{
//验证Order合法性 (同步执行) //OrderInfo增加到仓储 (异步执行,考虑到锁,要放到消息队列中,让后台Worker来执行具体的sql操作) //生成order的pdf (同步执行,其实也可以异步,此处demo意图,因此做成同步执行) //通知客户,email方式 (同步执行,同上)
}

如上面所示,如果我们只是在"OrderInfo增加到仓储"这里通过Async方式(无论是多线程,或者是msmq、rabbitq),如果只是触发这个异步执行,那么到函数返回时,很可能这个异步操作还没有执行完成,但是UI层(又或者其他函数)却需要某些信息,比如OrderID。因此,在这个函数中,除了发起异步调用外,还需要wait,直到异步调用执行完成,其实这个函数相当于完成了2个线程并行执行,但是最终返回的条件必须是2个线程都执行完成。
我们来看下

EventWaitHandle signal = new EventWaitHandle(true, EventResetMode.ManualReset); //多线程之间的阻塞机制,需要这个变量
OrderInfo returnedOrderInfo = null; //从多线程那边返回的变量会保存在这里
public bool PlaceOrder(OrderInfo order)
{
//验证Order合法性 (同步执行) //OrderInfo增加到仓储 (异步执行,考虑到锁,要放到消息队列中,让后台Worker来执行具体的sql操作)
string msgId=SendOrder2Queue(order); //发送order到msmq,并且获取相应的消息ID,因为后面会用到这个消息ID //生成order的pdf //会在这里阻塞,直到应答队列出现相应msgId的消息为止
signal.Reset(); //初始化变量
returnedOrderInfo = null; //初始化变量
ThreadPool.QueueUserWorkItem(new WaitCallback(CheckResponseQueue), msgId); //调用ms的ThreadPool进行多线程
signal.WaitOne(); //阻塞住,直到子线程发出Set命令 //通知客户,email方式 //收尾逻辑
if (this.returnedOrderInfo != null && !this.returnedOrderInfo.OrderID.Equals(Guid.Empty))
{
CloneOrder(order, this.returnedOrderInfo);
return true;
}
return false;
}

需要注意的地方是,发送email那里,必须放在最后面,想象下这种情况:插入数据库失败了,此时msmq的应答队列就会返回相应的失败消息,此时就需要根据结果来判断,是否需要发送email了(上面代码没有考虑到这点)。
考虑到断电等情况,需要将msmq创建为Transaction类型的queue,如下:

public static class Config
{
public static readonly string OrderQueueConnectionString = ".\\private$\\Order";
public static readonly string OrderResponseQueueConnectionString = ".\\private$\\OrderResponse";
public static void PrepareMSMQ()
{
if (MessageQueue.Exists(OrderQueueConnectionString))
MessageQueue.Delete(OrderQueueConnectionString);
if (MessageQueue.Exists(OrderResponseQueueConnectionString))
MessageQueue.Delete(OrderResponseQueueConnectionString); MessageQueue.Create(OrderQueueConnectionString, true); //true,代表Transaction的队列
MessageQueue.Create(OrderResponseQueueConnectionString, true); //同上
}
}

大家已经看到了,其实有2个队列需要建立,一个是发送队列,另外一个是应答队列;ThreadPool中的子线程就是用来Monitor这个应答队列中是否有相应消息的,我们来看看代码:

private void CheckResponseQueue(object state)
{
string msgId = (string)state;
string sMessageConnectionString_ResponseQueue = Config.OrderResponseQueueConnectionString;
MessageQueue mq_response = new MessageQueue(sMessageConnectionString_ResponseQueue);
mq_response.Formatter = new XmlMessageFormatter(new Type[] { typeof(OrderInfo) }); //这个很重要,要传输什么样的消息,就要写相应的格式化程序
while (true)
{
System.Threading.Thread.Sleep(200);
Message[] msgs = mq_response.GetAllMessages(); //这句会获取所有的消息,但是不会从队列中去掉,类似于Peek的效果 string foundMsgId = string.Empty;
foreach(Message msg in msgs)
{
if (msg.Label == msgId)
{
foundMsgId = msg.Id;
break;
}
}
if (foundMsgId != string.Empty)
{
Message msg=mq_response.ReceiveById(foundMsgId); //找到msgId后,这句才会真正从队列中移除消息
OrderInfo replyOrderInfo=(OrderInfo)msg.Body; returnedOrderInfo = new OrderInfo(); //赋值给返回变量
returnedOrderInfo.FirstName = replyOrderInfo.FirstName;
returnedOrderInfo.LastName = replyOrderInfo.LastName;
returnedOrderInfo.BuyWhat = replyOrderInfo.BuyWhat;
returnedOrderInfo.OrderID = replyOrderInfo.OrderID;
break;
}
}
signal.Set(); //发出信号,代表完成,让主线程继续往下执行
}

下面再来看看消息发送后,真正的后台处理程序(是个Console程序):

class Program
{
static void Main(string[] args)
{
System.Threading.Thread.Sleep(2000);
string msmq = Core.Config.OrderQueueConnectionString;
if (!MessageQueue.Exists(msmq))
{
Console.WriteLine("msmq not exist.");
return;
} MessageQueue mq = new MessageQueue(msmq);
MessageQueueTransaction tx = new MessageQueueTransaction(); //事务性队列必须用这个才能正确插入消息
mq.Formatter = new XmlMessageFormatter(new Type[] { typeof(OrderInfo) });
while (true)
{
Message msg = mq.Receive();
Console.WriteLine("processing {0}", msg.Id);
OrderInfo order = (OrderInfo)msg.Body; order.FirstName +=", processed on "+DateTime.Now.ToString();
order.OrderID = Guid.NewGuid(); if (msg.ResponseQueue != null) //由于在发送消息的时候已经指定了应答队列,因此此处只是简单的判断这个属性就可以了
{
Message msg_reply = new Message();
msg_reply.Body = order;
msg_reply.Label = msg.Id;
msg_reply.Formatter = new System.Messaging.XmlMessageFormatter(new Type[] { typeof(OrderInfo) }); tx.Begin(); //很重要
msg.ResponseQueue.Send(msg_reply, tx);
tx.Commit(); //同上
}
Console.WriteLine("done");
}
}
}

我们再来看下主程序:

class Program
{
static void Main(string[] args)
{
Config.PrepareMSMQ(); //准备msmq资源,比如create等 OrderInfo order = new OrderInfo();
order.FirstName = "aaron";
order.LastName = "dai";
order.BuyWhat = "Car"; Console.WriteLine("Old OrderID--->" + order.OrderID);
Console.WriteLine("Old FirstName--->" + order.FirstName); OrderService srv = new OrderService();
bool success=srv.PlaceOrder(order); Console.WriteLine("*******************************");
Console.WriteLine("Processed OrderID--->" + order.OrderID);
Console.WriteLine("Processed FirstName--->"+order.FirstName);
Console.WriteLine("*******************************");
Console.ReadLine();
}
}

就要好了,来看看效果图:

心怀远大理想。
为了家庭幸福而努力。
用A2D科技,服务社会。
半同步半异步模式的实现 - MSMQ实现的更多相关文章
- 分布式缓存系统 Memcached 半同步/半异步模式
在前面工作线程初始化的分析中讲到Memcached采用典型的Master_Worker模式,也即半同步/半异步的高效网络并发模式.其中主线程(异步线程)负责接收客户端连接,然后分发给工作线程,具体由工 ...
- 领导者/追随者(Leader/Followers)模型和半同步/半异步(half-sync/half-async)模型都是常用的客户-服务器编程模型
领导者-追随者(Leader/Followers)模型的比喻 半同步/半异步模型和领导者/追随者模型的区别: 半同步/半异步模型拥有一个显式的待处理事件队列,而领导者-追随者模型没有一个显式的队列(很 ...
- 使用C++11 开发一个半同步半异步线程池
摘自:<深入应用C++11>第九章 实际中,主要有两种方法处理大量的并发任务,一种是一个请求由系统产生一个相应的处理请求的线程(一对一) 另外一种是系统预先生成一些用于处理请求的进程,当请 ...
- 使用C++11实现一个半同步半异步线程池
前言 C++11之前我们使用线程需要系统提供API.posix线程库或者使用boost提供的线程库,C++11后就加入了跨平台的线程类std::thread,线程同步相关类std::mutex.std ...
- c++11 实现半同步半异步线程池
感受: 随着深入学习,现代c++给我带来越来越多的惊喜- c++真的变强大了. 半同步半异步线程池: 事实上非常好理解.分为三层 同步层:通过IO复用或者其它多线程多进程等不断的将待处理事件加入到队列 ...
- (原创)C++半同步半异步线程池2
(原创)C++半同步半异步线程池 c++11 boost技术交流群:296561497,欢迎大家来交流技术. 线程池可以高效的处理任务,线程池中开启多个线程,等待同步队列中的任务到来,任务到来多个线程 ...
- Half Sync And Half Async 半同步半异步模式
如题,这是一个典型的CS结构的模式,至少曾经在CS结构中用过,只是没用好.当年用UDP死活都处理不过来网络命令,用此模式轻松解决. 此模式是典型的三层架构,同步层在一个线程内处理业务逻辑:异步层接受并 ...
- C++11 半同步半异步线程池的实现
#include <list> #include <mutex> #include <thread> #include <condition_variable ...
- C++ 半同步半异步的任务队列
代码已发布至 HAsyncTaskQueue
随机推荐
- 传智播客.Net培训就业班入学测试题
2.对学员的结业考试成绩评测,要求在控制台中提示用户输入学员考试成绩,写一个方法,根据用户输入的成绩,返回一个等级:90分以上A级.80~90分B级.70~80分C级.60~70分B级.60分以下C级 ...
- FPGA 异步时钟处理方
1 假设FPGA设计,包括不同的频率的时钟,它会发出涉及异步时钟. 我们需要一些方法来使时钟同步,从而保证FPGA可靠性设计. 2 在建立和保持时间所构成的有效时间窗体内,数据输入到触发器进行转换. ...
- Enum:枚举
原文:Enum:枚举 枚举 (enum) 是值类型的一种特殊形式,它从 System.Enum 继承而来,并为基础的基元类型的值提供替代名称.枚举类型有名称.基础类型和一组字段.基础类型必须是一个内置 ...
- MVC验证13-2个属性至少输入一项
原文:MVC验证13-2个属性至少输入一项 有时候,我们希望2个属性中,至少有一个是必填,比如: using Car.Test.Portal.Extension; namespace Car.Te ...
- [Elasticsearch] 邻近匹配 (一) - 短语匹配以及slop參数
本文翻译自Elasticsearch官方指南的Proximity Matching一章. 邻近匹配(Proximity Matching) 使用了TF/IDF的标准全文搜索将文档,或者至少文档中的每一 ...
- Hadoop-2.4.1完全分布式环境搭建
Hadoop-2.4.1完全分布式环境搭建 Hadoop-2.4.1完全分布式环境搭建 一.配置步骤如下: 主机环境搭建,这里是使用了5台虚拟机,在ubuntu 13系统上进行搭建hadoop ...
- jQuery 表格
jQuery 表格插件汇总 本文搜集了大量 jQuery 表格插件,帮助 Web 设计者更好地驾御 HTML 表格,你可以对表格进行横向和竖向排序,设置固定表头,对表格进行搜索,对大表格进行分 ...
- 日志之再说Log4J
网上关于LOG4J的使用文章太多了,写这篇文章的目的一方面是为了回顾LOG4J的用法,一方面针对配置的使用自动将日志插入数据库,自动发送邮件,还有就是自定义输入实现.后续文章会总结下从LOG4J到LO ...
- Java泛型和集合之泛型VS模板
Java的泛型很像C++中的模板,说到Java 泛型和C++中的模板的关系时,有两个重要的方面需要被考虑到:语法和语义.语法看起来是相似的,可是语义却明显是不同的. 在语法上讲,选择尖括号 是因为他 ...
- [置顶] NB多项式事件模型、神经网络、SVM之函数/几何间隔——斯坦福ML公开课笔记6
转载请注明:http://blog.csdn.net/xinzhangyanxiang/article/details/9722701 本篇笔记针对斯坦福ML公开课的第6个视频,主要内容包括朴素贝叶斯 ...