springcloud之seata在微服務(wù)模塊全局異常捕捉后導(dǎo)致事務(wù)不滾優(yōu)雅方案解決
全網(wǎng)獨(dú)一份,原創(chuàng),更多好文,請(qǐng)關(guān)注微信公眾號(hào),干貨你觸手可及
seata幾個(gè)核心角色如下:
事務(wù)協(xié)調(diào)者(TC) :管理全局的分支事務(wù)的狀態(tài),用于全局性事務(wù)的提交和
回 滾。
事務(wù)管理者(TM) :用于開啟、提交或回滾事務(wù)。
資源管理器(RM) :用于分支事務(wù)上的資源管理,向 TC 注冊(cè)分支事務(wù),上報(bào)
分支事務(wù)的狀態(tài),接收 TC 的命令來(lái)提交或者回滾分支事務(wù)
異常捕獲了,就相當(dāng)你把事務(wù)try/catch了,seata的原理是入全局事務(wù)入口
(加了 @GlobalTransactional )方法,TM 向 TC 申請(qǐng)開啟一個(gè)全局事務(wù),全局事務(wù)創(chuàng)建成功并生成一個(gè)全局唯一的 XID,XID 在微服務(wù)調(diào)用鏈路的上下文中傳播,在各自的微服務(wù)中RM會(huì)判斷請(qǐng)求中是否有XID,有就開啟一個(gè)分支事務(wù)
根據(jù)分支事務(wù)的情況,TM 向 TC 發(fā)起針對(duì) XID 的全局提交或回滾決議,
TC 調(diào)度 XID 下管轄的全部分支事務(wù)完成提交或回滾請(qǐng)求。
下面分析事務(wù)捕獲,也就是說,分支事務(wù)沒異常,則事務(wù)提交,不會(huì)回滾。
微服務(wù)中全局異常,會(huì)使得分支事務(wù)httpResponse中的status狀態(tài) = 200,TM就是認(rèn)為事務(wù)是正常的,執(zhí)行提交哦,
這里解決方案就是在全局異常處理中,判斷下是否開啟了seata的全局事務(wù),如果開啟,則設(shè)置httpResponse中的status狀態(tài) = 500,這樣TM就是認(rèn)為事務(wù)是異常了,拋出TmTransactionException異常,事務(wù)就可以回滾了
解決方案代碼
這個(gè)是全局異常在一個(gè)方法中進(jìn)行處理的
下面是大家習(xí)慣了的異常處理習(xí)慣,代碼如下,原理都一樣
/**
* 描述: 全局異處理器 <br>
* 時(shí)間: 2020-06-07 16:32 <br>
* 作者:IT學(xué)習(xí)道場(chǎng)
*/
@ResponseBody
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler({Exception.class})
public AjaxVo<?> handleException(Exception e, HttpServletResponse response) {
//捕捉到的異常如果是自定義異常類,那么就返回自定義異常類中的錯(cuò)誤碼和錯(cuò)誤信息
String stackExceptionMsg = ExceptionUtil.stacktraceToString(e);
//異常輸出到日志
log.error(stackExceptionMsg);
setRespErrStatus(response);
return AjaxVo.fail(message);
}
@ExceptionHandler({TokenException.class})
public AjaxVo<?> handleTokenException(TokenException e, HttpServletResponse response) {
setRespErrStatus(response);
return AjaxVo.fail(e.getCode(), e.getMessage());
}
@ExceptionHandler({BaseException.class})
public AjaxVo<?> handleBaseException(BaseException e, HttpServletResponse response) {
setRespErrStatus(response);
return AjaxVo.fail(e.getCode(), e.getMessage());
}
@ExceptionHandler({BindException.class})
public AjaxVo<?> handleBindException(BindException e, HttpServletResponse response) {
StringBuilder message = new StringBuilder();
List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
Iterator var4 = fieldErrors.iterator();
while (var4.hasNext()) {
FieldError error = (FieldError) var4.next();
message.append(error.getField()).append(error.getDefaultMessage()).append(",");
}
message = new StringBuilder(message.substring(0, message.length() - 1));
log.error(message.toString());
setRespErrStatus(response);
return AjaxVo.fail(message.toString());
}
@ExceptionHandler({MethodArgumentNotValidException.class})
public AjaxVo<?> handlerMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletResponse response) {
StringBuilder message = new StringBuilder();
Iterator var3 = e.getBindingResult().getFieldErrors().iterator();
while (var3.hasNext()) {
FieldError error = (FieldError) var3.next();
message.append(error.getField()).append(error.getDefaultMessage()).append(",");
}
message = new StringBuilder(message.substring(0, message.length() - 1));
log.error(message.toString());
setRespErrStatus(response);
return AjaxVo.fail(message.toString());
}
@ExceptionHandler({HttpMediaTypeNotSupportedException.class})
public AjaxVo<?> handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e, HttpServletResponse response) {
String message = "該方法不支持" + e.getMessage() + "媒體類型";
log.error(message);
setRespErrStatus(response);
return AjaxVo.fail(message);
}
@ExceptionHandler({HttpRequestMethodNotSupportedException.class})
public AjaxVo<?> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e, HttpServletResponse response) {
String message = "該方法不支持" + e.getMessage() + "請(qǐng)求方法";
log.error(message);
setRespErrStatus(response);
return AjaxVo.fail(message);
}
@ExceptionHandler({IOException.class})
public AjaxVo<?> handleIOException(IOException e, HttpServletResponse response) {
log.error(ExceptionUtil.stacktraceToString(e));
setRespErrStatus(response);
return AjaxVo.fail(e.getMessage());
}
@ExceptionHandler({IllegalArgumentException.class})
public AjaxVo<?> handleIllegalArgumentException(IllegalArgumentException e, HttpServletResponse response) {
log.error(ExceptionUtil.stacktraceToString(e));
setRespErrStatus(response);
return AjaxVo.fail(HttpStatus.BAD_REQUEST.value(), e.getMessage());
}
@ExceptionHandler({NullPointerException.class})
public AjaxVo<?> handleNullPointerException(NullPointerException e, HttpServletResponse response) {
log.error(ExceptionUtil.stacktraceToString(e));
setRespErrStatus(response);
return AjaxVo.fail(HttpStatus.BAD_REQUEST.value(), MscmExceptionEnum.NULL_POINT_EXCEPTION.getMessage());
}
@ExceptionHandler({BusinessException.class})
public AjaxVo<?> handleBusinessException(BusinessException e, HttpServletResponse response) {
log.error(ExceptionUtil.stacktraceToString(e));
setRespErrStatus(response);
return AjaxVo.fail(HttpStatus.NOT_ACCEPTABLE.value(), e.getMessage());
}
@ExceptionHandler({ServiceException.class})
public AjaxVo<?> handleServiceException(ServiceException e, HttpServletResponse response) {
log.error(ExceptionUtil.stacktraceToString(e));
setRespErrStatus(response);
Result<Object> result = e.getResult();
if (Objects.nonNull(result)) {
return AjaxVo.fail(result.getCode(), e.getMessage());
}
return AjaxVo.fail(e.getMessage());
}
/**
* 如果開啟分布式事務(wù),就設(shè)置response.status = 500,seata的tm(事務(wù)管理器)
* 就是感知到 TmTransactionException異常,發(fā)起事務(wù)回滾
*/
private void setRespErrStatus(HttpServletResponse response){
//如果開啟分布式事務(wù),設(shè)置錯(cuò)誤狀態(tài)碼,讓事務(wù)回滾
if (StringUtils.isNotBlank(RootContext.getXID())) {
response.setStatus(HttpCode.SERVER_ERROR.value());
}else {
response.setStatus(HttpCode.OK.value());
}
}
}
AjaxVo 是我們系統(tǒng)的全局響應(yīng)統(tǒng)一數(shù)據(jù)結(jié)構(gòu)
你get了嗎?這么好,這么優(yōu)質(zhì)的公眾號(hào),關(guān)注下,你將收獲滿滿
作者:IT學(xué)習(xí)道場(chǎng)
歡迎關(guān)注微信公眾號(hào) : IT學(xué)習(xí)道場(chǎng)