Seata 为什么出现
分布式事务的解决方案,来保证其发生异常时,该业务调用的全部服务关于数据修改,新增的操作,要么都成功,要么都失败。保证数据的正确性。
Seata 的基本概念
- Transaction ID:全局事务ID
- TC:总控制器,统一管理各个服务的事务提交和回滚。
- TM:分布式事务的发动者。控制其范围,确认有哪些服务的事务要管理。
- RM:各个服务的事务执行情况,将提交,还是回滚的执行情况反馈给TC,让TC来决定整体事务是提交还是回滚操作。
Seata 安装使用
- 从官网 http://seata.io/zh-cn/ 下载对应版本zip包 解压到本地后修改config目录下的file.conf配置文件,设置自定义事务组名称和数据库地址
file.conf文件中的service模块的vgroup_mapping.my_test_tx_group 设置自定义事务组名称。
service {
# 修改事务组的名称为 fsp_tx_group 随便定义
vgroup_mapping.my_test_tx_group = "fsp_tx_group"
default.grouplist = "127.0.0.1:8091"
enableDegrade = false
disable = false
max.commit.retry.timeout = "-1"
max.rollback.retry.timeout = "-1"
}
再修改file.conf文件中的store 模块,将模式从file变成db,对应的db数据库配置相应的信息
## transaction log store
store {
## store mode: file、db
mode = "db"
## file store
file {
dir = "sessionStore"
# branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
max-branch-session-size = 16384
# globe session size , if exceeded throws exceptions
max-global-session-size = 512
# file buffer size , if exceeded allocate new buffer
file-write-buffer-cache-size = 16384
# when recover batch read size
session.reload.read_size = 100
# async, sync
flush-disk-mode = async
}
## database store
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
datasource = "dbcp"
## MysqL/oracle/h2/oceanbase etc. MysqL 数据库连接信息
db-type = "MysqL"
driver-class-name = "com.MysqL.jdbc.Driver"
# 对应的数据库
url = "jdbc:MysqL://127.0.0.1:3306/seata"
user = "root"
password = "1234qwer"
min-conn = 1
max-conn = 3
# 下面为对应的服务事务存在快照信息的表名 全局,分支,行锁
global.table = "global_table"
branch.table = "branch_table"
lock-table = "lock_table"
query-limit = 100
}
}
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
# 类型设置为nacos
type = "nacos"
nacos {
# 地址设置本地 8848端口
serverAddr = "localhost:8848"
namespace = ""
cluster = "default"
}
......
就可以先启动nacos再到seata的bin目录下启动seata 查看是否成功
Seata 分布式事务发起端搭建
- 添加依赖
<!--nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<!-- 如果seata的版本和使用的版本不一致 要先删掉再添加上对应版本的seata-->
<exclusions>
<exclusion>
<artifactId>seata-all</artifactId>
<groupId>io.seata</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>0.9.0</version>
</dependency>
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--web-actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 配置yml文件
server:
port: 2001
spring:
application:
name: seata-order-service
cloud:
alibaba:
seata:
#自定义事务组名称需要与seata-server中的对应
tx-service-group: fsp_tx_group
nacos:
discovery:
server-addr: localhost:8848
datasource:
driver-class-name: com.MysqL.jdbc.Driver
url: jdbc:MysqL://localhost:3306/seata_order
username: root
password: 123456
feign:
hystrix:
enabled: false
logging:
level:
io:
seata: info
mybatis:
mapperLocations: classpath:mapper/*.xml
@EnablediscoveryClient
@EnableFeignClients
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//取消数据源的自动创建
public class SeataOrderMainApp2001
{
public static void main(String[] args)
{
SpringApplication.run(SeataOrderMainApp2001.class, args);
}
}
- 编写测试业务方法 使用@GlobalTransactional注解实现分布式事务。
/**
* 创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->修改订单状态
* 简单说:下订单->扣库存->减余额->改状态 可以实现当某个微服务失败时,其他成功的微服务数据会回滚数据。
*/
@Override
// 设置全局事务名称
@GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class)
public void create(Order order)
{
log.info("----->开始新建订单");
//1 新建订单
orderDao.create(order);
//2 扣减库存
log.info("----->订单微服务开始调用库存,做扣减Count");
storageService.decrease(order.getProductId(),order.getCount());
log.info("----->订单微服务开始调用库存,做扣减end");
//3 扣减账户
log.info("----->订单微服务开始调用账户,做扣减Money");
accountService.decrease(order.getUserId(),order.getMoney());
log.info("----->订单微服务开始调用账户,做扣减end");
//4 修改订单状态,从零到1,1代表已经完成
log.info("----->修改订单状态开始");
orderDao.update(order.getUserId(),0);
log.info("----->修改订单状态结束");
log.info("----->下订单结束了,O(∩_∩)O哈哈~");
}
其他的服务和该发起端服务配置上没有区别,就是@GlobalTransactional注解在对应的业务实现上不必写。
Seata 原理简介
在一阶段,Seata 会拦截“业务 sql”,
1 解析 sql 语义,找到“业务 sql”要更新的业务数据,在业务数据被更新前,将其保存成“before image”,
2 执行“业务 sql”更新业务数据,在业务数据更新之后,
3 其保存成“after image”,最后生成行锁。
以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。
如果都正常,则将before image和after image 和行锁生成的表数据删掉,正常的修改对应业务表数据
如果其中一个失败了,就要通过对应的before image来还原数据,而还原数据前要通过after image 和现在数据库对应数据比较是否一致,是否发生脏写,脏写要自行处理。没有脏写,才可以将数据还原。
原文地址:https://www.jb51.cc/wenti/3287615.html
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。