banq
2013-10-11 17:10
>eventstore用mongodb,redis都有实现;in-memory,目前使用redis来实现

event的存储和播放其实真正自己做起来非常琐碎,建议采取专门的EventStore数据库,也就是函数数据库,比如:http://www.jdon.com/45742

Redis优势在于内存读写快,但是数据集大小有限制,适合做CQRS的C部分。

EventStore如果想自己实现,Kafka + Cassandra 比较合适,在C和R之间同步,将Redis中领域模型的领域事件通过Kafka写入Cassandra,然后定时同步到读系统,读系统实际是一个实时大数据系统。
参考:http://www.jdon.com/45698

w438418754
2013-10-11 17:12
2013-10-11 16:41 "@tangxuehua
"的内容
我没有认为聚合跟状态一更改就认为事情已经发生了哦!!
我只认为事件保存了才认为事情发生了,所以我才会在事件持久化成功后才更新in-memory(redis)的。 ...


其实是一样的,因为你保存后才会更新in-momory中的数据,反过来理解,更新了in-momory那不就是已经发生过的事情了么

所以在这里我和你的想法是不同的,我会先更新in-momory 然后发送事件到队列,这个时候 command handle 就已经处理完成了,
按照你的做法去做,那么处理一个事件必须等待其保持后才能执行更新in-momory 也就是才能执行下一个事件,这样会拖累了cpu执行能力,这个就等于是同步IO的操作了,所以你才能在command handle中回调给用户结果

w438418754
2013-10-11 17:24
2013-10-11 17:09 "@tangxuehua
"的内容
呵呵,你说到LMAX,我正好也看过。
我最大的疑惑是,他的input events是什么呢?

LMAX是先持久化input events,然后再到内存处理。
然后内存如果挂了或要重启,那可以通过input events来还原。这我我觉得不 ...


是的,他这里确实很疑惑不过我还是认为他是event 而不是command

因为如果按照他的做法,其实就是从你的思路出发,先保存事件,再更新模型中的事件然后发送事件到event handle 做处理
而这种做法的疑问就在于,你产生事件后是否需要等待事件保存,
如果不等待,那么我在你还没更新状态的时候又对这个模型做了更改了呢?这个时候业务逻辑是会出问题的,
如果等待,那么就照成了他必须被IO所限制,所以我那个时候才从另一个方向去考虑这个问题

tangxuehua
2013-10-11 17:55
现在的分歧是,
我的做法是:先保存是事件在更新内存;
你的做法是:先更新内存在保存事件;

我的做法相当于认为eventstore是最新数据;你的做法相当于in-memory是最新数据;

其实两者没有差别,
首先,更新内存遇到的并发问题总需要检测的吧?
其次,保存事件和更新内存,你觉得哪个快呢?保存事件是保存到redis,更新内存也是更新到redis,有差别吗?在我看来,保存事件还要更快一点,因为事件小,聚合根大。同样的网络调用(调用redis),肯定是保存事件快。

[该贴被tangxuehua于2013-10-11 18:12修改过]

tangxuehua
2013-10-11 18:16
另外,对于LMAX架构,如果他的business logic processor是domain model的话,那input event就是command了,为什么是事件呢?事件应该是domain model产生的才对。

所以从martin的介绍来看,lmax明显就是先journal input events,再给domain model处理。然后他的output events之所以可以异步保存,是因为LMAX总是单机单线程的架构,所以没有所谓的并发问题。所以LMAX为了系统可用性,配置了多个replica来处理相同的input events,只不过slave的机器不发布output events.

tangxuehua
2013-10-11 18:24
话说回来,呵呵。
要是你的in-memory关机后还能自动恢复,也就是能保证持久化,同时还能支持并发冲突检测,比如多个人同时修改一个聚合根的情况。那我同意先更新内存,再异步持久化事件。

但这样持久化事件意义就不大了,in-memory可以自己恢复,也能自己控制并发。这样就意味着没必要通过event sourcing来恢复in-memory了,保存事件来干嘛呢?呵呵,最多就用来做一些其他的用途,但没必要用来恢复in-memory了。

通常我们所说的in-memory是内存缓存,是缓存。也就是宕机后数据会没的。

[该贴被tangxuehua于2013-10-11 18:25修改过]

tangxuehua
2013-10-11 18:30
2013-10-11 17:10 "@w438418754
"的内容
按照你的做法去做,那么处理一个事件必须等待其保持后才能执行更新in-momory 也就是才能执行下一个事件,这样会拖累了cpu执行能力 ...


我现在的设计中并不是事件必须持久化完成后,才能处理处理下一个command。而是domain产生事件后,先发到一个队列,然后队列的消费者,会取出事件,然后先持久化事件,如果成功,则更新内存;所以样的设计,一旦domain把事件发送到队列后,就可以处理下一个command了。所以不会阻塞。

[该贴被tangxuehua于2013-10-11 18:30修改过]

w438418754
2013-10-11 21:25
2013-10-11 18:30 "@tangxuehua
"的内容
我现在的设计中并不是事件必须持久化完成后,才能处理处理下一个command。而是domain产生事件后,先发到一个队列,然后队列的消费者,会取出事件,然后先持久化事件,如果成功,则更新内存;所以样的设计,一旦domain把事件发送到队列后, ...



所以这种方式会出现问题,假设有一个商品,商品有一个库存数量,现在很多人对其进行购买,你在生成事件是需要对商品数量进行判断,如果大于0则能生成购买事件,否则提示已经没有库存了,而按照你的做法,这个判断最终会有可能小于0的,比如我的库存只有1了,那么第一个人进行购买正常提交事件,但是这个第二个人也购买了(提交了command)你这个时候还没有执行第一个人的event 那么第二个人判断时是可以购买的,因为数据还没有更新,这个时候第一个的事件执行了,数量变成了0 你第二个的人购买的事件又进行了处理变成了-1。所以你必须等待event处理完后才可以进入下一个command的处理,否则是有问题的

w438418754
2013-10-11 21:42
2013-10-11 18:16 "@tangxuehua
"的内容
另外,对于LMAX架构,如果他的business logic processor是domain model的话,那input event就是command了,为什么是事件呢?事件应该是domain model产生的才对。

所以从marti ...


是的,所以我同样对他有疑问,如果business logic processor是domian model的话,那么input event确实应该是command 但是保存command就让我觉得相当的奇怪(那么他是不会保存event?如果保存了就不对了,还是有持久化操作,这就降低了cpu的处理能力)

而保存command 那么他同样能起到回溯的效果,不过他的粒度非常粗,在一定程度上影响了性能,而且command handle做不做通知消息这些操作呢?如果做那么同样又降低了cpu处理能力,而且这是客户如果收到通知(短信)那么就觉得想当不对了,我转账一次,怎么收到两次(好几次)的通知,如果不在这里做,那么在哪里呢?


所以我觉得至少我看不懂他那副图,如果他是对的话,因为以我的思路去思考这个问题,那么business logic processor必然变成了event handle 这点我自己都不能认同,所以说不通~

tangxuehua
2013-10-12 00:19
事件是持久化是有唯一索引来做并发控制的,如果第二个人购买时取到的商品数量虽然是1,也就是认为还够,但是第二次购买生存的事件是保存不了的,因为事件的版本号肯定重复了

tangxuehua
2013-10-12 00:24
这个就是我前面一直强调的并发控制呀!难道你忽略了?呵呵,你先保存到内存的话也要做更新的并发控制的,除非你的内存不是被应用服务器共享的

banq
2013-10-12 06:38
2013-10-12 00:19 "@tangxuehua
"的内容
事件是持久化是有唯一索引来做并发控制的,如果第二个人购买时取到的商品数量虽然是1,也就是认为还够,但是第二次购买生存的事件是保存不了的,因为事件的版本号肯定重复了 ...


关于这个并发问题,使用传统并发控制都会带来锁和堵塞,只有使用Actors模型才能优雅解决这个问题:http://www.jdon.com/45728

两个人进来购买商品时,是从一个信箱或队列排队进来的,有先来后到,商品购买作为一个聚合根,也就是一个Actors模型,对外接口都是异步并发的信箱,内部状态处理都是单线程,包括状态改变以后的领域事件的发出,都是在一个单线程内进行,因此对于一个聚合根的事件保存EventStore自然是顺序的,不存在冲突并发。

tangxuehua
2013-10-12 09:52
2013-10-12 06:38 "@banq
"的内容
关于这个并发问题,使用传统并发控制都会带来锁和堵塞,只有使用Actors模型才能优雅解决这个问 ...


Actors这种设计能支持集群部署吗?比如我集群部署我的应用服务器,然后每台机器上跑相同的in-memory领域模型。那这样就导致有两个相同ID的聚合根(Actor)在不同的服务器上处理请求,假设处理请求时这两个actor的状态是相同的,那这两个Actor产生的事件保存时,也会遇到并发问题呀。

所以,其实这里并发问题如何解决只是表象,本质是要解决分布式环境下的数据一致性的问题。

同一个聚合根在单台机器,可以方便的用单线程控制,但是集群的情况下,就没那么容易了。如果集群中的所有服务器的内存共享,那肯定有并发问题,如果不共享,则需要同步,因为是分布式,所以必然会有数据不一致问题。

banq
2013-10-12 15:35
2013-10-12 09:52 "@tangxuehua "的内容
比如我集群部署我的应用服务器,然后每台机器上跑相同的in-memory领域模型 ...


这是一个问题,但是严重性不大,CQRS架构中一般C部分使用一台服务器即可,内存中领域模型有些类似关系数据库,无法无缝扩展,或者做sharding,比如一个服务器是聚合根A,另外一个服务器是聚合根B。总之目标是保证在分布式环境中聚合根实例只有一个。这个从LMAX架构也可以看出,就是采取两台服务器也是为了可靠性,我们可以采取datagrid数据网格内存矩阵之类的产品,如terracotta,实现两台服务器内存之间快速同步。





banq
2013-10-12 16:08
2013-10-12 15:35 "@banq "的内容
这是一个问题,但是严重性不大,CQRS架构中一般C部分使用一台服务器即可 ...


我想补充的是,因为聚合本身是一种结构化数据,结构本身含义是聚合的意思,因此,在一个分布式环境中,需要把一个结构数据的聚合集合看成是一个整体,全局只能有一个,这种视角不同于非结构化数据处理,比如写入数据同步采取矢量矩阵复制只适合非结构化数据。

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

猜你喜欢