在CQRS模式中,服务的写命令状态天然地会先于读查询状态而得到更新,由于这种复制延迟(replication lag)的存在使得开发者需要考虑最终一致性。因为查询模型是通过事件来更新的,所以对数据的查询可能返回的是过期的数据。
![](https://img.haomeiwen.com/i7749898/2c6827f275a98d11.jpeg)
假设用户更新了某个订单的金额,但是单击“确定”按钮时,用户看到的还是之前的订单的数据!这是非常令人失望的用户体验。使用“POST/redirect/GET”模式的Web界面通常都会遇到这个问题。
在某些系统中,这可能并不是什么大事。比如,在即时信息流系统中,延迟更新是很常见的——如果用户在Twitter上发了一条微博,他所有的粉丝们有没有在同一时间收到这条消息并不是那么重要。
而在另外一些系统中,确保用户查询不到无效的状态是非常重要的工作。在这种场景中,可以采用的3种策略:乐观更新、轮询以及发布-订阅。
![](https://img.haomeiwen.com/i7749898/54dbd6693ee81aa8.jpeg)
1.乐观更新
开发者应该根据命令所预期的结果来乐观地更新界面的内容。如果命令执行失败,可以将界面的状态回滚。比如,假设用户对Instagram上的一个内容点了“like”,在Instagram后台成功保存这个修改之前,App就已经显示出一个红心图标了,如果最后后台保存失败,Instagram会撤销界面上的这个改动(取消红心图标的显示),这样用户就需要再次“like”一下来显示出红心图标。
这种方式依赖于开发者拥有更新界面的所有信息,或者开发者能够根据输入数据得出这些信息。所以在用于一些简单场景时,这种方式效果最好。
2.轮询
界面可以一直轮询对应的查询API,直到所期望的修改生效。在启动一个命令时,客户端可以设置一个版本号,如时间戳。对于后续的查询,客户端可以一直轮询,直到服务器返回的版本号等于或者大于指定的这个版本号,这表示查询数据模型已经成功更新体现了最新的状态。
3.发布-订阅
不同于一直轮询修改结果,界面也可以在查询模型上订阅一些事件——比如,通过Web socket。在这种情况下,只有在读模型发出了“updated”事件以后,界面才会更新。
想要全面理解CQRS是比较困难的,它需要采用一种不同于之前处理常规的CRUD(增删改查) API时的思维方式。但是,在微服务应用中,CQRS确实很有用的。如果应用得当,CQRS有助于确保查询功能的性能和可用性,即便数据和功能是隶属于不同服务的不同数据存储上的。
摘取自 摩根·布鲁斯和保罗·A.佩雷拉的《微服务实战》
网友评论