半同步半异步模式的实现 - 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
随机推荐
- jquery 仅仅读
大家都理解这是什么,正常的写法例如以下: if (status == true) { $("#minDelistStr").val(totalAmount);// 去掉首部的&qu ...
- Swift语言指南(八)--语言基础之元组
原文:Swift语言指南(八)--语言基础之元组 元组 元组(Tuples)将多个值组合为一个复合值.元组内的值可以是任何类型,各个元素不需要为相同类型(各个元素之间类型独立,互不干扰--Joe.Hu ...
- linux网络编程学习笔记之四 -----多-threaded服务器
对于使用过程中并发.通过实现更轻量级线程. 每个线程都是一个独立的逻辑流. 主题是CPU在执行调度的最小独立单位,这个过程是资源分配单元.当然,这是在微内核操作系统说.总之,这是唯一的一个操作系统内核 ...
- 使用gson和httpclient呼叫微信公众平台API
吐槽:微信api很无语.有一部分xml.有一部分json. 最近看如何调用微信公众平台json有关api更方便.终于找到了httpcliect和gson对. 假设你有一个更好的办法,请告诉我. 了解如 ...
- Android Studio非gradleproject编译后的apk文件在哪?
非gradle的apk文件位置和gradle有一些差别,怎样找到apk文件在哪?我直接上图吧,选中project,右键: 在windows是选择"show in exlporer" ...
- C#框架
从零开始编写自己的C#框架(1)——前言 记得十五年前自学编程时,拿着C语言厚厚的书,想要上机都不知道要用什么编译器来执行书中的例子.十二年前在大学自学ASP时,由于身边没有一位同学和朋友学习这种 ...
- SQL点滴15—在SQL Server 2008中调用C#程序
原文:SQL点滴15-在SQL Server 2008中调用C#程序 T-SQL的在执行普通的查询的时候是很高效的,但是在执行循环,判断这样的语句的时候效率就不那么的高了.这时可以借助CLR了,我们可 ...
- SQL点滴9—SQL Server中的事务处理以及SSIS中的内建事务
原文:SQL点滴9-SQL Server中的事务处理以及SSIS中的内建事务 我们可以把SSIS中的整个package包含在一个事务中,但是如果在package的执行过程中有一个表需要锁定应该怎么处理 ...
- Linux下使用cat制作“内涵图”
http://blog.csdn.net/odaynot/article/details/7939869
- ventBroker简单实现
C#编程实践—EventBroker简单实现 前言 话说EventBroker这玩意已经不是什么新鲜货了,记得第一次接触这玩意是在进第二家公司的时候,公司产品基础架构层中集成了分布式消息中间件,在.n ...