微服务业务开发三个难题——拆分,事务,查询(下)

上集我们阐述了使用微服务体系架构的关键障碍是领域模型,事务和查询,这三个障碍似乎和功能拆分具有天然的对抗。只要功能拆分了,就涉及这三个难题。


然后我们向你展示了一种解决方案就是将每个服务的业务逻辑实现为一组聚合。然后每个事务只能更新或创建一个单独的聚合。然后通过事件来维护聚合(和服务)之间的数据一致性。


在本集中,我们将会向你介绍使用事件的时候遇到了一个新的问题,就是怎么样通过原子方式更新聚合和发布事件。然后会展示如何使用事件源来解决这个问题,事件源是一种以事件为中心的业务逻辑设计和持久化的方法。之后,我们会阐述微服务架构下的查询困难的问题。然后向你介绍一种称为命令查询责任分离()的方法来实现可扩展和高性能的查询。

 

 

从表面上看,使用事件来保持聚合之间的一致性似乎很简单。


当一个服务创建或更新数据库的一个聚合时,它只是简单地发布一个事件。

但是,这只是表象,其实还有一个核心问题就是:更新数据库和发布事件必须是原子的。否则,就会出现类似这样的情况:如果服务在更新数据库之后但在发布事件之前崩溃,则系统就出现了不一致的问题。

 

传统的解决方案是一般都是使用分布式事务来搞,一个涉及数据库和消息的分布式事务。但是,由于上一集所述的原因,不是一个可行的选择。

其实除了,还有几种解决这个问题的方法。


一种解决方案就是,应用程序可以通过向类似这样的消息中间件的发布一个事件来执行更新。然后一个消息订阅这个事件,通过消费该事件然后最终更新数据库。这种方法可以确保数据库被更新并且事件被发布。

但是缺点就是这种一致性模型过于复杂,至少有点复杂。而且应用程序不能够立即读取到自己刚刚的写入。


图通过发布事件到消息来更新数据库

 

另一种做法就是,如图所示,就是应用程序追加事务日志到数据库(),将每个记录的更改转换为事件,然后把事件发布到消息。这种做法的一个重要好处就是应用程序本身不需要任何的改变。

 

然而,一个缺点是,这种做法是一种底层()的事件,而不是上层业务事件。可能难以将上层业务事件(由于数据库更新的原因)从底层更改逆转到表中的行。

 

 

微服务业务开发三个难题-拆分、事务、查询(下)


 

第三种解决方案就是,图所示的这种,使用数据库表来作为一种临时性的。当一个服务更新一个聚合,它会一个事件到表,作为本地事务的一部分。然后一个单独的进程轮询表并将事件发布到消息。


这种做法的好处就是能够发布的业务事件。


缺点是这种做法容易出错,有这种潜在的可能,因为事件发布代码必须与业务逻辑同步。


微服务业务开发三个难题-拆分、事务、查询(下)

3 - message queue


上面三种做法都有比较典型的缺点。


发布一个事件到并稍后更新的做法总是不能提供一种的一致性,也就是只能保证最终一致。


追加事务日志提供了一致的读取,但却不能发布高级业务事件。


使用数据库表作为提供了一致的读取并且可以发布业务事件,但

却对开发人员有依赖,就是开发人员得记得在状态发生改变的时候加上发布事件的逻辑。

 

幸运的是,我们还有另外一种解决方案,那就是,事件源。它是一种针对持久化和业务逻辑的一种以事件为中心方法,称为事件源。这里解释的不够清楚,稍后慢慢展开。

 

 

事件源(Event sourcing)是一种以事件为中心的持久化方法。这不是一个新的概念。

 

我第一次了解到这个概念是在大概五年多以前,之后对这个新生事物一直充满了好奇,直到我开始开发微服务。接下来,你将会看到通过事件源来实现事件驱动的微服务架构是多么不错的一种方法。

微服务业务开发三个难题——拆分,事务,查询(下)