仅供参考, 自建索引, 以备后查
数据库事务:从事务开始到结束的所有数据库操作,要么同时成功,要么同时失败。
ACID:原子性 Atomicity 一致性 Consistency 隔离性 Isolation 持久性 Durability
分布式事务应用场景:单体数据库纵向拆分为多个平行的数据库后,由于数据库事务仅支持单个数据库,当需要对多库实现事务时,需用到分布式事务
分布式事务问题也叫 分布式数据一致性问题,实际应用中,就尽可能从设计层避免分布式事务问题
X/Open 分布式事务模型 2PC
X/Open DTP (X/Open Distributed Transaction Processing Reference Model)
两阶段提交(2PC, Two-Phase-Commit),包含有三种角色:
RM 注册到 TM,AP 从 TM 申请 RM 连接,生成全局事务并获取 XID。之后 AP 加入 XID 参数执行操作,事务结束,TM 通知事务结束并根据 RM 执行结果执行提交或回滚操作。TM 与 多个 RM 之间的事务控制是基于 XA (XA Specification) 协议来完成的,Oracle、MySQL、DB2 实现了此 XA 协议接口。
两阶段提交 2PC:
缺点:
三阶段提交(加入超时机制,避免资源永久锁定)
CAP定理
CAP定理,又名 布鲁乐定理。 简单来说指:在分布式系统中不能同时满足 一致性 Consistency、可用性 Availability 和 分区容错性 Partition Tolerance,最多同时满足两个,如满足 CP 或 AP,不可能实现 CAP 或 CA。原因是 网络通信 不是绝对可靠,网络延时、网络异常时常发生。
CP 模式:两阶段与三阶段提交就是采用这种方案,可能导致用户完成操作需要等待较长时间的问题
AP 模式:放弃强一致,运用其它技术实现最终一致,AP 模式是解决分布式数据一致性问题的主要选择
BASE理论
基于CAP中CA模式不可实现而衍生的新思想,通过牺牲数据一致性获得高可用性,有以下有三个特性:
分布式事务问题常见解决方案 TCC补偿方案 (两阶段提交)
TCC (Try-Confirm-Cancel) 比较成熟的分布式数据一致性解决方案,把完整业务拆分为三个步骤:
分布式事务问题常见解决方案 基于可靠性消息的最终一致性方案
互联网公司常用的方案,主要利用消息中间件 (Kafka RocketMQ RabbitMQ) 的可靠性机制来实现数据一致性。
分布式事务问题常见解决方案 最大努力通知型
与基于可靠性消息类似,适用于对数据一致要求不高的场景,如支付宝支付,用户支付之后,支付宝服务器会在一定时间内不停的通知用户程序,并提供交易结果查询接口。
分布式事务框架 Seata
开源分布式事务解决方案,主要针对微服务分布式事务,提供 AT、TCC、Saga 和 XA 事务模式。
Seata AT 模式 (两阶段提交)
Seata 主推方案,基于 XA 演进而来,分为 TM、RM 和 TC 三大模块。TM 与 RM作为 Seata 客户端与业务系统交互,TC 则作为 Seata 服务端独立部署。
Seata Saga 模式(多事务,若失败则挨个回滚)
又称长事务解决方案,主要用于在没有两阶段的情况下使用。核心思想是:跨多个数据库的业务流程拆分为多个本地短事务,之后若出现回滚操作,则对已提交的事务进行补偿。例如:对增加值的进行减法操作(对某些资源会出现一定的问题)
Seata 安装
独立安装、Docker、Kubernetes、Helm 等方式。
Linux/Mac 下启动
# sh seata-server.sh
# sh seata-server.sh -p 8091 -h 192.168.0.120 -m file 指定机器端口及file存储模式
可以使用 -m db 来使用 db 模式存储相关数据,并在文件 \conf\file.conf 中配置数据库连接
全局事务 globaltable 、分支事务 branchtable、全局锁 lock_table
\conf\registry.conf
其中 registry 表示使用的注册中心组件,如 nacos、zookeeper,默认情况是 file 可快速集成 Seata,但 file 模式不具备注册中心的动态发现与动态配置功能;
而 config 则用于配置 Seata 服务端,通过此项设置 Seata 服务端的加载的配置文件,支持从远程配置中心读取或本地读取,远程读取设置的设置方式与 registry 相同。
\conf\file.conf
存储 Seata 服务端的配置信息,包含 transport、server、metrics 分别表示 协议配置、服务端配置、监控
配置中心加载配置信息
使用 Nacos 作为配置中心
Seata AT 模式集成 Dubbo
示例:订单服务、库存服务、账户服务、公共服务、业务REST接口服务;服务之间相互调用
分布式数据库:seata_order、seata_repo、seata_account 共三个
每个服务创建单独 module 并引用相关依赖
逐个启动服务并访问 业务REST接口服务 对外的接口
添加Seata分布式事务
引用Seata的Starter组件依赖
application.yml中添加Seata配置项
在各个数据库中添加 回滚日志表
添加全局事务控制 @GlobalTransactional
Spring Cloud Alibaba 集成 Seata AT 模式
添加 Spring Cloud Alibaba Seata 依赖,由于不支持 yml 形式,只能用 file.conf 与 registry.conf 来配置。
在访问数据库中的服务中,添加配置类,设置 数据源代理DataSourceProxy、初始化GlobalTransactionalScanner装载到Spring IOC容器
注意 @SpringBootApplication(exclude=GlobalTransactionalAutoConfiguration.class)
Seata AT 模式实现原理
AT模式从XA事务模型演进而来,也是一个 改进版的两阶段提交协议
把修改前及修改后的数据存入 undo_log 表中,之后释放本地事务锁定的资源,若之后阶段出现异常,则根据 undo_log 进行回滚。
在 XA 中则会一直锁定资源到最后阶段,这是最大的不同点。
TC收到所有事务分支的事务状态汇报后,来决定提交或回滚。
若提交:清理 undo_log 的回滚数据即可,毕竟之前一步中,所有分支事务都已经提交了。
若回滚:根据 undo_log 的数据进行补偿,只要各个分支事务回滚成功,数据一致性就得到保证了。
ACID中的隔离性,指的是:多个用户并发访问数据库时,多个并发事务之间要相互隔离,互不干扰。
AT模式中,多个全局事务操作同一张表时,事务隔离性是基于全局锁来实现的,
写隔离:(针对同一张表同一字段的更新)分布式事务分为本地锁(数据库锁)和全局锁(分布式锁),本地锁获取后只要是全局锁获取不成功,最终不会提交,会回滚。因此就算多个事务同时执行,仍不会发生脏写;
读隔离:Read Uncommitted 读取未提交内容、Read Committed 读取提交内容、Repeatable Read 可重读、Serialiable 可串行化
多个服务之间相互通信 即 系统间通信协作通常有两种:
HTTP/RPC 通信实时,但耦合性高;
消息通信 降低耦合,提高系统处理能力,但非实时
RocketMQ 是分布式消息中间件(消息队列),阿里巴巴经多年双11难的一个低延迟、高可靠、可伸缩、易于使用的消息框架,提供顺序消息、事务消息、定时消息、消息重试和追踪等功能。应用场景:削峰填谷(解决突发的大量请求)、异步解耦(主要业务完成后附加业务异步通知执行)、顺序收发(各个子业务之间需要先后执行,如先扣款再发货之后收货等)、分布式事务一致性(交易系统与支付系统要最终数据一致)、大数据分析、分布式缓存(各服务器缓存数据,接收通知更新缓存)。
安装:依赖JDK1.8及以上,分三种集群部署模式,可源码安装或w使用安装包。
发消息:Spring Cloud Alibaba 已集成 RocketMQ,使用 Spring Cloud Stream 即可。
引入依赖、配置连接信息等、编码使用Binder发送消息
@EnableBinding({Source.class}) 绑定配置文件中名称为 output 的消息通道,output 在 Source 类中定义。
可以自定义消息通道,参考 Source,使用 @Output("order_output") 即可定义一个 order_output 消息通道;当然,需要在配置文件中配置 order_output 的配置项。
接收消息:引入依赖、配置连接信息等、编码监听消息
@EnableBinding({Sink.class}) 默认存在 input 的消息通道,使用 @StreamListener(value=Sink.INPUT) 标记方法用于接收消息。
同样可以自定义,参考 Sink,使用 @Input("order_input") 即可定义 order_input 消息通道;同样需要进行配置 order_input 配置项。
Spring Cloud Stream:用于简化 Spring Cloud 应用程序中消息业务的开发。
应用程序通过注入输入通道 input 和 输出通道output 与 消息中间件Middleware通信,输入输出通道通过绑定器 Binder 连接到外部代理。
此框架基于 发布/订阅 机制,
核心由四部分构成: SpringFramework 中的 Spring Messaging 和 Spring Integration,以及 Spring Cloud Stream 中的 Binders 和 Bindings。
Spring Messaging:Spring Framework 中的统一消息编程模型
Message(消息对象,包含消息头Header和消息体Payload)、
MessageChannel(消息通道接口,用于接收发送消息)、
MessageHandler(消息处理接口)
Spring Integration:Spring Framework 中用于支持企业集成的扩展机制,对 Spring Messaging 进行扩展
MessageDispatcher(消息分发接口,用于分发消息和添加删除消息处理器)
MessageRouter(消息路由接口,定义默认输出消息通道)
Filter(消息过滤注解,配置消息过滤表达式)
Aggregator(消息聚合注解,用于多条消息合并)
Splitter(消息分割,一条消息拆分为多条)
Binders:目标绑定器,负责与消息中间件集成
doBindProducer(绑定消息中间件发送消息模块)
doBindConsumer(绑定消息中间件接收消息模块)
Bindings:外部消息中间件系统与应用程序的生产者和消费者(由Binders创建)之间的桥梁
Spring Cloud Alibaba RocketMQ 架构图
MessageChannerl(output)、MessageChannel(input) 为消息通道,用于发送订阅消息,Spring Cloud Stream标准接口
Binder bindProducer、Binder bindConsumer 目标通道绑定器,与RocketMQ消息服务器交换消息,即发布与接收消息,
由 Spring Cloud Alibaba 按照 Spring Cloud Stream 标准协议实现
Spring Cloud Stream 消息发送流程
业务代码调用 MessageChannel send();
org.springframework.integration.channel.AbstractMessageChannel 是接口 MessageChannel 的抽象实现类,定义发送、接收消息的公用方法;
消息传递到 org.springframework.integration.channel.AbstractSubscribableChannel 类,执行 doSend(),方法中 获取 MessageDispatcher 的实现类 UnicastingDispatcher;
调用 org.springframework.integration.dispatcher.UnicastingDispatcher dispatcher() 把消息分发给所有的 MessageHandler;
MessageHandler 执行 handleMessage() 处理消息。
注册 MessageHandler 是通过 org.springframework.cloud.stream.binder.AbstractMessageChannelBinder 在初始化 Binding 时执行 doBindProducer 调用 AbstractSubscribableChannel subscribe() 设置的。
AbstractMessageChannelBinder 的初始化则由 org.springframework.cloud.stream.binding.AbstractBindingLifecycle 在 Spring 容器加载 Bean 完成初始化后执行的。
RocketMQ Binder 集成消息发送
同时 AbstractMessageChannelBinder 提供了创建 MessageHandler 的规范,即调用 abstract 方法 createProducerMessageHandler(),而 com.alibaba.cloud.stream.binder.rocketmq.RocketMQMessageChannelBinder 则是实现了此方法并创建了 com.alibaba.cloud.stream.binder.rocketmq.integration.RocketMQMessageHandler 实例,是 MessageHandler 的 RocketMQ 具体实现,在 RocketMQBinder 中实现转化消息格式并发送消息。
RocketMQMessageHandler 中有 org.apache.rocketmq.spring.core.RocketMQTemplate 对象,是对 RocketMQ 客户端API的封装,SpringBoot支持RocketMQTemplate,SpringCloudStream对其同样兼容。
DefaultMQProducer 是 RocketMQ 客户端提供的 API,用于将消息发送到 RocketMQ 消息服务器。
RocketMQMessageHandler 则对 Message 进行解析,根据参数设置调用不同实现。如根据Header属性决定发送普通消息、事务消息、定时消息或顺序消息。
RocketMQ Binder 集成消息订阅
AbstractMessageChannelBinder createConsumerEndpoint() 用于创建 MessageProducer ,在初始化Binder时调用。
RocketMQMessageChannelBinder 具体实现了 createConsumerEndpoint() 并创建了 com.alibaba.cloud.stream.binder.rocketmq.integration.RocketMQInboundChannelAdapter 对象,此类是接口 MessageProducer 的实现类,其适配了 SpringFramework的重试和回调机制,用于在 RocketMQ Binder 中订阅消息并转化消息格式。
com.alibaba.cloud.stream.binder.rocketmq.consuming.RocketMQListenerBindingContainer 是接口 RocketMQListenerContainer 的实现,通过 setupMessageListener() 方法设置 RocketMQListener 来消费消息。DefaultMessageListenerOrderly 与 DefaultMessageListenerConcurrently 是 MessageListener 的两种实现,顺序消费和并发消息。在 consumeMessage() 执行时会调用 RocketMQListener onMessage() 来调用业务代码处理消息。
Spring Cloud Stream 消息订阅流程
企业Binder 收到订阅消息,发送到 MessageChannel,再通过 Dispatcher 调用 MessageHandler,最后调用 @StreamListener 注解标记的方法调用业务方法。
RocketMQInboundChannelAdapter extends MessageProducerSupport 调用 sendMessage() 发送消息。
方法内部调用 getOutputChannel 得到初始化 RocketMQ Binder 时传入的 DirectChannel 通道。
MessagingTemplate extends GenericMessagingTemplate 执行 send() 再次发送消息,通过 DirectChannel 通道获取 MessageDispatcher 的实现类 UnicastingDispatcher;
之后进行消息分发,传递消息给 MessageHandler。
DirectChannel 对应的消息处理器是 StreamListenerMessageHandler ,此处理器会调用使用 @StreamListener 注解标记的业务方法。
StreamListenerAnnotationBeanPostProcessor 扫描类找到使用 @StreamListener 标记的 Method,并创建 StreamListenerHandlerMethodMapping 对象,保存了映射关系。
映射关系为 StreamListener 到 HandlerMethod。因此在 StreamListenerMessageHandler 处理器中可以调用 @StreamListener 的业务方法。
RocketMQ 集群管理
分布式服务SOA中,中间件和应用不允许单点存在,服务发现机制因此是必备的。
RocketMQ 是淘宝中间件团队参考 Kafka 重新设计用 Java 编写出来的,因此概念会有 Kafka 相似。
Kafka、RabbitMQ、RocketMQ 等都是基于 发布/订阅 机制,消息发送方Producer 发送消息到 消息服务器,消息接收方Consumer 从 消息服务器订阅消息。 发送方与接收方为客户端,消息服务器是服务端,客户端与服务端需要通过 注册中心 来感知对方。
RocketMQ 架构四大组件:
Producer:消息提供者,发送消息到 Broker,支持分布式集群方式部署
Consumer:消息接收者,负责从 Broker 订阅消费消息,同样支持分布式集群方式部署
Broker:消息存储角色,用于消息存储、投递和查询,具有服务高可用保证,支持分布式部署
NameServer:服务管理角色,负责管理 Broker 集群的路由信息,支持分布式集群方式部署。 简单Topic模式的路由注册中心,类似于Dubbo中的Zookeeper、Nacos,支持 Broker 动态注册与发现。包含 服务注册(接收Broker注册信息,保存并启动心跳检查) 和 路由信息管理(提供给客户端查询Broker接口,因此客户端可以与Broker进行消息发送与接收)。
基本概念:
Message:消息,传输信息的物理载体,生产和消费数据的最小单位。必须关联一个Topic,且拥有唯一MessageID,并可携带具有业务标识的Key
Topic:主题,一类消息的集合,包含若干消息,Topic是RocketMQ进行消息订阅的基本单位
Queue:消息队列,组成Topic的最小单元。默认情况是一个Topic对应多个Queue。Topic是逻辑概念,Queue是物理概念。消费Topic消息实际是拉取Queue消息。
Tag:区分同一个主题下不同类型的消息,优化RocketMQ的消息查询系统,且可以根据Tag实现不同的处理逻辑,扩展性更好
UserProperties:用户自定义属性,属于Message一部分
ProducerGroup:同类Producer集合,发送消息逻辑一致的一组服务器。当某台消息提供者出问题,则Broker从组内选取其它一台进行交互
ConsumerGroup:同类Consumer集合,接收消息后的处理逻辑一致的一组服务器,订阅了相同的Topic消息
ZooKeeper VS NameServer
Kafka服务注册与发现通常用ZooKeeper来完成,在Kafka中,Topic是逻辑概念,分区(Partition)是物理概念。1个Topic可以设置多个分区,每个分区又可以设置多个副本(Replication),即1 Master - N Slave 模式。 Master是经过选举得出的,Broker实例具有Master/Slave双重身份,就看选举结果如何了。
RocketMQ使用NameServer,Topic也是逻辑概念,队列(Queue)是物理概念(与kafka的分区对应),同样 1 Master - N Slave 模式。Master与Slave是预告设置的。
本人根据书中介绍得到的是,Kafka中,Master和其对应的Slave在同一个进程中进行管理,而Rocket则用不同进程来管理Master与Slave。有其它见解或者实际研究过的欢迎来更正、来打扰
顺序消息 :业务流程需要先后次序时使用,实现类似对列FIFO的效果;分为 局部有序 和 全局有序
局部有序:发送到同一队列的消息有序,发送时需指定队列。
发送:设置输出通道的 sync=true e.g. spring.cloud.stream.rocketmq.bindings.output.producer.sync=true;消息发送时,指定Header头信息 MessageBuilder.withPayload(message).setHeader(BinderHeaders.PARTITION_HEADER,0);
接收:设置接收通道的 orderly=true e.g. spring.cloud.stream.rocketmq.bindings.input.consumer.orderly=true。
全局有序:设置Topic指定全局有序,性能差,不推荐使用
发送方式:同步、异步、单向
同步:发送请求后等待Broker服务器返回,支持失败重试,适用较重要的场景
异步:异步发送消息,不阻塞进程,不支持失败重试,适用响应时间要求高的场景
单向:与异步一致,但不支持回调,应用于时间要求超高,可靠性要求不高的场景