具体例子求解,异步后数据弱导致的决策失误问题
最近看了 cqrs和lamx.
采用 富领域模型 ,event source. 异步操作.
这个有个bad case, 我不知道采用什么方案解决.
钱有100,两口子之前有约定要剩下90. 老公看到有100,花10元,花完以后因为事件异步,数据不一致,此时老婆刷新页面也看到100,再花10元. 最终所有异步事件都处理完之后最终是80元. 不符合用户的预期.
这个问题如何解决?
最近看了 cqrs和lamx.
采用 富领域模型 ,event source. 异步操作.
这个有个bad case, 我不知道采用什么方案解决.
钱有100,两口子之前有约定要剩下90. 老公看到有100,花10元,花完以后因为事件异步,数据不一致,此时老婆刷新页面也看到100,再花10元. 最终所有异步事件都处理完之后最终是80元. 不符合用户的预期.
这个问题如何解决?
你这个案例是数据一致性问题。数据一致性分强一致性和弱一致性。你现在要求的是强一致性,而异步属于弱一致性,当然解决不了强一致性。
DDD聚合根实体使用行为守护状态,能够保证状态变化符合强一致性。所以,这里建立一个聚合根Money,里面封装的是余额100元。通过Money的add和remove方法保证余额的高一致性。
正因为有了DDD聚合根将特殊的高一致性对付了,缺省的其他普遍的才可以使用异步这种弱一致性方法,就如同足球比赛,将C罗这个特殊人才看住了,其他就好办了,擒贼先擒王。
函数编程也是这个道理。将状态可变看住了,其他就可以都要用不变性。两者都是相通的。
可以参考我的什么是流式思维中案例,类似其中TV的ChannelState 。这里大概用代码重新写一下,
|
|
[该贴被banq于2014-06-19 06:47修改过]
昨天咨询了下我们的高T.这个场景在国外银行很常见,国外有夫妻卡.,他认为是这样实现的:
先说说不用异步事件框架实现是如何保持一致性的.
丈夫显示100元, 进行消费,向后端 传递聚合跟的objectVersion 1, 正常扣钱10. 传递聚合跟的objectVersion值+1变为2.
妻子由于也是显示100元,进行消费,所以递聚合跟的objectVersion也是1, 在你的调用方法前会做业务校验,由于版本号objectVersion不匹配,妻子会得到错误.页面重新刷新,显示90元. 妻子就不会继续消费了.
再说说异步事件架构下:
丈夫显示100元,进行消费,. 变成事件1存储. 正常返回给前端.但不是真的出钱. 而是告知用户"后台正在处理,请稍等".
妻子由于也是显示100元,进行消费,同样变成事件2存储.正常返回给前端.同样不出钱,告知用户"后台正在处理,请稍等".
这时候 列里有两个事务产生的事件. 顺序看上面两个事务commit的次序. 加时丈夫事件1先被执行. 检查聚合跟的objectVersion,成功.通过一个新接口告知给前端钱已扣除.妻子的事件2后执行,检查聚合跟的objectVersion,失败.通过一个新接口告知给前端钱无法扣除,无法消费.
同步架构采用异步架构,整个业务流程都变了. 需要新增加一个接口.还有就是.异步事件执行可能因为网络等原因产生偶然Exception.需要有重试的机制.
总结: 同步流程采用异步后. 对于开发者和产品经理来说都更复杂了.
对于开发者来说: 需要1.保存事件 2. 重试机制 3. 新增加一个接口(即异步框架里的回调接口) 4.告知产品经理流程已变成异步化.
对产品经理来说: 需要把原来的一个同步流程思考为多个流程.
那么到底是谁决定谁呢?
开发和产品经理是互相决定,影响的. 一方面当产品经理处于用户体验的角度,可能会主动把一个同步流程,拆成多个异步流程.增加步骤和接口. 这时候开发者坑并不愿意,因为增加了工作量. 另一方面开发者处于性能,并发量的考虑,可能会把PM思考的一个同步调用改成异步. 这时候产品经理需要知道要有新的页面提醒用户"后台处理中", 流程已变.
[该贴被sinaID87521于2014-06-19 11:37修改过]
可能我一下把结果写出来,造成你的理解错误。你的版本校验方式其实也是一种对象锁,类似synchronized, 这种锁效率也很低,还不如Java 8的并发编程提供的读写锁呢,更无法和Disruptor 单写原则。
我再详细写一下代码:
|
[该贴被sinaID87521于2014-06-20 13:38修改过]
晕倒,if (balance-10)<90 代表的是业务规则,等同于if( 业务规则为真 )。业务规则属于模型的一部分,或者是核心部分,也就是每个夫妻的约束,我这里是伪编码。
你的版本校验方法类似同步锁,类似于数据库锁,当然可以用,但是并发性能不是非常好,是堵塞式的,既然我们在这里谈CQRS,谈事件,当然是追求非堵塞的。
这个业务规则所有人都是不同的,是无限的.不可能这么来解决.
[该贴被sinaID87521于2014-06-20 15:52修改过]
乐观锁当然也是锁,看这篇文章:http://www.jdon.com/performance/singlewriter.html
英文原文:http://en.jdon.com/single-writer-principle.html 。
英文原始地址:http://www.javacodegeeks.com/2012/08/single-writer-principle.html
因为你开篇提到LMAX,所以,我应该将LMAX disruptor革命性特点告诉你。
再提供一个事务与锁方面老文章:http://www.jdon.com/45728
[该贴被banq于2014-06-20 16:05修改过]
[该贴被sinaID87521于2014-06-20 16:14修改过]
[该贴被sinaID87521于2014-06-20 16:19修改过]
求解楼主,看不太懂
1. asynchronization是啥关键字
2. 只有 setAupper setAlower为什么能调用 a.setUpper
3. setAupper为啥调用a.setLower ?
4. a是哪里来的.
最后,我觉得这个永远不可能出现(4,3) 的情况.
可能第3点我没理解,有特别的含义.
[该贴被sinaID87521于2014-06-20 18:02修改过]
冲突次数少干脆用原子类型不就好了,没必要把依赖放到外面。
冲突次数多的话,这方式非常影响消费体验。
夫妻卡对银行来说是特殊情况——只有少许人,而且周期长。这种Token方式更新对于一般卡来说简直就是灾难。为夫妻卡专门开发一个独立系统?我觉得挺可笑的。