分布式对应的是单体架构,早期单体架构是非常流行的,如下
但随着业务的复杂度提高,逐渐演变出了分布式服务,互相协作,每个服务负责不同的业务,如下
服务与服务之间需要远程协作才能完成事务,这种分布式环境下由不同的服务之间通过远程协作完成的事务称之为分布式事务,如创建订单扣减库存事务。
在分布式架构下,每个节点只知晓自己操作的失败或者成功,无法得知其他节点的状态。当一个事务跨多个节点时,为了保持事务的原子性与一致性,而引入一个协调者来统一掌控所有参与者的操作结果,并指示它们是否要把操作结果进行真正的提交或者回滚(rollback)。典型的场景就是微服务架构下微服务之间通过远程服务调用完成事务操作。
1、Seata概念
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
Seata特点:
Seata 支持四种事务模式:
Seata 中三个核心概念:
其中,TC 为单独部署的 Server 服务端,TM 和 RM 为嵌入到应用中的 Client 客户端。
2、搭建Seata服务端
Seata的协调者其实就是阿里开源的一个服务,其下载地址:
https://github.com/seata/seata/releases
此处使用的版本是1.3.0,这个工具在windows或linux上部署的差别不大,此处部署在windows上。
首先下载1.3.0版本的zip压缩包,然后解压到指定目录。
①创建TC所需要的表
创建名为seata的数据库,找到seata-1.3.0源码的script\server\db这个目录,将会看到以下SQL文件:
此处选中mysql数据库,执行sql脚本即可,创建了3张表:
Server 端的 DB tables 创建完成之后,你还得为每个微服务背后的数据库创建一个特殊的表,叫做 undo_log,在 Seata 的 AT 模式下,Seata Server 发起一个 Rollback 指令后,微服务作为 Client 端要负责执行一段 Rollback 脚本,这个脚本所要执行的回滚逻辑就保存在 undo_log 中,undo_log 的建表语句可以从资源文件目录下的 client.sql 文件中找到。
②在conf目录中配置两个地方
a.首先配置 file.conf 文件
b.再配置 registry.conf 文件
file.conf 中配置 TC 的存储模式,TC 的存储模式有三种:
在seata-1.3.0\script\config-center\nacos中有一个脚本nacos-config.sh则是将config.txt中的全部配置自动推送到nacos中,运行下面命令(windows可以使用git bash运行,git在windows环境自行下载安装即可):
# -h 主机,你可以使用localhost,-p 端口号 你可以使用8848,-t 命名空间ID,-u 用户名,-p 密码
$ sh nacos-config.sh -h 127.0.0.1 -p 8848 -g SEATA_GROUP -u nacos -w nacos
推送成功则可以在Nacos中查询到所有的配置,如下
注意sh所在路径不能有空格,否则报错。
启动即可,在nacos中查看如下
3、CAP原则
CAP原则又叫CAP定理,同时又被称作布鲁尔定理(Brewer’s theorem),指的是在一个分布式系统中,不可能同时满足以下三点。
在分布式系统中,必须满足CAP中的P,此时只能在C和A之间做出取舍,若是选择了CA,舍弃了P,说白了就是一个单体架构。
4、Base理论
BASE理论是对CAP中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。
5、Seata实例
以电商系统为例介绍一下如何编码实现分布式事务。
用户购买商品完成下单的业务逻辑,整个业务逻辑有2个微服务提供支持:
搭建product商品服务
①pom依赖
<!-- seata -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
注意依赖的版本与服务的版本保持一致。
②创建数据库,新建undo_log:回滚日志表,这是Seata要求必须有的,每个业务库都应该创建一个,SQL如下:
CREATE TABLE `undo_log` (
`branch_id` bigint(20) NOT NULL COMMENT 'branch transaction id',
`xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'global transaction id',
`context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'undo_log context,such as serialization',
`rollback_info` longblob NOT NULL COMMENT 'rollback info',
`log_status` int(11) NOT NULL COMMENT '0:normal status,1:defense status',
`log_created` datetime(6) NOT NULL COMMENT 'create datetime',
`log_modified` datetime(6) NOT NULL COMMENT 'modify datetime',
UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = 'AT transaction mode undo table' ROW_FORMAT = Compact;
③bootstrap.yml中配置seata
# 客户端seata的相关配置
seata:
## 是否开启seata,默认true
enabled: true
application-id: ${spring.application.name}
## seata事务组的名称,一定要和config.tx(nacos)中配置的相同
tx-service-group: ${spring.application.name}-tx-group
## 配置中心的配置
config:
## 使用类型nacos
type: nacos
## nacos作为配置中心的相关配置,需要和server在同一个注册中心下
nacos:
## 命名空间,需要server端(registry和config)、nacos配置client端(registry和config)保持一致
namespace:
## 地址
server-addr: localhost:8848
## 组, 需要server端(registry和config)、nacos配置client端(registry和config)保持一致
group: SEATA_GROUP
## 用户名和密码
username: nacos
password: nacos
registry:
type: nacos
nacos:
## 这里的名字一定要和seata服务端中的名称相同,默认是seata-server
application: seata-server
## 需要server端(registry和config)、nacos配置client端(registry和config)保持一致
group: SEATA_GROUP
namespace:
username: nacos
password: nacos
server-addr: localhost:8848
④分布式事务
@GlobalTransactional
@Override
public void addOrderLogsAndAddBrand(Long orderId, Integer orderStatus, String user, String detail) {
log.info("添加订单操作日志,orderId={},detail={}", orderId, detail);
OmsOrderLog orderLog = new OmsOrderLog();
orderLog.setDetail(detail);
orderLog.setOrderId(orderId);
orderLog.setOrderStatus(orderStatus);
orderLog.setUser(user);
boolean save = this.save(orderLog);
log.info("下单:" + save);
Object deduct = productFeignClient.deduct();
log.info("扣减:" + deduct.toString());
}
搭建order订单服务同product商品服务。
6、测试
先启动Nacos注册中心,然后启动Seata Server,最后启动Order、Product微服务。
测试下单接口服务,生成订单的同时进行扣减库存,当扣减库存失败时,分布式事务进行回滚,数据库中的数据会保持原样。
分布式事务正常时如下:
分布式事务异常时如下:
将扣减库存的接口手动设置休眠2min,这样肯定超时了,下单操作则整体回滚,数据库中数据未变,则说明分布式事务成功了。
第一个操作对应的库操作先增加一条记录,第二个库执行异常未进行库操作,导致分布式事务异常,进行回滚,第一个操作对应的库中新增加的记录又被删除了。