开源地址:https://github.com/tangxuehua/enode
上一篇文章,介绍了enode框架的物理部署思路。本文我们再简单分析一下Command Service的API设计:
Command Service在enode框架中的地位非常重要,用户使用enode框架的主入口就是command service。UI层如controller会通过发送command给command service,然后框架就开始处理该command。不然看出,command service有可能会被高并发的访问。那么command service该提供什么样的API呢?
首先command service的职责是什么?是处理controller发送过来的command,那如何处理呢?大方向一般就两种,即同步执行command和异步处理command;同步的时候,用户希望command完全处理完才返回,中间如果遇到任何错误,就报异常,然后controller会捕获该异常,然后做后续处理;一般用户希望马上知道command有没有执行成功时,会用同步的方法来执行command。异步处理command,用户只需要把command发送给command service,然后不用等待command处理完成。但是用户可能希望知道command什么处理完成了,所以需要提供一个回调函数,通过回调函数来通知用户某个command处理完成了,一般异步编程的风格都会有回调函数的设计。基于上面的分析,enode的ICommandService接口的设计如下:
/// <summary>Represents a service to send or execute command. /// </summary> public interface ICommandService { /// <summary>Send a command asynchronously. /// </summary> void Send(ICommand command, Action<CommandAsyncResult> callback = null); /// <summary>Execute a command synchronously. /// </summary> void Execute(ICommand command); }
代码应该很清晰了,就不解释了。如果我们追求最快的用户可用性,那可以选择异步执行command,即调用send方法;如果每次都希望command执行完才返回,则可以使用同步执行command,即execute方法;目前框架中同步执行command的实现原理其实是在异步的基础上加了一个同步控制,
优点:
- 框架内部对command的处理流程可以完全一致了,不必因为需要同步和异步的处理而设计重复的代码,当然,我们可以做一些抽象,以减少重复的代码,但实际上这比较困难;
- 内部都是异步处理可以很方便的实现command的重试机制;
- 利用ManualResetEvent实现异步同步化,我们可以很方便的实现command的处理超时控制;
- 因为内部所有command的处理都是异步的,也就是所有的command都在固定的一些队列中排队等候处理,而队列的消费者,即处理command的线程我们在框架启动时就做了配置,所以访问domain in memory的并发线程数量可控,这样我们就可以一定程度上降低并发冲突的可能性;
缺点:
用户调用ICommandService.Execute方法法执行某个command时,他的意图是希望马上执行某个command并返回结果;但是我们现在内部并不是马上处理该command,而是先排队,然后用ManualResetEvent卡住当前线程,然后当command处理完成后,才允许当前线程继续往下走。当然,如果command迟迟未被执行(默认是10秒),则会自动超时,然后返回给command发起者;这样做的坏处是当并发很高的时候,同步执行command可能会超时;对于这一点,在这篇博文中已经对如何提高系统的“高吞吐量、低延迟、高可用”做了比较详细的思路分析,还没看过的朋友可以去看一下。