当前依赖,全局事务XID,不需要手动进行绑定,自动进行传递
<dependency>
<groupId>com.alibaba.cloud</groupId>
<!–加入spring-cloud-alibaba-seata,解决xid不传递问题–>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>2.2.0.RELEASE</version>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
</exclusion>
</exclusions>
</dependency>
全局事务XID需要通过过滤器或拦截器进行手动绑定,否则下游服务获取不到全局XID回滚不了
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.3.0</version>
<!-- 这里需要排除自身的seata-all -->
<exclusions>
<exclusion>
<artifactId>seata-all</artifactId>
<groupId>io.seata</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- 导入与之前下载的seata版本一致的包 -->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>1.3.0</version>
</dependency>
OpenFeign进行手动传递XID
@Component
public class FeignConfiguration implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
requestTemplate.header("XID", RootContext.getXID());
}
}
提供者手动绑定XID
@Component
public class SeataFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
//手动绑定XID
String xid = request.getHeader("XID");
if(StringUtils.isNotBlank(xid)){
RootContext.bind(xid);
}
filterChain.doFilter(servletRequest,servletResponse);
}
}
seata在1.0.0版本之后就不需要手动进行数据源代理,已经被自动代理
客户端的配置文件
seata:
enabled: true
tx-service-group: my_first_seata #配置文件中的事务服务组一样
config:
type: nacos # nacos中拉去对应的配置文件
nacos:
server-addr: 192.168.60.46:8849
group: SEATA_GROUP
registry: # 会去nacos中拉去seata-server服务
type: nacos
nacos:
application: seata-server
server-addr: 192.168.60.46:8849
group: SEATA_GROUP
seata1.0.0之后config文件下就移除了nacos-config.txt等文件,改为了config.txt需要手动下载,并且config.txt需要在nacos-config.sh的上一级目录下才能推送到nacos中
# 只需要修改下面几种配置即可,这里是配置客户端需要拉取的配置文件
service.vgroupMapping.自定义的名称=default
store.mode=db #修改为db
store.db.dbType=mysql #修改msql的连接方式账号和密码
RM:资源管理者/事务参与者,也可以是一个TM
TM:事务管理者,也是一个微服务,充当分布式事务的发起者
TC:全局事务协调者seata-server,一个包需要搭建,TC来决定事务的回滚和提交
两阶段提交:只执行,不提交
seata 核心概念:边执行,边提交(两阶段的变种)
阶段一:业务SQL:update product set name = ‘GTS’ where name = ‘TXC’
业务数据和回滚日志记录会在同一个本地事务中保存,会释放本地锁和连接资源
阶段二(回滚):
通过一阶段的回滚日志进行反向补偿
阶段二(提交):
一阶段提交本地事务,必须需要拿到更改数据的全局锁,拿不到全局锁,不能提交本地事务,超出等待时间,会回滚本地事务,释放本地锁
例:tx1和tx2两个全局事务同时修改 a表的m字段,m初始为1000;
tx1先开始,拿到本地锁,将m 1000-100 = 900。本地事务提交前,先拿到该记录的全局锁,本地提交释放本地锁。tx2开始,拿到本地锁,将m 900-100=800,提交本地事务前,先获取该记录的全局锁,tx1全局事务提交前,全局锁会被tx1所持有,tx2就会重试等待全局锁。
tx1二阶段全局提交,释放全局锁。tx2拿到全局锁提交本地事务。如果tx1二阶段为全局回滚,那么会重新重试获取本地锁,此时tx2如果还在等待全局锁,同时持有本地锁,tx1分支事务就会等待tx2超时释放本地锁之后,再次获取本地锁;整个过程 全局锁都是被 tx1锁持有,不会存在脏数据的问题
Seata AT模式的默认全局隔离级别是读未提交,如果在特定场景下,必需要求全局的读已提交,Seata采用通过select for update 语句来进行代理的;select for update语句的执行会申请全局锁 ,如果全局锁被其它事务锁持有,就会回滚select for update的本地执行并且重试,因为这时候查询是被锁住,直到全局锁拿到,即读取相关的数据是已提交的
AT模式是基于本地支持ACID事务的关系型数据库:
相应的TCC模式,不依赖本地底层数据的事务支持: