工程中实践的微服务设计模式

京东云开发者
• 阅读 229

最近在读《微服务架构设计模式》,开始的时候我非常的好奇,因为在我印象中,设计模式是常说的那23种设计模式,而微服务的设计模式又是什么呢?这个问题也留给大家,在文末我会附上我对这个问题的理解。本次文章的内容主要是工作中对微服务设计模式的应用,希望能对大家有所启发。

事务发件箱模式

事务发件箱模式:将消息保存在数据库 “发件箱”表 作为事务的一部分

policy 为处理投保的微服务,以投保事务为例:

工程中实践的微服务设计模式

如上图所示,在投保过程中有4步操作(注意持久化结算任务(Task)A的操作),这个过程便是事务发件箱模式的体现,为什么说它是事务发件箱模式呢?

  1. Task 表中持久化的消息任务最终会被发送到 MQ 中,所以它是发件箱
  2. 持久化 Task 记录的操作被包含在事务中,所以称它为事务发件箱

Task 记录中保存的消息体是通过另一个服务 service-job 轮询扫描初始化状态的任务,并将其发送到 MQ 的,发送成功后任务修改为完成状态,这种方式被称为事务性发件箱模式中的轮询发布数据模式

这种设计模式有一个弊端:随着数据量不断增大,经常轮询数据库可能造成昂贵的开销。在我们的系统中采用了两种措施进行优化:

  • 分库分表:以空间换时间,避免单表数据量过大造成的开销
  • 将完成状态的任务进行“数据结转”的设计:任务先保存在 Task 表中,被执行完成后被归档到 TaskRecord 表中

此外还有一种更加高效但是开发稍复杂的方式:拖尾数据库日志模式

拖尾数据库日志模式

工程中实践的微服务设计模式

数据库的每次更新都对应着一条数据库事务日志,通过事务日志挖掘器读取事务日志,并将每条与消息有关的记录发送给消息代理(开源框架可参考:Github: debezium,可以将MySQL的binlog读取到Kafka中),但是这种方法的弊端是需要在开发上做一些努力,因为需要监听数据库事务日志和调用数据库底层相关的API。

相信“邮递员”

工程实践中,还有一种没有采用事务发件箱模式来保证数据一致性的方法:在事务中先持久化完成状态的任务,随后直接将消息发送给消息队列,如果消息发送失败,捕获异常并将任务修改成初始化状态,随后依赖 service-job 服务进行补偿:即将初始化状态的任务发送给 MQ。我们还是以投保的过程为例,如下代码所示:

// 1. 持久化保单数据
savePolicy();
// 2. 持久化保单明细数据
savePolicyDetail();
// 3. RPC 调用投保接口
rpcPolicy();
// 4. 持久化完成状态的任务,任务中记录了要发送给MQ的消息体
int num = insertTask(TaskStatus.COMPLETE);
// 5. 如果插入成功了,借助线程池发送消息
if (num > 1) {
    threadPoolExecutor.execute(() -> {
        try {
            mq.send(task.info);
        } catch(Exception e) {
            // 发送失败,抛出异常,修改任务状态为初始化状态,依赖 service-job 服务进行补偿
            updateTask(TaskStatus.INIT);
        }
    });
}

这种设计模式默认认为MQ集群始终是高可用的,我将这种设计模式命名为相信“邮递员” 。在生产实践中,因为有MQ运维团队在保障MQ集群的高可用,所以这种设计模式也是比较稳定的。


在投退保流程中,涉及不同数据库的修改操作,如保单的持久化、保单状态的修改以及相关结算的推送,要保证这个过程中的数据一致性,那么便不能再依赖ACID本地事务,而是需要使用跨服务的 Saga 设计模式来维护数据一致性。下面我们来介绍两种,分别是协同式Saga编排式Saga

Saga 通过使用异步消息来驱动一系列本地事务,来维护多个服务之间的数据一致性。

协同式Saga

我们先带着 Saga 的概念来看一下投保的流程:

工程中实践的微服务设计模式

在这个过程中,Saga的决策和执行顺序分布在Saga的每一个参与方中,并且通过消息交换的方式进行沟通,一个Saga的参与方执行完触发另一个Saga执行,保证数据一致性,这种方式被称为协同式Saga

这种设计模式的优劣如下:

  • 优势:比较简单,服务间松耦合
  • 弊端:比较难理解,因为它没有一个地方定义了Saga的执行流程,Saga的处理逻辑分布在不同的服务中,需要根据代码触发的任务去整理整个流程

为什么没有采用XA来实现分布式事务

XA采用两阶段提交协议实现分布式事务,在事务中的所有参与者都成功时提交,有失败时便回滚。要使用该模式一方面要求所有的事务参与者(数据库或消息代理)满足XA标准,另一方面,它本质上是同步的进程间通讯,同步的通讯机制有一个弊端:它会降低分布式系统的可用性(假如分布式系统中每个服务的可用性为99%,如果服务与服务之间是同步调用的方式:服务必须从另外一个服务获取响应后才能返回它的客户端调用,那么分布式系统的可用性为各个服务可用性的乘积,随着同步交互服务的增加,可用性会随之降低,最大化可用性的方式应该最小化系统间的同步操作量)。所以,一般互联网公司很少采用强一致性的设计,而是采用最终一致性设计(银行可能会使用到强一致性)。此外,XA实现分布式事务需要依赖事务的协调者(如Seata),实现起来相比于上述方式复杂。

编排式Saga

Saga 的另一种实现方式是编排式,编排式 Saga 需要事务的协调者(DTM),全局事务发起人将整个全局事务的编排信息,包括每个步骤的正向操作和反向补偿操作定义好之后,提交给事务协调者(DTM),协调者按步骤异步执行Saga逻辑。

如果投保流程使用编排式Saga的话,投保成功的过程如下:

工程中实践的微服务设计模式

编排式Saga的事务定义执行步骤非常灵活,假如我们要在投保失败的情况下做取消结算的补偿逻辑的话,可以自行定义,图示如下:

工程中实践的微服务设计模式

这种设计模式的优劣如下:

优势:能够集中流程控制、易于扩展和服务间松耦合,如果服务之间的依赖关系复杂,且业务流程经常变动,使用编排式Saga是合适的
弊端:引入协调者增加了开发复杂性(扩展学习:DTM开源项目文档


现在我们回到文章开篇的问题:微服务架构设计模式与我们常说的设计模式的区别是什么?

我们常说的设计模式是面向对象的设计模式,它的解决方案元素是类,而微服务设计模式是站在更高维度即系统架构层面的设计模式,它面向的对象是系统中各个服务,解决方案也由相互协作的服务构成的。


巨人的肩膀

  • 《微服务架构设计模式》:第 1 - 4 章
点赞
收藏
评论区
推荐文章
徐小夕 徐小夕
4年前
《前端实战总结》之迭代器模式的N+1种应用场景
眼看12月就来了,抓住今年的尾巴,好好总结一下前端的不足与收获。这篇文章是笔者写设计模式专题的第二篇文章,也是基于工作中的总结和提炼,在实际应用场景中都会大量使用,至于为什么要写设计模式,主要是为了提高团队代码质量和可维护性,后续会继续推出设计模式相关的文章,供大家参考和学习。你将学到迭代器模式的含义实现一个数组迭代器实现一个对象迭代器
责任链和策略设计模式-基于Java编程语言
责任链和策略设计模式这两种设计模式非常实用,下面简单介绍一下我对这两种设计模式的理解和它们在Spring框架源码中的应用。
Wesley13 Wesley13
3年前
java中饿汉与懒汉的故事(单例设计模式)
java中的单例设计模式关于设计模式,这其实是单独存在的东西,它不属于java,但是在java中使用较多,所以今天我就给大家介绍下单例设计模式中的饿汉和懒汉这俩朴素的打工人。首先我先说明下单例设计模式是啥(如果不想了解,可以直接划下去看饿汉和懒汉):类的单例设计模式就是采用一定的方法保证在整个软件系统中,对某个类只能存在一
架构师日记-深入理解软件设计模式 | 京东云技术团队
本文从设计模式与编程语言的关系,设计模式与架构模式的区别,设计原则和设计模式的关系等几个维度进行了分析和解答。关于设计模式应该如何学习和应用的问题,给出了学习意见和实践心得。
Wesley13 Wesley13
3年前
Java 设计模式(1)
设计模式(Designpattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种
Wesley13 Wesley13
3年前
InfoQ 趋势报告:架构和设计领域技术演变详解
https://www.infoq.cn/article/R7lWXd0R4VFf3E0bB\38本文概述了我们对当前“架构和设计”领域的看法,这个领域侧重于基础设施模式、技术框架模式的实现,以及软件架构师必须掌握的设计流程和技能。关键要点:我们看到了“演化式架构”设计需求的增长,这种架构建立在可替换性设计和关注“胶水”
设计模式之策略模式:让你的代码灵活应对不同的算法 | 京东云技术团队
作为一个程序员,我们经常会面临着在不同的情况下选择不同的算法来解决问题的需求。这种情况下,策略模式是一个非常有用的设计模式。在本文中,我将向你介绍策略模式的概念、结构以及如何应用这个模式来使你的代码更灵活。
玩转Spring状态机 | 京东云技术团队
说起Spring状态机,大家很容易联想到这个状态机和设计模式中状态模式的区别是啥呢?没错,Spring状态机就是状态模式的一种实现,在介绍Spring状态机之前,让我们来看看设计模式中的状态模式。1\.状态模式状态模式的定义如下:状态模式(StatePat
京东云开发者 京东云开发者
6个月前
玩转Spring状态机
说起Spring状态机,大家很容易联想到这个状态机和设计模式中状态模式的区别是啥呢?没错,Spring状态机就是状态模式的一种实现,在介绍Spring状态机之前,让我们来看看设计模式中的状态模式。1.状态模式状态模式的定义如下:状态模式(StatePatt
京东云开发者 京东云开发者
5个月前
从多线程设计模式到对 CompletableFuture 的应用
最近在开发延保服务频道页时,为了提高查询效率,使用到了多线程技术。为了对多线程方案设计有更加充分的了解,在业余时间读完了《图解Java多线程设计模式》这本书,觉得收获良多。本篇文章将介绍其中提到的Future模式,以及在实际业务开发中对该模式的应用,而这些