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

news2024/11/17 0:26:55

大家好,我是 方圆。最近在读《微服务架构设计模式》,开始的时候我非常的好奇,因为在我印象中,设计模式是常说的那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 章

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1574037.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【第二十篇】使用BurpSuite实现SSRF(实战案例)

SSRF漏洞:向服务器发送伪造请求即可访问或操作服务器上的资源。 假设一个网站http://ice.com,允许用户输入一个URL,然后在服务器端通过发起HTTP请求来获取该URL的内容并显示给用户。如果网站没有对URL进行验证,通过输入 http://ice/admin ,即可获取到内部系统的敏感信息,…

【数据结构】考研真题攻克与重点知识点剖析 - 第 3 篇:栈、队列和数组

前言 本文基础知识部分来自于b站:分享笔记的好人儿的思维导图与王道考研课程,感谢大佬的开源精神,习题来自老师划的重点以及考研真题。此前我尝试了完全使用Python或是结合大语言模型对考研真题进行数据清洗与可视化分析,本人技术…

【学习笔记】Elsevier的Latex模板文件(附网址)

注:这是一篇没有技术含量的水文,主要是看有人下载下来,居然当成资源需要积分才能下载。我觉得不行,故提供原始下载地址供查阅使用。 链接: 上述图片所示网址:链接直达

Kubernetes学习笔记8

Kubernetes集群客户端工具kubectl 我们已经能够部署Kubernetes了,那么我们如何使用Kubernetes集群运行企业的应用程序呢?那么,我们就需要使用命令行工具kubectl。 学习目标: 了解kubectl 命令帮助方法 了解kubectl子命令使用分…

C++如何分开配release和debug版本编译环境

右键当前文件->属性。 在下拉栏目中找到release和dubug分别进行配置。

【qml】 在QML场景图中使用QPainter 的方法

【qml】 在QML场景图中使用QPainter 的方法_qquickpainteditem-CSDN博客 QQuickPaintedItem 1.官方资料 QQuickPaintedItem类提供了一种在QML场景图中使用QPainter API的方法。 QQuickPaintedItem使QPainter API与QML场景图一起使用成为可能。它在场景图中设置了一个纹理矩…

【Spring Cloud Alibaba】9 - OpenFeign集成Sentinel实现服务降级

目录 一、简介Sentinel 是什么如何引入Sentinel 二、服务搭建1.安装Sentinel控制台1.1 下载1.2 启动1.3 访问 2.改造服务提供者cloud-provider服务2.1 引入依赖2.2 添加API2.3 添加配置文件 3.改造cloud-consumer-feign服务3.1 引入依赖3.2 添加Feign接口3.3 添加服务降级类3.4…

InnoDB 数据页结构

1.行格式 1.1 Compact行格式 1.1.1 示意图 1.1.2 准备一下 1)建表 mysql> CREATE TABLE record_format_demo (-> c1 VARCHAR(10),-> c2 VARCHAR(10) NOT NULL,-> c3 CHAR(10),-> c4 VARCHAR(10)-> ) CHARSETascii ROW_FORMATCOM…

卫星遥感影像如何选择合适的分辨率

​ 卫星遥感影像的分辨率是影响其应用效果的关键因素之一。分辨率越高,所获取的图像细节越丰富,能够更准确地反映地物的特征和变化。因此,在选择卫星遥感影像时,需要根据实际需求和数据可获取性来选择合适的分辨率。 一、分辨…

大语言模型上下文窗口初探(下)

由于篇幅原因,本文分为上下两篇,上篇主要讲解上下文窗口的概念、在LLM中的重要性,下篇主要讲解长文本能否成为LLM的护城河、国外大厂对长文本的态度。 3、长文本是护城河吗? 毫无疑问,Kimi从一开始就用“长文本”占领…

162 Linux C++ 通讯架构实战16,UDP/TCP协议的优缺点,使用环境对比。UDP 服务器开发

UDP/TCP协议的优缺点 TCP :面向连接的,可靠数据包传输。对于不稳定的网络层,采取完全弥补的通信方式。丢包重传 优点:稳定,数据流量稳定,速度稳定,顺序稳定 缺点:传输速度慢&…

大语言模型上下文窗口初探(上)

由于篇幅原因,本文分为上下两篇,上篇主要讲解上下文窗口的概念、在LLM中的重要性,下篇主要讲解长文本能否成为LLM的护城河、国外大厂对长文本的态度。 1、什么是上下文窗口? 上下文窗口(context window)是…

【已解决】ZIP压缩文件如何设置密码?

ZIP是常用的压缩格式之一,对于重要的ZIP文件,我们还可设置密码保护,那ZIP压缩文件怎么设置密码呢?不清楚的小伙伴一起来看看吧! 给ZIP文件设置密码,我们需要用到支持ZIP格式的解压缩软件,比如7…

数字乡村:科技引领新时代农村发展

随着信息技术的迅猛发展和数字化浪潮的推进,数字乡村作为新时代农村发展的重要战略,正日益成为引领农村现代化的强大引擎。数字乡村不仅代表着农村信息化建设的新高度,更是农村经济社会发展的重要支撑。通过数字技术的深入应用,农…

41.基于SpringBoot + Vue实现的前后端分离-校园网上店铺管理系统(项目 + 论文PPT)

项目介绍 二十一世纪互联网的出现,改变了几千年以来人们的生活,不仅仅是生活物资的丰富,还有精神层次的丰富。本课题研究和开发校园网上店铺,让安装在计算机上的该系统变成管理人员的小帮手,提高校园店铺商品销售信息处…

C语言——详解字符函数和字符串函数(二)

Hi,铁子们好呀!之前博主给大家简单地介绍了部分字符和字符串函数,那么这次,博主将会把这些字符串函数给大家依次讲完! 今天讲的具体内容如下: 文章目录 6.strcmp函数的使用及模拟实现6.1 strcmp函数介绍和基本使用6.1.1 strcmp函…

还在担心报表不好做?不用怕,试试这个方法(三)

系列文章: 《还在担心报表不好做?不用怕,试试这个方法》(一) 《还在担心报表不好做?不用怕,试试这个方法》(二) 概要 在上一篇文章《还在担心报表不好做?…

python爬虫学习第十五天-------ajax的get和post请求

嗨嗨嗨!兄弟姐妹大家好哇!今天我们来学习ajax的get和post请求 一、了解ajax Ajax(Asynchronous JavaScript and XML)是一种在 Web 开发中用于创建交互式网页应用程序的技术。通过 Ajax,网页可以在不重新加载整个页面…

C语言——#define的使用

#define定义常量 基本语法 #define name stuff //(#define)(变量名)(定义的数值) 这里记得,是不加分号的 定义常量(这里 就要涉及我们经常说的宏定义) 定义常量的使…

langchain 学习笔记-FunctionCalling三种方式

ChatGPT 基于海量的训练数据生成答案,所以它无法回答训练数据中没有的信息或搜索信息 。人们希望 ChatGPT 具有对话以外的各种功能,例如“我想管理我的待办事项列表”。 函数调用是对此类请求的响应。 通过使用函数调用,ChatGPT 现在可以在生…