enode框架入门:消息队列的设计思路2013-11-26 博客园 netfocus上一篇文章,简单介绍了enode框架内部的整体实现思路,用到了staged event-driven architecture的思 想。通过前一篇文章,我们知道了enode内部有两种队列:command queue、event queue;用户发送的command 会进入command queue排队,domain model产生的domain event会进入event queue,然后等待被dispatch到所 有的event handlers。本文介绍一下enode框架中这两种消息队列到底是如何设计的。先贴一下enode 框架的内部实现架构图,这样对大家理解后面的分析有帮助。

我们需要什么样的消息队列enode的设计初衷是在单个进程内提供基于DDD+CQRS+EDA的应用开发。 如果我们的业务需要和其他系统交互,那也可以,就是通过在event handler中与其他外部系统交互,比如广 播消息出去或者调用远程接口,都可以。也许将来,enode也会内置支持远程消息通信的功能。但是不支持远 程通信并不表示enode只能开发单机应用了。enode框架需要存储的数据主要有三种:消息,包括command消息和event消息,目前出于性能方面的考虑,是存储在mongodb中;之所以要持久化消 息是因为消息队列里的消息不能丢失;聚合根,聚合根会被序列化,然后存储在内存缓存中,如redis或memcached中;事件,就是由聚合根产生的事件,事件存储在eventstore中,如mongodb中;好,通过上面的分析,我们知道enode框架运行时的所有数据,就存储在mongodb和redis这两个地方。而这 两种存储都是部署在独立的服务器上,与web服务器无关。所以运行enode框架的每台web服务器上是无状态的 。所以,我们就能方便的对web服务器进行集群,我们可以随时当用户访问量的增加时增加新的web服务器,以 提高系统的响应能力;当然,当你发现随着web服务器的增加,导致单台mongodb服务器或单台redis服务器处 理不过来成为瓶颈时,也可以对mongodb和redis做集群,或者对数据做sharding(当然这两种做法不是很好做 ,需要对mongodb,redis很熟悉才行),这样就可以提高mongodb,redis的吞吐量了。好了,上面的分 析主要是为了说明enode框架的使用范围,讨论清楚这一点对我们分析需要什么样的消息队列有很大帮助。现在我们知道,我们完全不需要分布式的消息队列了,比如不需要MSMQ、RabbitMQ,等重量级成熟的 支持远程消息传递的消息队列了。我们需要的消息队列的特征是:基于内存的消息队列;虽然基于内存,但消息不能丢失,也就是消息要支持持久化;消息队列要性能尽量高;消息队列里没有消息的时候,队列的消费者不能让CPU空转,CPU空转会直接导致CPU占用100%,导致机器无 法工作;要支持多个消费者线程同时从队列取消息,但是同一个消息只能被一个消费者处理,也就是一个消息不能 同时被两个消费者取走,也就是要支持并发的dequeue;需要一种设计,实现消息至少会被处理一次;具体指:消息被消费者取走然后被处理的过程中,如果没有 处理成功(消费者自己知道有没有处理成功)或者根本没来得急处理(比如那时正好断电了),那需要一种设 计,可以我们有机会重新消费该消息;因为我们做不到100%不会重复处理一个消息,所以我们的所有消息消费者要尽量做到支持等幂操作,就是 重复的操作不会引起副作用;比如插入前先查询是否存在就是一种支持等幂的措施;这一点,框架会尽量提供 支持等幂的逻辑,当然,用户自己在设计command handler或event handler时,也要尽量考虑等幂的问题。注 意:一般command handler不用考虑,我们主要要考虑的是event handler。原因,下次文章中再细谈吧。