6CQRS(Command Query Responsibility Segregation)关键点
读写分离: 将应用架构分为Command和Query两个部分
Command处理写请求
Query处理读请求
实践1
数据库共享,代码分离,强一致性
实践2
数据库分离,通过DomainEvent异步同步或者其他方式同步,Command采用EventSourcing避免并发锁
举个例子:秒杀并发改库存,最简单的方案,可以并发存储用户请求的Command,并发存储库存变更的Event,将并发修改的竞争资源"剩余数量"做内存实现,不存储,如果断电关机,保证Event足够,即可恢复"剩余数量"的最终状态
高性能的三个敌人
1 资源争夺
2 大量数据
3 网络开销
资源争夺实践方案
传统实践
如果共享资源是数据库行,由于数据库无法一下子支持海量并发,所以要保护数据库工作在最佳性能状态,在应用层排队,通过排队等待换或者排队批处理获得数据库的持续最优性能而不至于造成不可控的并发数争夺资源
基于ES,还有一个好处,直接存储Event,不存储聚合,架构上避免了并发更新聚合
在CQRS中的实践
1 一个Command修改一个聚合根,缩小事务的范围
如果业务上需要同时修改多个聚合根,通过事件驱动的思想实现业务流程(Saga)
2 同一个聚合根的修改操作类似秒杀场景,遵照传统实践方案排队控制共享资源并发数即可
Command和Event的幂等操作
Command是一个动作,结果是触发聚合发生变化
一般来说,我们对Command进行建模是有用的,比如用作审计,在这里,可以用来判重
对Command进行唯一编码,在数据库做主键,让数据库来保证唯一
Event是聚合发生改变后发出的Event
我们可以通过 聚合+Event序号 的方式,来唯一确定一个Event,通过数据库主键来判重,最后发送到可靠的消息中间件供其他服务消费
大量数据
Command
根据业务,水平,垂直拆分,时间拆分(水平?)
Event
在回溯时候用到,根据业务,按照Event类型,时间等维度拆分
Query
根据业务,做拆分
网络开销
一般来说,一个Command的处理过程中包括几个阶段
1 持久化Command
2 业务处理
3 其他服务的协作调用
4 生成并持久化Event
5 发布Command
基于消息驱动,纯异步,如果必要,将CommandStore和EventStore改为内存实现,让生命周期未结束的聚合一直驻留内存甚至不存储(因为可以从EventStore恢复状态)
参考资料:
http://www.cnblogs.com/netfocus/p/4055346.html
https://github.com/tangxuehua/enode
http://developer.51cto.com/art/201407/446106.htm
问题:
1 服务挂掉,如果是纯内存实现,从Query服务中获取? 此时Query可能不够新,所以还是需要内存实现中有同步持久化逻辑?
应该选择用event来恢复状态
2 可否使用Event代替Command?
http://www.cuishilin.com/2015/12/69527.html
区别如上
3 事件的发出和事务持久化需要从时序上保证是事务的原子性,不能做一半?
4 数据中心的数据备份,按照时间异步处理?
5 Query服务中的数据是否保存历史?历史都从数据中心获取?
6 根据业务需要,核心系统有可能只处理当天数据,系统一天的业务结束后,归档数据到数据中心,第二天重新加载?
7 Query服务根据Event维护数据表的逻辑由团队中谁来做?代码放在对应Command服务里?
8 Query服务的连表查询由谁来做?他需要了解全部的表结构?(通过根据Event维护数据表这个过程来熟悉?)
9 是否可以考虑由每个Command的服务自己提供Query服务?
举例来说:查询某人的数据,账户相关数据在各个服务中都需要冗余?
10 Command服务拒绝提供任何Query接口?全都使用Query服务的?
11 Saga练习? https://mp.weixin.qq.com/s/HfLvNvbnE8DrM3n-DXgRQQ