对LMAX架构以及Event Sourcing模式的一些新思考和问题的记录

最近又学习了一下LMAX架构,让我对该架构以及event sourcing模式又有了很多新的认识和疑问。

注:如果不知道什么是lmax架构和event sourcing模式的看官可以自己先去查查资料:

  • LMAX可以看看martin写的一篇文章:http://martinfowler.com/articles/lmax.html
  • Event Sourcing的资料比较多,随便google一下即可。
  • 当然,我的博客里也有大量关于这两个方面的笔记,有兴趣的可以看看。

下面是我的一些最新的想法。

LMAX architecture:input event + business logic processor(BLP) + output event

架构主要执行过程:
首先input event由上层(如controller)创建并最后统一汇集到input disruptor(一个并发控制组件),然后BLP在单个线程内处理所有的input event,一般处理的情况有:1)简单时,直接让aggregate 处理,处理完之后aggregate会产生output event;2)如果是复杂的过程,如long running process,则通过saga的方式来控制整个业务流程;首先也是由aggregate来处理input event,然后产生的output event会由saga响应,然后saga会根据流程逻辑决定接下来要做什么,即产生哪个input event;实际上我把saga也看成是一种聚合,因为saga也有状态,saga表达了一个流程的处理状态,saga也有唯一标识,saga也需要被持久化;总之,BLP在处理完input event后会产生output event。然后这些output event会被某些关心的event handler处理;另外有些event handler在处理output event时又会产生另外的input event并最终也发送到input disruptor,整个过程大概是这样。不知我理解的是否正确。

下面针对我上面的理解再做一些总结:

  1. 整个过程有下面这几个主要元素构成:input event + BLP(包含aggregate,saga) + output event;
  2. input event,output event用于消息(message)传递,实际上他们都属于消息,并且也都是domain event?
  3. BLP用于处理业务逻辑(由aggregate负责)和流程控制逻辑(由saga负责);
  4. aggregate产生output event,output event会最终被发送到output disruptor;
  5. output event有两个主要作用:1)可以让领域外知道领域内发生了什么;2)可以通过output event串联某些复杂的业务过程,如银行转账,如提交订单,etc;
  6. 值得注意的是:整个BLP(saga+aggregate)是in-memory的,重建BLP是用input event来实现,而不是output event;这也是为什么LMAX架构中在BLP处理input event之前必须先通过一个叫journaler的东西持久化input event的原因。目的就是为了在需要的时候利用这些input event通过event sourcing(事件溯源)模式重建整个BLP。其实这个行为更直白的理解就是让BLP再重新处理一遍所有的input event;当然,在重建过程中对于任何要访问外部系统接口的地方,都要禁止访问,否则会带来问题,尤其是更新外部系统的时候,这个其实比较简单,只要设计一个gateway即可,重建blp的时候设置一下该gateway即可。

接下来我想阐述一些我觉得自己比较纠结的地方:

event sourcing的中文解释是事件溯源,关键是如何理解溯源?我的理解是:根据已经发生的事情来重现历史。如果这个理解是正确的,那何为已经发生的事情?lmax是通过input event来溯源,也就是说Lmax认为已经发生的事情是input event,而非output event,即LMAX认为已经发生是指只要input event一旦被创建就表示事情已经发生了,即已经发生是针对用户而言的,如用户提交了订单,那就是OrderSubmitted,用户点击了修改资料的保存按钮,那就是UserProfileChangeRequested;而我们之前的做法是根据aggregate产生的output event来溯源,即我们认为已经发生是相对aggregate而言的;那么到底哪种思路更好呢?虽然两种做法都能最终还原BLP。但就我个人理解,我觉得lmax的做法更合理,实际上如果让LMAX和CQRS架构的command端做对比,那么input event相当于command,只不过command一般都是动词,所以就是CreateOrder,ChangeUserProfile。所以可以理解为lmax架构实际上是在replay command;所以问题就演变为我们到底应该replay command还是replay event?想想replay是谁在replay?是聚合根,这点毫无疑问。另外,replay从语义上来说实际上就是和play做的事情是一样的,只不过是“重做”的意思。那么要理解重做首先要理解什么是“做”?我对“做”的理解就是执行行为并改变状态。所以“重做”就是重新重新执行行为并改变状态;replay command相当于是在重做别人给aggregate一些命令;而replay event相当于是在重做aggregate自己以前曾今做过的一些事情。其实,最重要的一点是,到底要重做什么?是重做用户的要求(what user want to do)还是重做聚合根内已经发生的事情(what domain has happened.),这个问题的回答直接决定到底该replay command 还是 replay event,呵呵。所以,按照这样的思路来思考就很明显了,LMAX是在重做用户的要求,而我们之前的replay event则是在重做聚合根内已经发生的事情。如果我认为重做应该是重做用户的要求,那replay event就不是真正意义上的重做了,而仅仅只是改变状态。举例:假设有一个Note聚合根,有一个ChangeTitle的公共方法,然后还有一个ChangeTitleCommand,ChangeTitleCommand的handler会调用Note的ChangeTitle方法;另外Note还有一个OnTitleChanged的私有方法,用于响应TitleChanged事件。如果是replay command,那会导致ChangeTitle会被重新调用,这就是重做用户的要求;而如果是replay event,则只有OnTitleChanged方法被重新调用,也就是说只是在重做聚合根内已经发生的事情。思考到这里,我不得不承认第一个思考出这种思路的人很厉害,因为他用了这种绕个弯的做法(将本来可以放在一个方法内一次性完成的任务(先改状态然后再产生output event))拆分为两个步骤,第一步是先仅仅产生一个TitleChanged的事件,第二步才是响应该事件并作出状态改变。这样拆分的目的是可以让第二步的方法(OnTitleChanged方法)可以用于event sourcing。另外,这两步对聚合根外部来说是透明的,因为外部根本不知道内部是通过两个步骤来实现的。不得不承认这种做法在replay的时候远比replay command要容易的多,因为所有的aggregate的内部事件响应函数都不会涉及与任何外部系统的交互。虽然这种做法挺好,但是我觉得我们非常有必要搞清楚这两种不同的event sourcing的区别。

另一方面,从确保event必须被持久化的角度来讲:我觉得LMAX的架构,即replay command的好处是,可以很容易在进入BLP之前持久化command,真正做到在BLP处理之前确保所有事件已经被持久化了;而如果是replay event,那我们就没办法实现一个in-memory的BLP了,因为首先BLP是in-memory的,即没有任何IO,但是我们又要求必须持久化output event。那怎么办呢?如果是同步的方式持久化output event,那就不是in-memory了;如果是异步的方式来持久化output event,那虽然可以做到in-memory,但怎么确保output event一定已经被持久化了呢?

目前就这些了,以后有更多的思考内容再补充上来。

-------------------------------------------------------------------------------------------------------------------------------

后来又做了一些思考。

想来想去,最终还是倾向于应该通过output event来做event sourcing。因为毕竟只有output event才真正表示domain aggregate认可的可以发生的domain event。而我们要重建的就是聚合根,到底是应该通过重复执行用户的命令来让模型达到最新状态还是通过让聚合根重新执行已经发生过的事情呢?现在想来,应该是后者。虽然前者也可以,但是要付出的代价相对比较大,比如重建时要禁用外部系统的调用,最麻烦的还是重启发布时的很多细节问题要考虑;而通过聚合根已经发生的事情来重建,则相对很容易,因为重建时不会涉及任何模型之外的东西!

但是因为我们现在采用了in-memory domain的架构,所以传统的基于数据库事务的做法已经无法使用了。所以需要设计另外一种架构确保在domain修改状态之前domain event已经被持久化了,为什么要做这个保证是因为event sourcing+in memory的架构实际上是一种event driven architecture,即整个领域模型的状态的修改都是由事件驱动的,这意味着如果要改变内存中的领域模型的状态,那必须先确保引起该状态修改的domain event必须已经被持久化了。

从用户发起一个command后执行的流程如下:disruptor是并发控制组件,大家可以暂时理解为一个消息队列。如果要进一步了解disruptor,可以看看LMAX架构。

  1. Send ChangeNoteTitleCommand to input disruptor;
  2. Command handler execute method called by input disruptor;
  3. Note.ChangeTitle method called by command handler execute method;
  4. NoteTitleChanged domain event is created and raised in note.ChangeTitle method;
  5. The infrastructure framework send the above NoteTitleChanged event to input disruptor when the raise method is called;
  6. Journal event handler called by input disruptor to persist the event;
  7. Another event handler called by input disruptor to really apply all the note state changes according with the event.
  8. A third event handler called by input disruptor to send the event to the output disruptor;
  9. All the external event handlers are called by the output disruptor; for example, some external event handlers will update the CQRS query side data;

以上步骤必须严格按照上面的顺序一步步执行下来,否则无法确保逻辑正确。
另外,以上流程目前只考虑单台机器,未考虑主备或集群的架构如何实现;之所以用英文写是因为我还要拿去和老外讨论,呵呵。不过这几句英文应该比较简单吧。

时间: 2024-11-03 04:17:02

对LMAX架构以及Event Sourcing模式的一些新思考和问题的记录的相关文章

关于基于Event Sourcing模式实现的领域模型如何处理模型重构的问题的思考

基于DDD+Event Sourcing设计的模型如何处理模型重构? 问题背景:ddd的核心是聚合,一个聚合内包含一些实体,其中一个是根实体,这个大家都有共识:另外,如果将DDD与Event Sourcing结合,那就是一个聚合根会产生一些event:那么这里的问题是:如果一个领域对象,一开始是entity,后来升级为聚合根,但是该entity之前根本没有对应的event,因为它不是聚合根.因此它升级后我们如何通过event sourcing获取升级后的聚合根最新状态:同理,相反的例子是聚合根降

LMAX架构简介

  Reference URL: http://martinfowler.com/articles/lmax.html   该架构主要基于:Disruptor + In Memory DDD + Event Sourcing 通过高并发框架(Disruptor)实现用户事件的输入和Domain Event的输出: 一个常驻内存的Business Logic Processor(DDD领域模型),它负责在纯内存中处理业务逻辑:关键点:首先确保用户输入事件被持久化到数据库,并定时创建快照,然后在内存

LMAX架构

原文地址:http://martinfowler.com/articles/lmax.html 作者:Martin Fowler 译文地址:http://www.jdon.com/42452 译者:banq LMAX是一种新型零售金融交易平台,它能够以很低的延迟(latency)产生大量交易(吞吐量). 这个系统是建立在JVM平台上,核心是一个业务逻辑处理器,它能够在一个线程里每秒处理6百万订单. 业务逻辑处理器完全是运行在内存中(in-memory),使用事件源驱动方式(event sourc

为什么要使用CQRS和Event Sourcing

Business value of the events, the value of having a log, the fact that the Event Store is additive only. The biggest advantage of this architecture is that it allows you to design your model to meet specific requirements. Typically, the requirements

.NET应用架构设计—表模块模式与事务脚本模式的代码编写

阅读目录: 1.背景介绍 2.简单介绍表模块模式.事务脚本模式 3.正确的编写表模块模式.事务脚本模式的代码 4.总结 1.背景介绍 要想正确的设计系统架构就必须能正确的搞懂每个架构模式的用意,而不是胡子眉毛一把抓.现在有一个现象是什么呢,项目的结构从表面上看是很不错,层分的很合理,其实对业务系统来说也就那么几种层设计方法,但是现在很多项目的逻辑架构的设计不是理想,有很多概念大家并不是很了解,当然也许每个人对技术的追求不同罢了.不管你追求不追求,事实我们还是要去往正确的方向努力才对的. 很多人包

sqlserver-C#asp.net三层架构,怎么才能通过主键查询到相对应的记录

问题描述 C#asp.net三层架构,怎么才能通过主键查询到相对应的记录 类库代码如下: /// 把SqlDataReader对象中的数据读出到List中 /// 转换后的列表对象 private List GetUse(SqlDataReader reader) { List uselist = new List(); while (reader.Read()) { use.UName = reader["UName"].ToString(); use.Password = read

新模式聚合消费新需求 电子商务成内需最大亮点

当前,9374.html">世界经济持续不景气,国内外环境面临新变化.在这种背景下,国务院总理温家宝作2013年政府工作报告时指出,要坚定不移地把扩大内需作为经济发展的长期战略方针.扩大内需的难点和重点在消费,潜力也在消费,扩大居民消费要在提高消费能力.稳定消费预期.增强消费意愿.改善消费环境上下工夫,不断提高消费对经济增长的拉动力. 对于如何扩大居民消费意愿,全国政协委员.致公党中央委员徐晓兰提出,可以利用电子商务这个新途径来扩大内需.尤其是如今网络消费异军突起,在社会消费品零售总额中的

盈利模式决定移动互联网发展趋势:O2O模式下的新思维——“赢在系统,胜在联盟”

趋势网讯: 随着网络巨头纷纷加入移动互联网阵营,移动应用产业正呈现多样化发展趋势,而盈利模式将决定未来.作为新兴产业,以及无量的发展前景,草根创业将迎来新的发展机遇,移动应用以其便捷.灵活性而迅速扩大和普及,从互联网到移动互联网,创业投资者最为担心的仍然是靠什么赚钱?中国互联网公司正面临新的挑战,即盈利模式不够清晰,另外,由于手机.平板电脑等移动端屏幕的客观限制,单靠广告业务为主导营收方式显然吃力,尽管当前移动广告业务正在迅速发展,但移动广告所带来的弊端已经日益凸显. O2O盈利模式的可预见性

中国的保险销售模式正在酝酿新的变革

从20年前的"手提包+自行车",到现在的"互联网+移动终端",中国的保险销售模式正在酝酿新的变革.互联网.大数据时代的到来给金融业造成的革命性.颠覆性的变化正在发酵,而保险业身处其中亦不例外. 2012年,"三马卖保险"加速了保险机构进军电子商务,今年以来,不少保险公司纷纷提出建设电商渠道计划. "我们会做互联网方面的业务."人保寿险总裁李良温近日对<第一财经日报>记者表示;而新华保险此前公告称将投入1亿元,自建电