2013-10-12 16:08 "@banq
"的内容
有人担心分布式环境只有一个聚合对象,如果这个聚合访问量大,怎么办呢?目前我想没有理论上无限解决方案,但是按照LMAX架构一台服务器大概每秒处理600万订单来看,基本应付大部分高频交易,所以,首先确定你的业务是否属于高频高事务交易。 ...

关于LMAX,我现在已经想通他的input events,business logic processor, output events了。
business logic processor (blp)类似于ddd中的领域模型,用于处理业务逻辑;
input events,指CQRS架构中的command
output events,指domain event

然后,LMAX的input events交给blp处理前会先持久化到日志中,就是架构图中的journaler做的事情。

martin把通过input events来还原BLP的做法叫做event sourcing。实际上本质是command sourcing。因为这种做法是通过重新执行一个系统的所有的输入事件来还原系统整个状态的架构。

这种做法在关系型数据库或者redis中都是这样的做法。这种做法确实可以做到让blp非常快,且它产生的domain event甚至都不需要持久化了,因为我们已经持久化了input events了。所以LMAX架构中,通过output disruptor把output events异步分发出去。

但是,这种做法实际上是有前提的,那就是BLP的逻辑要不能改!这点非常重要,因为一旦改了逻辑,那再通过input events来还原BLP的状态就不是以前的状态了。但是像数据库SQL或者redis的命令为什么可以这样做呢?因为sql或redis的命令的处理逻辑一定是不变的,也就是相当于那种系统,只要输入是什么,那结果是什么就确定了的。

但是像我们的业务系统,领域模型的结构或者逻辑,比如一个if语句都不能改,否则就会出现无法通过input events还原BLP的风险。

所以,基于以上的考虑,我基本不会考虑使用类似LMAX的架构在DDD的应用程序中,因为我的业务逻辑不可能不修改!

因此,要实现单线程600TPS的架构,对我来说还是个传说,呵呵。

所以,我目前只能还是实现真正的event sourcing,也就是根据output events来还原BLP。这样的方式来还原BLP相对比较靠谱。因为我们的domain结构是不常改的,就算改,也可以做到和以前兼容。而逻辑的改动不影响event sourcing。而要做event sourcing,那就必须持久化domain event。所以,我才会设计为,一定是把domain event持久化完成了才认为事实发生了。

2013-10-12 16:42 "@tangxuehua
"的内容
这种做法实际上是有前提的,那就是BLP的逻辑要不能改! ...

这个问题解决办法我是这样想的:只要BLP的接口方法不变就可以,接口中有两个方法,一个方法是给command调用,还有一个方法是给eventSourcing进行事件回放时用的。业务逻辑都存在这个两个方法具体实现中,因此BLP逻辑应该是可以修改的。

见:http://www.jdon.com/44815中有一段Match代码有两个方法。

继续刚才那个问题。
我觉得在集群分布式的环境下,如果要使用actor模型,那也不是不可能。只要做到有一个非常可靠严谨的路由框架,确保对同一个聚合根的操作,一定是路由到同一个线程。并且能在集群中机器动态增加或减少的情况下依然能做到。

我觉得actor模型走的是通过消除共享数据确保没有资源竞争的方向,这固然能提高系统性能。但在集群情况还是有一些难题要解决;解决方法我觉得也是你说的sharding了,最极端的sharding就是一个聚合根一台服务器,但我们知道这是不可能的,呵呵。所以分片的粒度肯定还要更大一点,比如按照业务角度分片,也可以按照hash取模的方式分片。然后,通过路由,让不同的请求路由到不同的服务器处理。这样理论上能保证无数据并发的情况。

通过上面的分片思想,加上ddd+event sourcing的思想与架构,我相信一定能实现一个既能应对复杂业务逻辑变化,又能相对高效(当然单机肯定比LMAX架构性能差,因为in-memory要等到output events持久化完成了才能更新)的分布式,可靠地系统。

2013-10-12 16:51 "@banq
"的内容
这个问题解决办法我是这样想的:只要BLP的接口方法不变就可以,接口中有两个方法,一个方法是给command调用,还有一个方法是给eventSourcing进行事件回放时用的。业务逻辑都存在这个两个方法具体实现中,因此BLP逻辑应该是可以修改 ...

这样不行啊,这样对event sourcing有效的,event sourcing模式的时候,一个方法给command调用,该方法产生事件,另一个方法用于事件溯源的时候使用,用来更新聚合的状态。
但对input events不行啊,因为input events是command,背后执行的逻辑是完整的逻辑,包括各种业务规则验证,以及聚合状态的更新。要是我们的业务规则改了,那根据input events来重演就得到的是不同的结果了。

2013-10-12 16:08 "@banq"的内容
有人担心分布式环境只有一个聚合对象,如果这个聚合访问量大,怎么办呢?目前我想没有理论上无限解决方案 ...

聚合写操作无法用水平伸缩scale out/in扩展来解决,但是可以用scala up/down垂直伸缩来解决,CPU的核数不断增加,充分利用多CPU也许是解决结构化数据的一个方向,水平伸缩适合非结构化数据。

见:go reactive:http://www.jdon.com/45811#23143633

2013-10-14 06:32 "@banq"的内容
聚合写操作无法用水平伸缩scale out/in扩展来解决,但是可以用scala up/down垂直伸缩来解决,CPU的核数不断增加,充分利用多CPU也许是解决结构化数据的一个方向,水平伸缩适合非结构化数据。

见:go reactive: ...

恩,多谢banq分享,我正在学习。

如果他的input events 是command的话,那么他在command handle上做发送消息,日志等等就很奇怪了,做回溯的时候,不仅性能比event sourcing 低,而且还要修改这些操作的配置(不让他发送或者记日志)

而且如果domain model 放在单独服务器,性能根本达不到800w没秒的处理量(一个处理器)
[该贴被w438418754于2013-10-15 11:34修改过]

在JVM领域,akka的eventsourced扩展是一个非常好的解决方案,详情可以查看https://github.com/eligosource/eventsourced。
目前该项目已经成为akka-的子项目akka-persistence了。
http://doc.akka.io/docs/akka/snapshot/scala/persistence.html