环境版本:
jdk1.8
maven 3.6
spring-cloud Hoxton.RELEASE
spring-cloud-alibaba 2.1.1.RELEASE
seata-service 0.9
dynamic-datasource-spring-boot-starter 3.0以上
一.简单调用
1.1.在全局事务的开始方法加入 @GlobalTransactional 即可
模拟一个报错的代码,在第五行断点,看看各个表的情况
@PostMapping(value = "test-seata")
@GlobalTransactional(rollbackFor = Exception.class)
public void testSeata(@RequestBody @ApiParam HsSaveReq hsSave) throws InterruptedException {
hsService.saveHs(hsSave);
int i = 1 / 0;
log.info("成功");
}
1.2.报错前
![](https://img.haomeiwen.com/i11575511/86716c69b77d59e2.png)
![](https://img.haomeiwen.com/i11575511/3758421494c8c814.png)
![](https://img.haomeiwen.com/i11575511/921d19863196b591.png)
![](https://img.haomeiwen.com/i11575511/1eb1933ece285b0f.png)
undo_log中的rollback_info里面存储了对象的前后数据信息,实践的时候可以看看
1.3.报错或者超时(默认6s)的时候
1.正常回滚,并删除3张锁表的当前事务信息,业务数据回滚(利用undo_log存储的rollback_info进行反向sql),且异步执行
2.脏数据回滚不成功,需要人工干预
3.undo_log日志表删除
1.4.客户端打印信息
![](https://img.haomeiwen.com/i11575511/c00c712e7a220259.png)
上面的信息为业务sql插入
1.打印出了全局事务id(xid),分支id(branchId),seata的模式(branchType),资源编码(resourceId)
2.undo_log日志删除刷新
3.AT模式的二阶段提交完成
4.回滚状态:已回滚
二.多服务调用
需要增加一个 拦截器来传递上下服务之间的全局事务id (xid)
2.1.实现WebMvcConfigurer ,重写 addInterceptors
@Configuration
public class SeataHandlerInterceptorConfiguration implements WebMvcConfigurer {
public SeataHandlerInterceptorConfiguration() {
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册HandlerInterceptor,拦截所有请求
registry.addInterceptor(new SeataHandlerInterceptor()).addPathPatterns(new String[] { "/**" });
}
}
2.2.实现 ClientHttpRequestInterceptor
public class SeataRestTemplateInterceptor implements ClientHttpRequestInterceptor {
public SeataRestTemplateInterceptor() {
}
@Override
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes,
ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
HttpRequestWrapper requestWrapper = new HttpRequestWrapper(httpRequest);
String xid = RootContext.getXID();
if (StringUtils.isNotEmpty(xid)) {
// 构建请求头
requestWrapper.getHeaders().add("TX_XID", xid);
}
return clientHttpRequestExecution.execute(requestWrapper, bytes);
}
}
2.3.实现HandlerInterceptor,重写 preHandle和afterCompletion
@Slf4j
@Configuration
public class SeataHandlerInterceptor implements HandlerInterceptor {
public SeataHandlerInterceptor() {
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String xid = RootContext.getXID();
String rpcXid = request.getHeader("TX_XID");
// 获取全局事务编号
if (log.isDebugEnabled()) {
log.debug("xid in RootContext {} xid in RpcContext {}", xid, rpcXid);
}
if (xid == null && rpcXid != null) {
// 设置全局事务编号
RootContext.bind(rpcXid);
if (log.isDebugEnabled()) {
log.debug("bind {} to RootContext", rpcXid);
}
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) {
String rpcXid = request.getHeader("TX_XID");
if (StringUtils.isNotEmpty(rpcXid)) {
String unbindXid = RootContext.unbind();
if (log.isDebugEnabled()) {
log.debug("unbind {} from RootContext", unbindXid);
}
if (!rpcXid.equalsIgnoreCase(unbindXid)) {
log.warn("xid in change during RPC from {} to {}", rpcXid, unbindXid);
if (unbindXid != null) {
RootContext.bind(unbindXid);
log.warn("bind {} back to RootContext", unbindXid);
}
}
}
}
}
2.4.业务编写,这里调用采用的是openFeign
@PostMapping(value = "test-seata-ll")
@ApiModelProperty(value = "seata AT模式 多服务调用回滚")
@GlobalTransactional(rollbackFor = Exception.class)
public void testSeataLl(@RequestBody @ApiParam HsSaveReq hsSave) throws Exception {
// 模拟 A调用B,C
// A,B成功,C失败情况,看看回滚
hsService.saveHs(hsSave);
// 删除一个存在的记录
ResultData<Boolean> B = testProxy.deletePlatformOrgan("1");
// 删除一个不存在的记录
ResultData<Boolean> C = testProxy.deletePlatformRole("1");
//可以根据自己的业务进行判断
if (!"200".equals(B.getCode()) || !"200".equals(C.getCode())) {
throw new Exception("回滚");
}
}
也可以看看seata库中表的情况和undo_log表的情况,也是一致的
网友评论