凡事没有完美,又要快,又要绝对持久化,很难的,1s我觉得是在性能和持久化方面做了很好的权衡了。而且还有sharding和UPS电源和SSD,master-slave等办法尽量确保这1s内能写完所有日志。但理论上确实总是还是可能会丢。但你会如何选择呢?选择这1s,还是选择性能低下的安全的持久化?

redis上的数据本来就是被所有的应用服务器共享的呀。并发很正常,但是redis内部是单线程的,所以天生支持各种并发命令的处理。
[该贴被tangxuehua于2013-10-11 13:14修改过]

我是这样想的(这里用到的是EDA和CQRS的思想)

domain model 中触发事件改变模型状态,这时把触发的事件发送到持久化事件队列中(这里的队列可能是远程的持久化服务器,也可能是本地做持久化),然后持久化队列有一个线程对其进行持久化,并发送事件到事件处理段来更新读服务器(只有保存成功后才会发送)

做个比喻,比如我用手拿杯子,domain model 中的数据就好像,我大脑已经有这个想法了,而实际使用的数据库的数据就好比我收已经拿到杯子了

domain model 无需知道是否保存了,就算断电了,数据没有来得及保存,在做事件溯源的时候,就当做没有发生过这个一样


而这样,domain model 不用与IO打交道(当然这里有可能和发送远程打交道,但是你也能在本地做一个队列,然后发送到远程的持久化服务器上)

domain model 在内存中执行有多快你比我清楚,
[该贴被w438418754于2013-10-11 14:19修改过]

恩,我现在就是这样做的!

事件产生后,先放在一个持久化队列中,然后队列的消费者逐个持久化事件,如果成功,则发布事件到查询端,如果失败,则一直重试。

是否有IO,要看从什么角度去看了,如果只是从command handler来看,那是没IO的,因为只是产生事件,然后发送到队列就好。但是从整个一次完整处理过程来理解,持久化事件也算IO的呀,而且这里的IO也只是狭义的,实际上像发送到队列,难道不是IO吗?呵呵,从in-memory(如redis)取聚合跟难道不是IO吗?呵呵,访问网络就是IO。

到目前为止,我还没见过真正没有IO的纯in-memory系统。

可能我没表达清楚,domain model 产生事件后发送到持久化队列(无需等待执行,只要发送出去就好了,而且这个队列是在本台服务器)之后直接执行事件更新状态,这个时候事件是没有持久化的,也就是说断电后数据是不在的

而另一个线程去读取持久化队列中的数据(这里就和domain model不是同一个线程了,也就是异步了),取出数据化是要发送远程进行持久化,还是在本地都可以(当然远程的话就可以做同步服务器,如果是本地,那么死机后只能等待这台机器进行重启回溯事件)


从in-memory取聚合跟难道不是IO吗?
我认为不是,所以我才说是否有必要用redis,因为我的数据是放在应用服务器的内存中,而不是在redis里面,

如果要使用redis,那么redis存的也是query中的数据,也就是读的数据,其实和domain model 是没有关系的

1.应用服务器需要无状态,否则水平扩展不容易实现;
2.任何消息必须持久化,否则一断电,就没消息了;
[该贴被tangxuehua于2013-10-11 15:40修改过]

当然这样就会存在一个问题,写数据的时候是没有返回值得,也就是你不会知道什么时候成功和特殊状况(如断电),如果是业务错误,那么你会及时收到提示,因为在领域模型中会对这里进行处理,如密码小于6w数,账号唯一性(我觉得这个就是ddd中工厂需要做的事情了,account 的创建不是直接new 而是通过工厂对其进行生产)等等,

2.任何消息必须持久化,否则一断电,就没消息了;

所以我才觉得我和你的想法有些不同,你认为domain model产生事件后更改了状态就已经是这个已经发生的事情了(所以你必须在这里做同步的IO保存)
而我认为,我可以先更改domain model 异步做IO保存,保存成功后才是真正发生的事情 event handle 处理完成后才会看得到这件事,所以command handle 我是不会去通知用户任何我执行的command是否已经成功的消息,而是会在event handle上做通知
[该贴被w438418754于2013-10-11 16:23修改过]

1.应用服务器需要无状态,否则水平扩展不容易实现;

这个问题也是我现在在考虑的一个问题之一,
假设现在有很多的数据,我们把他按照地区分片
现在有一台服务器处理北京这边的数据,这是如果你扩展一台服务器进来也处理北京这边的数据,你的数据放在redis中,你要如何处理同一条数据被两个服务器同时使用的情况呢,这就和并发的原理是一样的
[该贴被w438418754于2013-10-11 16:20修改过]

如果解决了你说的水平扩展问题,那么我觉得在一台电脑中采用多核处理也并不会有问题~~

如果是用redis,那因为redis本来就是数据存储的,做水平分割肯定要比应用服务器方便,应用服务器主要是用来计算和处理逻辑的,本身无状态。把数据和逻辑分开,分布式系统一般都是如此。

比如缓存服务器集群、消息队列中间件服务器集群、应用服务器集群、数据库集群;每一个集群都有各自的作用和扩展方法;

应用服务器如果无状态,那就可以随便增加或减少;
缓存服务器,如果用mongodb,那mongodb本身就自带在线扩容(增加sharding)的能力,redis不支持,但也有方案来做,比如用redis作者所说的presharding方式;
消息队列的集群,应该很常见,像rabbitmq,淘宝的metaq都支持;
数据库集群,mysql的案例很多吧。

如果你的应用服务器里有状态,如一些消息暂存在里面;那要是这台机器挂了,谁来处理这些消息呢?相反,如果是用专业的消息队列服务器来统一存储各种消息,那通过像zookeep等分布式协调服务,可以判断出,哪个消费者挂了,然后就会把本该由该消费者的消息自动传递给其他消费者,从而保证系统一直可用,所有消息都能有服务器处理。
[该贴被tangxuehua于2013-10-11 16:38修改过]

2013-10-11 16:04 "@w438418754
"的内容
所以我才觉得我和你的想法有些不同,你认为domain model产生事件后更改了状态就已经是这个已经发生的事情了(所以你必须在这里做同步的IO保存)
而我认为,我可以先更改domain model 异步做IO保存,保存成功后才是真正发生的事 ...

我没有认为聚合跟状态一更改就认为事情已经发生了哦!!
我只认为事件保存了才认为事情发生了,所以我才会在事件持久化成功后才更新in-memory(redis)的。

2013-10-11 16:04 "@w438418754
"的内容
,所以command handle 我是不会去通知用户任何我执行的command是否已经成功的消息,而是会在event handle上做通知 ...

对于,这一点,我在事件持久化完成后,我会调用command的一个会掉函数,告诉用户某个command在command side处理完了。

如果是用redis,那因为redis本来就是数据存储的,做水平分割肯定要比应用服务器方便,应用服务器主要是用来计算和处理逻辑的,本身无状态。把数据和逻辑分开,分布式系统一般都是如此。

比如缓存服务器集群、消息队列中间件服务器集群、应用服务器集群、数据库集群;每一个集群都有各自的作用和扩展方法;

应用服务器如果无状态,那就可以随便增加或减少;
缓存服务器,如果用mongodb,那mongodb本身就自带在线扩容(增加sharding)的能力,redis不支持,但也有方案来做,比如用redis作者所说的presharding方式;
消息队列的集群,应该很常见,像rabbitmq,淘宝的metaq都支持;
数据库集群,mysql的案例很多吧。

如果你的应用服务器里有状态,如一些消息暂存在里面;那要是这台机器挂了,谁来处理这些消息呢?相反,如果是用专业的消息队列服务器来统一存储各种消息,那通过像zookeep等分布式协调服务,可以判断出,哪个消费者挂了,然后就会把本该由该消费者的消息自动传递给其他消费者,从而保证系统一直可用,所有消息都能有服务器处理。

你这里的我可以理解,但是你这里其实并不是解决分片问题,而是解决失败后故障转移的问题,分片还是无法动态进行扩展的~~
当然如果只是失败后故障转移,我想lmax中也提到了,他是使用多个服务器同时处理的方式(只是他这种方式我也是有些疑问的,不过他这样就可以实现数据存放在应用服务器的问题了)

呵呵,你说到LMAX,我正好也看过。
我最大的疑惑是,他的input events是什么呢?

LMAX是先持久化input events,然后再到内存处理。
然后内存如果挂了或要重启,那可以通过input events来还原。这我我觉得不是event sourcing了,而是command sourcing了。你觉得呢?这种做法就和redis的aof一样了,存储的都是cmd,然后redis重启时,读取日志中的所有cmd,然后再重新执行一遍。

而event sourcing,重演的时候是根据domain产生的domain event.也就是基于“事实”进行重演。

所以LMAX,他的重演,我觉得很复杂了,因为一个command要重新执行,涉及到的东西就很多了,还有很多外部系统的交互呢。