您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
工程中实践的微服务设计模式
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
工程中实践的微服务设计模式
wy****
2024-04-08
IP归属:北京
100浏览
最近在读《微服务架构设计模式》,开始的时候我非常的好奇,因为在我印象中,设计模式是常说的那23种设计模式,而微服务的设计模式又是什么呢?这个问题也留给大家,在文末我会附上我对这个问题的理解。本次文章的内容主要是工作中对微服务设计模式的应用,希望能对大家有所启发。 ### 事务发件箱模式 > 事务发件箱模式:将消息保存在数据库 **“发件箱”表** 作为事务的一部分 policy 为处理投保的微服务,以投保事务为例: ![投退保.png](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-04-06-19-20jexT0xUqJ8EBBAd.png) 如上图所示,在投保过程中有4步操作(注意持久化结算任务(Task)A的操作),这个过程便是事务发件箱模式的体现,为什么说它是事务发件箱模式呢? 1. Task 表中持久化的消息任务最终会被发送到 MQ 中,所以它是发件箱 2. 持久化 Task 记录的操作被包含在事务中,所以称它为事务发件箱 Task 记录中保存的消息体是通过另一个服务 service-job **轮询扫描初始化状态的任务**,并将其发送到 MQ 的,发送成功后任务修改为完成状态,这种方式被称为事务性发件箱模式中的**轮询发布数据模式**。 这种设计模式有一个弊端:**随着数据量不断增大,经常轮询数据库可能造成昂贵的开销**。在我们的系统中采用了两种措施进行优化: * **分库分表**:以空间换时间,避免单表数据量过大造成的开销 * **将完成状态的任务进行“数据结转”的设计**:任务先保存在 Task 表中,被执行完成后被归档到 TaskRecord 表中 此外还有一种更加高效但是开发稍复杂的方式:**拖尾数据库日志模式**。 ### 拖尾数据库日志模式 ![拖尾事务日志.png](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-04-06-19-22pnIVyvYLQre7uw7.png) 数据库的每次更新都对应着一条数据库事务日志,通过事务日志挖掘器读取事务日志,并将每条与消息有关的记录发送给消息代理(开源框架可参考:[Github: debezium](https://github.com/debezium/debezium),可以将MySQL的binlog读取到Kafka中),但是这种方法的弊端是需要在开发上做一些努力,因为需要监听数据库事务日志和调用数据库底层相关的API。 ### 相信“邮递员” 工程实践中,还有一种没有采用事务发件箱模式来保证数据一致性的方法:在事务中先持久化完成状态的任务,随后直接将消息发送给消息队列,如果消息发送失败,捕获异常并将任务修改成初始化状态,随后依赖 service-job 服务进行补偿:即将初始化状态的任务发送给 MQ。我们还是以投保的过程为例,如下代码所示: ```java // 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 的概念来看一下投保的流程: ![投退保分享.png](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-04-06-19-23HUsnn32xAgnKVobc.png) 在这个过程中,Saga的决策和执行顺序分布在Saga的每一个参与方中,并且**通过消息交换的方式**进行沟通,一个Saga的参与方执行完触发另一个Saga执行,保证数据一致性,这种方式被称为**协同式Saga**。 这种设计模式的优劣如下: * **优势**:比较简单,服务间松耦合 * **弊端**:比较难理解,因为它没有一个地方定义了Saga的执行流程,Saga的处理逻辑分布在不同的服务中,需要根据代码触发的任务去整理整个流程 > **为什么没有采用XA来实现分布式事务**? > > XA采用**两阶段提交协议**实现分布式事务,在事务中的所有参与者都成功时提交,有失败时便回滚。要使用该模式一方面要求所有的事务参与者(数据库或消息代理)满足XA标准,另一方面,它本质上是**同步的进程间通讯**,同步的通讯机制有一个弊端:它会降低分布式系统的可用性(假如分布式系统中每个服务的可用性为99%,如果服务与服务之间是同步调用的方式:服务必须从另外一个服务获取响应后才能返回它的客户端调用,那么分布式系统的可用性为各个服务可用性的乘积,随着同步交互服务的增加,可用性会随之降低,**最大化可用性的方式应该最小化系统间的同步操作量**)。所以,一般互联网公司很少采用强一致性的设计,而是采用最终一致性设计(银行可能会使用到强一致性)。此外,XA实现分布式事务需要依赖事务的协调者(如Seata),实现起来相比于上述方式复杂。 ### 编排式Saga Saga 的另一种实现方式是编排式,编排式 Saga 需要事务的协调者(DTM),全局事务发起人将整个全局事务的编排信息,包括每个步骤的正向操作和反向补偿操作定义好之后,提交给事务协调者(DTM),协调者按步骤异步执行Saga逻辑。 如果投保流程使用编排式Saga的话,投保成功的过程如下: ![投退保分享3.png](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-04-06-19-24jHOzjv7sTh24Lhf0.png) 编排式Saga的事务定义执行步骤非常灵活,假如我们要在投保失败的情况下做取消结算的**补偿逻辑**的话,可以自行定义,图示如下: ![投退保分享4.png](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-04-06-19-25QFspX7xii7FAgqQ.png) 这种设计模式的优劣如下: **优势**:能够集中流程控制、易于扩展和服务间松耦合,如果服务之间的依赖关系复杂,且业务流程经常变动,使用编排式Saga是合适的 **弊端**:引入协调者增加了开发复杂性(扩展学习:[DTM开源项目文档](https://dtm.pub/)) *** 现在我们回到文章开篇的问题:**微服务架构设计模式与我们常说的设计模式的区别是什么?** > 我们常说的设计模式是面向对象的设计模式,它的解决方案元素是类,而微服务设计模式是站在更高维度即系统架构层面的设计模式,它面向的对象是系统中各个服务,解决方案也由相互协作的服务构成的。 *** ### 巨人的肩膀 * 《微服务架构设计模式》:第 1 - 4 章
上一篇:AI从入门到入门之手写数字识别模型java方式Dense全连接神经网络实现
下一篇:写给职场新人|从迷茫到屡获殊荣的技术人成长之路
wy****
文章数
22
阅读量
2476
作者其他文章
01
高性能MySQL实战(一):表结构
最近因需求改动新增了一些数据库表,但是在定义表结构时,具体列属性的选择有些不知其所以然,索引的添加也有遗漏和不规范的地方,所以我打算为创建一个高性能表的过程以实战的形式写一个专题,以此来学习和巩固这些知识。1. 实战我使用的 MySQL 版本是 5.7,建表 DDL 语句如下所示:根据需求创建 接口调用日志 数据库表,请大家浏览具体字段的属性信息,它们有不少能够优化的点。CREATE TABLE
01
分布式服务高可用实现:复制
1. 为什么需要复制我们可以考虑如下问题:当数据量、读取或写入负载已经超过了当前服务器的处理能力,如何实现负载均衡?希望在单台服务器出现故障时仍能继续工作,这该如何实现?当服务的用户遍布全球,并希望他们访问服务时不会有较大的延迟,怎么才能统一用户的交互体验?这些问题其实都能通过 “复制” 来解决:复制,即在不同的节点上保存相同的副本,提供数据冗余。如果一些节点不可用,剩余的节点仍然可以提供数据服务
01
高性能MySQL实战(三):性能优化
这篇主要介绍对慢 SQL 优化的一些手段,而在讲解具体的优化措施之前,我想先对 EXPLAIN 进行介绍,它是我们在分析查询时必要的操作,理解了它输出结果的内容更有利于我们优化 SQL。为了方便大家的阅读,在下文中规定类似 key1 的表示二级索引,key_part1 表示联合索引的第一部分,unique_key1 则表示唯一二级索引,primary_key 表示主键索引。高性能MySQL实战(一
01
从2PC和容错共识算法讨论zookeeper中的Create请求
最近在读《数据密集型应用系统设计》,其中谈到了zookeeper对容错共识算法的应用。这让我想到之前参考的zookeeper学习资料中,误将容错共识算法写成了2PC(两阶段提交协议),所以准备以此文对共识算法和2PC做梳理和区分,也希望它能帮助像我一样对这两者有误解的同学。1. 2PC(两阶段提交协议)两阶段提交 (two-phase commit) 协议是一种用于实现 跨多个节点的原子事务(分布
wy****
文章数
22
阅读量
2476
作者其他文章
01
高性能MySQL实战(一):表结构
01
分布式服务高可用实现:复制
01
高性能MySQL实战(三):性能优化
01
从2PC和容错共识算法讨论zookeeper中的Create请求
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号