環(huán)境版本:
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以上
一.簡單調(diào)用
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.報錯前
分支事務表
全局事務表
全局鎖
undo_log
undo_log中的rollback_info里面存儲了對象的前后數(shù)據(jù)信息,實踐的時候可以看看
1.3.報錯或者超時(默認6s)的時候
1.正骋羯簦回滾,并刪除3張鎖表的當前事務信息,業(yè)務數(shù)據(jù)回滾(利用undo_log存儲的rollback_info進行反向sql),且異步執(zhí)行
2.臟數(shù)據(jù)回滾不成功,需要人工干預
3.undo_log日志表刪除
1.4.客戶端打印信息
控制臺信息
上面的信息為業(yè)務sql插入
1.打印出了全局事務id(xid),分支id(branchId),seata的模式(branchType),資源編碼(resourceId)
2.undo_log日志刪除刷新
3.AT模式的二階段提交完成
4.回滾狀態(tài):已回滾
二.多服務調(diào)用
需要增加一個 攔截器來傳遞上下服務之間的全局事務id (xid)
2.1.實現(xiàn)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.實現(xiàn) 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)) {
// 構(gòu)建請求頭
requestWrapper.getHeaders().add("TX_XID", xid);
}
return clientHttpRequestExecution.execute(requestWrapper, bytes);
}
}
2.3.實現(xiàn)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.業(yè)務編寫,這里調(diào)用采用的是openFeign
@PostMapping(value = "test-seata-ll")
@ApiModelProperty(value = "seata AT模式 多服務調(diào)用回滾")
@GlobalTransactional(rollbackFor = Exception.class)
public void testSeataLl(@RequestBody @ApiParam HsSaveReq hsSave) throws Exception {
// 模擬 A調(diào)用B,C
// A,B成功,C失敗情況,看看回滾
hsService.saveHs(hsSave);
// 刪除一個存在的記錄
ResultData<Boolean> B = testProxy.deletePlatformOrgan("1");
// 刪除一個不存在的記錄
ResultData<Boolean> C = testProxy.deletePlatformRole("1");
//可以根據(jù)自己的業(yè)務進行判斷
if (!"200".equals(B.getCode()) || !"200".equals(C.getCode())) {
throw new Exception("回滾");
}
}
也可以看看seata庫中表的情況和undo_log表的情況,也是一致的