文章目录

  • 什么是数据分发
  • 场景
  • 作用特性
  • 缺点
  • 微服务数据分发一致性
  • 一致性方案
  • 数据双写
  • 事务性发件箱(Transactional Outbox)
  • 变更数据捕获(Change Data Capture, CDC)
  • 当前成熟技术方案(CDC企业级项目)
  • 一致性方案对比


什么是数据分发

场景

微服务架构下,不同服务单一数据源原则只能使用自己的数据源,对于其他服务只能通过远程获取,这样相互影响增大,耦合性高,同时实现也比较复杂,可通过共享部分数据来简化对其他服务的数据获取过程。这种情况下可使用分发机制,实现不同微服务之间的数据共享。

微服务子项目同步父项目依赖 微服务同步数据_微服务

作用特性

  1. 降低业务之间的松散耦合度,方便不同业务的数据隔离和独立
  2. 提高系统的可扩展性,增强系统性能
  3. 可保证单一的数据来源(Single Source of Truth)-元数据,分发的数据将作为元数据的复制

缺点

  1. 元数据与复制数据的一致性需要维护,强一致可能无法保证,可要求最终一致性
  2. 需要使用分布式事务用于解决一致性问题

微服务数据分发一致性

一致性方案

数据双写

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FBUOj3rQ-1611286428295)(http://doc.szkunton.com/uploads/tec-share/images/m_17e7adb563e4a161aa96b3b2a727b543_r.png)]

微服务A把数据写入DB,同时还要把数据写到MQ,如何才能保证双写的事务性(保证两个操作都正确完成)?

执行步骤:

  • 第一步先更新数据库,如果更新成功,那么result设为true,如果更新失败,那么result设为false;
  • 第二步,如果result为true,也就是说DB更新成功,那么我们就继续做第三步,向mq发送消息

如果发消息也成功,那么我们的流程就走到第四步,整个双写事务就成功了。

如果发消息抛异常,也就是发消息失败,那么容器会执行该方法的事务性回滚,上面的数据库更新操作也会回滚。

问题

  • 如果发消息抛异常了,并不保证说发消息失败了,可能只是由于网络异常抖动而造成的抛异常,实际消息可能是已经发到MQ中,但是抛异常会造成上面数据库更新操作的回滚,结果造成两边数据不一致。

事务性发件箱(Transactional Outbox)

发送MQ消息之前,先将发送消息放到数据库(outbox),然后通过一个中介服务不断从outbox轮询数据进而发送到MQ,这样数据的更新和outbox数据将在同一个事务下执行

微服务子项目同步父项目依赖 微服务同步数据_微服务_02

在数据库中,除了订单Order表,为了实现事务性双写,我们还需增加了一个发件箱Outbox表。Order表和Outbox表都在同一个数据库中,对它们进行同时更新的话,通过数据库的事务机制,可以实现事务性更新。

Order Service流程订单示例:

  1. Order Service先将新订单数据写入Order表,然后它再向Outbox表中写入一条订单新增记录,这两个DB操作可以包在一个DB事务里头,实现事务性写入
  2. 引入一个称为消息中继Message Relay的角色(定时查询Outbox表微服务),它负责定期Poll拉取Outbox中的新数据
  3. 将新数据Publish发送到MQ,如果写入MQ确认成功,Message Relay就可以将Outbox中的对应记录标记为已消费。这里可能会出现一种异常情况,就是Message Relay在将消息发送到MQ时,发生了网络抖动,实际消息可能已经写入MQ,但是Message Relay并没有得到确认,这时候它会重发,直到明确成功为止。所以,这里也是一个At Least Once,也就是至少交付一次的消费语义,消息可能被重复投递。因此,MQ之后的消费方要做消息去重或幂等处理。

总之,事务性发件箱模式可以保证,对Order表的修改,然后将对应事件发送到MQ,这两个动作可以实现事务性,也就是实现数据分发的事务性。

注意

这里的Message Relay角色既可以是一个独立部署的服务,也可以和Order Service在同一个服务。生产实践中,需要考虑Message Relay的高可用部署,还有监控和告警,否则如果Message Relay挂了,消息就发不出来,然后,依赖于消息的各种消费方也将无法正常工作。

案例Killbill Common Queue

变更数据捕获(Change Data Capture, CDC)

利用了数据库的事务日志记录,捕获记录变更日志并发送到MQ

微服务子项目同步父项目依赖 微服务同步数据_数据库_03

数据库变更记录日志

  • 对于数据库变更提交操作,都记录所谓事务日志Transaction Log,也称为提交日志Commit Log。比方说MySQL支持binlog,Postgres支持Write Ahead log
  • 事务日志可以简单理解为数据库本地的一个文件队列,它记录了按时间顺序发生的对数据库表的变更提交记录

Order Service一个新订单捕获数据流程:

  1. Order Service将新订单记录写入Order表,并且提交。这是一次表变更操作,这次变更会被记录到数据库的事务日志当中,其中内容包括发生的变更数据
  2. 我们还需要引入一个称为Transaction Log Miner(事务日志监控者)这样的角色,这个Miner负责监控数据库事务日志变化,如果有新的变更记录,Miner就会捕获到变更记录
  3. Miner会将变更记录发送到MQ消息队列,同之前的Message Relay一样,这里的发送到MQ也是At Least Once语义,消息可能会被重复发送,所以MQ之后的消费者需要做去重或者幂等处理

总之,CDC技术同样可以保证,对Order表的修改,然后将对应事件发送到MQ,这两个动作可以实现事务性,也就是实现数据分发的事务性

注意

这里的CDC一般是一个独立部署的服务,生产中需要做好高可用部署,并且做好监控告警。否则如果CDC挂了,消息也就发不出来,然后,依赖于消息的各种消费方也将无法正常工作。

当前成熟技术方案(CDC企业级项目)

微服务子项目同步父项目依赖 微服务同步数据_微服务_04

当前几个比较成熟的企业级的CDC开源项目:

  1. 第一个是阿里开源的Canal,目前在github上有超过1.4万颗星,这个项目在国内用得比较多,之前在拍拍贷的实时数据场景,Canal也有不少成功的应用。Canal主要支持MySQL binlog的增量订阅和消费。它是基于MySQL的Master/Slave机制,它的Miner角色是通过伪装成Slave来实现的。这个项目的使用文档相对比较完善,建议大家一步参考学习。
  2. 第二个是Redhat开源的Debezium,目前在github上有超过3.2k星,这个项目在国外用得较多。Debezium主要是在Kafka Connect的基础上开发的,它不仅支持mysql数据库,还支持postgres/sqlserver/mongodb等数据库。
  3. 第三个是Zendesk开源的Maxwell,目前在github上有超过2.1k星。Maxwell是一个轻量级的CDC Deamon,主要支持MySQL binlog的变更数据捕获和处理。
  4. 第四个是Airbnb开源的SpinalTap,目前在github上有两百多颗星。SpinalTap主要支持MySQL binlog的变更捕获和处理。这个项目的星虽然不多,但是它是在Airbnb SOA服务化过程中,通过实践落地出来的一个项目,值得参考。

对于上面的这些项目,如果你想生产使用的话,推荐阿里的Canal,因为这个项目毕竟是国内大厂阿里落地出来,而且在国内已经有不少企业落地案例

一致性方案对比

Transactional Outbox vs CDC

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DXKPC0Sn-1611286428299)(http://doc.szkunton.com/uploads/tec-share/images/m_b2b140226fb963db10c08c35f20986d4_r.png)]

事务性分发的两种落地模式,一种是事务性发件箱模式,另外一种是变更数据捕获模式,这两种模式其实各有优劣,为了帮助大家做选型决策,我这边对这两种模式进行一个比较,请看上面的比较表格:

  1. 首先比较一下复杂性,事务性发件箱相对比较简单,简单做法只需要在数据库中增加一个发件箱表,然后再启一个Poller线程拉消息和发消息就可以了。CDC技术相对比较复杂,需要你深入理解数据库的事务日志格式和协议。另外Miner的实现也不简单,要保证不丢消息,如果生产部署的话,还要考虑Miner的高可以部署,还有监控告警等环节。
  2. 第二个比较的是Polling延迟和开销。事务性发件箱的Polling是近实时的,同时如果频繁拉数据库表,难免会有性能开销。CDC是比较实时的,同时它不侵入数据库和表,所以它的性能开销相对小。
  3. 第三个比较的是应用侵入性。事务性发件箱是有一定的应用侵入性的,应用在更新业务数据的同时,还要单独发送消息。CDC对应用是无侵入的,因为它拉取的是数据库事务日志,这个和应用是不直接耦合的。当然,CDC和事务性发件箱模式并不排斥,你可以在应用层采用事务性发件箱模式,同时仍然采用CDC到数据库去捕获和发件箱中的消息对应的事务日志。这个方法对应用有一定的侵入性,但是通过CDC可以获得较好的数据同步性能。
  4. 第四点是适用场合。事务性发件箱主要适用于中小规模的企业,因为做法比较简单,一个开发人员也可以搞定。CDC则主要适用于中大规模互联网企业,最好有独立框架团队负责CDC的治理和维护。