Skip to content

Commit

Permalink
Update event-driven-data-management-for-microservices.md
Browse files Browse the repository at this point in the history
增加空格
  • Loading branch information
lihangqi committed Sep 5, 2019
1 parent cd2e1e8 commit a8bb173
Showing 1 changed file with 13 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@

![service table](/images/Private-table-of-the-corresponding-service.png)

订单服务不能直接访问客户表,只能通过客户服务发布的API来访问。订单服务也可以使用 distributed transactions, 也就是周知的两阶段提交 (2PC)。然而,2PC 在现在应用中不是可选性。根据 CAP 理论,必须在可用性(availability)和 ACID 一致性(consistency)之间做出选择,availability 一般是更好的选择。但是,许多现代科技,例如许多 NoSQL 数据库,并不支持 2PC。在服务和数据库之间维护数据一致性是非常根本的需求,因此我们需要找其他的方案。
订单服务不能直接访问客户表,只能通过客户服务发布的 API 来访问。订单服务也可以使用 distributed transactions, 也就是周知的两阶段提交 (2PC)。然而,2PC 在现在应用中不是可选性。根据 CAP 理论,必须在可用性(availability)和 ACID 一致性(consistency)之间做出选择,availability 一般是更好的选择。但是,许多现代科技,例如许多 NoSQL 数据库,并不支持 2PC。在服务和数据库之间维护数据一致性是非常根本的需求,因此我们需要找其他的方案。

第二个挑战是如何完成从多个服务中搜索数据。例如,设想应用需要显示客户和他的订单。如果订单服务提供API来接受用户订单信息,那么用户可以使用类应用型的 join 操作接收数据。应用从用户服务接受用户信息,从订单服务接受此用户订单。假设,订单服务只支持通过私有键(key)来查询订单(也许是在使用只支持基于主键接受的NoSQL数据库),此时,没有合适的方法来接收所需数据。
第二个挑战是如何完成从多个服务中搜索数据。例如,设想应用需要显示客户和他的订单。如果订单服务提供 API 来接受用户订单信息,那么用户可以使用类应用型的 join 操作接收数据。应用从用户服务接受用户信息,从订单服务接受此用户订单。假设,订单服务只支持通过私有键(key)来查询订单(也许是在使用只支持基于主键接受的 NoSQL 数据库),此时,没有合适的方法来接收所需数据。

# 1.2 事件驱动架构

对许多应用来说,这个解决方案就是使用事件驱动架构(event-driven architecture)。在这种架构中,当某件重要事情发生时,微服务会发布一个事件,例如更新一个业务实体。当订阅这些事件的微服务接收此事件时,就可以更新自己的业务实体,也可能会引发更多的时间发布。

可以使用事件来实现跨多服务的业务交易。交易一般由一系列步骤构成,每一步骤都由一个更新业务实体的微服务和发布激活下一步骤的事件构成。下图展现如何使用事件驱动方法,在创建订单时检查信用可用度,微服务通过消息代理(Messsage Broker)来交换事件。

1. 订单服务创建一个带有NEW状态的 Order (订单),发布了一个 “Order Created Event(创建订单)” 的事件。
1. 订单服务创建一个带有 NEW 状态的 Order (订单),发布了一个 “Order Created Event(创建订单)” 的事件。

![Order-Created-Event](/images/Order-Created-Event.png)

Expand All @@ -53,17 +53,17 @@

![pre-join](/images/pre-join.png)

当客户订单视图更新服务收到客户或者订单事件,就会更新 客户订单视图数据集。可以使用文档数据库(例如MongoDB)来实现客户订单视图,为每个用户存储一个文档。客户订单视图查询服务负责响应对客户以及最近订单(通过查询客户订单视图数据集)的查询。
当客户订单视图更新服务收到客户或者订单事件,就会更新 客户订单视图数据集。可以使用文档数据库(例如 MongoDB)来实现客户订单视图,为每个用户存储一个文档。客户订单视图查询服务负责响应对客户以及最近订单(通过查询客户订单视图数据集)的查询。

事件驱动架构也是既有优点也有缺点,此架构可以使得交易跨多个服务且提供最终一致性,并且可以使应用维护最终视图;而缺点在于编程模式比ACID交易模式更加复杂:为了从应用层级失效中恢复,还需要完成补偿性交易,例如,如果信用检查不成功则必须取消订单;另外,应用必须应对不一致的数据,这是因为临时(in-flight)交易造成的改变是可见的,另外当应用读取未更新的最终视图时也会遇见数据不一致问题。另外一个缺点在于订阅者必须检测和忽略冗余事件。
事件驱动架构也是既有优点也有缺点,此架构可以使得交易跨多个服务且提供最终一致性,并且可以使应用维护最终视图;而缺点在于编程模式比 ACID 交易模式更加复杂:为了从应用层级失效中恢复,还需要完成补偿性交易,例如,如果信用检查不成功则必须取消订单;另外,应用必须应对不一致的数据,这是因为临时(in-flight)交易造成的改变是可见的,另外当应用读取未更新的最终视图时也会遇见数据不一致问题。另外一个缺点在于订阅者必须检测和忽略冗余事件。

# 1.3 原子操作Achieving Atomicity
# 1.3 原子操作 Achieving Atomicity

事件驱动架构还会碰到数据库更新和发布事件原子性问题。例如,订单服务必须向ORDER表插入一行,然后发布 Order Created event,这两个操作需要原子性。如果更新数据库后,服务瘫了(crashes)造成事件未能发布,系统变成不一致状态。确保原子操作的标准方式是使用一个分布式交易,其中包括数据库和消息代理。然而,基于以上描述的 CAP 理论,这却并不是我们想要的。
事件驱动架构还会碰到数据库更新和发布事件原子性问题。例如,订单服务必须向 ORDER 表插入一行,然后发布 Order Created event,这两个操作需要原子性。如果更新数据库后,服务瘫了(crashes)造成事件未能发布,系统变成不一致状态。确保原子操作的标准方式是使用一个分布式交易,其中包括数据库和消息代理。然而,基于以上描述的 CAP 理论,这却并不是我们想要的。

## 1.3.1 使用本地交易发布事件

获得原子性的一个方法是对发布事件应用采用 multi-step process involving only local transactions,技巧在于一个EVENT表,此表在存储业务实体数据库中起到消息列表功能。应用发起一个(本地)数据库交易,更新业务实体状态,向 EVENT 表中插入一个事件,然后提交此次交易。另外一个独立应用进程或者线程查询此 EVENT 表,向消息代理发布事件,然后使用本地交易标志此事件为已发布,如下图所示:
获得原子性的一个方法是对发布事件应用采用 multi-step process involving only local transactions,技巧在于一个 EVENT 表,此表在存储业务实体数据库中起到消息列表功能。应用发起一个(本地)数据库交易,更新业务实体状态,向 EVENT 表中插入一个事件,然后提交此次交易。另外一个独立应用进程或者线程查询此 EVENT 表,向消息代理发布事件,然后使用本地交易标志此事件为已发布,如下图所示:

![multi-step process](/images/multi-step-process.png)

Expand All @@ -81,9 +81,9 @@

此方法的例子如 LinkedIn Databus 项目,Databus 挖掘 Oracle 交易日志,根据变化发布事件,LinkedIn 使用 Databus 来保证系统内各记录之间的一致性。

另外的例子如:AWS 的 streams mechanism in AWS DynamoDB,是一个可管理的 NoSQL 数据库,一个 DynamoDB 流是由过去24小时对数据库表基于时序的变化(创建,更新和删除操作),应用可以从流中读取这些变化,然后以事件方式发布这些变化。
另外的例子如:AWS 的 streams mechanism in AWS DynamoDB,是一个可管理的 NoSQL 数据库,一个 DynamoDB 流是由过去 24 小时对数据库表基于时序的变化(创建,更新和删除操作),应用可以从流中读取这些变化,然后以事件方式发布这些变化。

交易日志挖掘也是优缺点并存。优点是确保每次更新发布事件不依赖于2PC。交易日志挖掘可以通过将发布事件和应用业务逻辑分离开得到简化;而主要缺点在于交易日志对不同数据库有不同格式,甚至不同数据库版本也有不同格式;而且很难从底层交易日志更新记录转换为高层业务事件。
交易日志挖掘也是优缺点并存。优点是确保每次更新发布事件不依赖于 2PC。交易日志挖掘可以通过将发布事件和应用业务逻辑分离开得到简化;而主要缺点在于交易日志对不同数据库有不同格式,甚至不同数据库版本也有不同格式;而且很难从底层交易日志更新记录转换为高层业务事件。

交易日志挖掘方法通过应用直接更新数据库而不需要 2PC 介入。下面我们再看一种完全不同的方法:不需要更新只依赖事件的方法。

Expand All @@ -95,16 +95,16 @@ Event sourcing (事件源)通过使用根本不同的事件中心方式来

![Event-sourcing](/images/Event-sourcing.png)

事件是长期保存在事件数据库中,提供 API 添加和获取实体事件。事件存储跟之前描述的消息代理类似,提供API来订阅事件。事件存储将事件递送到所有感兴趣的订阅者,事件存储是事件驱动微服务架构的基干。
事件是长期保存在事件数据库中,提供 API 添加和获取实体事件。事件存储跟之前描述的消息代理类似,提供 API 来订阅事件。事件存储将事件递送到所有感兴趣的订阅者,事件存储是事件驱动微服务架构的基干。

事件源方法有很多优点:解决了事件驱动架构关键问题,使得只要有状态变化就可以可靠地发布事件,也就解决了微服务架构中数据一致性问题。另外,因为是持久化事件而不是对象,也就避免了 object relational impedance mismatch problem。

数据源方法提供了100%可靠的业务实体变化监控日志,使得获取任何时点实体状态成为可能。另外,事件源方法可以使得业务逻辑可以由事件交换的松耦合业务实体构成。这些优势使得单体应用移植到微服务架构变的相对容易。
数据源方法提供了 100%可靠的业务实体变化监控日志,使得获取任何时点实体状态成为可能。另外,事件源方法可以使得业务逻辑可以由事件交换的松耦合业务实体构成。这些优势使得单体应用移植到微服务架构变的相对容易。

事件源方法也有不少缺点,因为采用不同或者不太熟悉的变成模式,使得重新学习不太容易;事件存储只支持主键查询业务实体,必须使用 Command Query Responsibility Segregation (CQRS) 来完成查询业务,因此,应用必须处理最终一致数据。

# 1.4 总结

在微服务架构中,每个微服务都有自己私有的数据集。不同微服务可能使用不同的SQL或者NoSQL数据库。尽管数据库架构有很强的优势,但是也面对数据分布式管理的挑战。第一个挑战就是如何在多服务之间维护业务交易一致性;第二个挑战是如何从多服务环境中获取一致性数据。
在微服务架构中,每个微服务都有自己私有的数据集。不同微服务可能使用不同的 SQL 或者 NoSQL 数据库。尽管数据库架构有很强的优势,但是也面对数据分布式管理的挑战。第一个挑战就是如何在多服务之间维护业务交易一致性;第二个挑战是如何从多服务环境中获取一致性数据。

最佳解决办法是采用事件驱动架构。其中碰到的一个挑战是如何原子性的更新状态和发布事件。有几种方法可以解决此问题,包括将数据库视为消息队列、交易日志挖掘和事件源。

0 comments on commit a8bb173

Please sign in to comment.