springcloud之seata在微服務模塊全局異常捕捉后導致事務不滾優(yōu)雅方案解決
全網獨一份,原創(chuàng),更多好文,請關注微信公眾號,干貨你觸手可及
seata幾個核心角色如下:
事務協(xié)調者(TC) :管理全局的分支事務的狀態(tài),用于全局性事務的提交和
回 滾。
事務管理者(TM) :用于開啟、提交或回滾事務。
資源管理器(RM) :用于分支事務上的資源管理,向 TC 注冊分支事務,上報
分支事務的狀態(tài),接收 TC 的命令來提交或者回滾分支事務
異常捕獲了,就相當你把事務try/catch了,seata的原理是入全局事務入口
(加了 @GlobalTransactional )方法,TM 向 TC 申請開啟一個全局事務,全局事務創(chuàng)建成功并生成一個全局唯一的 XID,XID 在微服務調用鏈路的上下文中傳播,在各自的微服務中RM會判斷請求中是否有XID,有就開啟一個分支事務
根據分支事務的情況,TM 向 TC 發(fā)起針對 XID 的全局提交或回滾決議,
TC 調度 XID 下管轄的全部分支事務完成提交或回滾請求。
下面分析事務捕獲,也就是說,分支事務沒異常,則事務提交,不會回滾。
微服務中全局異常,會使得分支事務httpResponse中的status狀態(tài) = 200,TM就是認為事務是正常的,執(zhí)行提交哦,
這里解決方案就是在全局異常處理中,判斷下是否開啟了seata的全局事務,如果開啟,則設置httpResponse中的status狀態(tài) = 500,這樣TM就是認為事務是異常了,拋出TmTransactionException異常,事務就可以回滾了
解決方案代碼
這個是全局異常在一個方法中進行處理的
下面是大家習慣了的異常處理習慣,代碼如下,原理都一樣
/**
* 描述: 全局異處理器 <br>
* 時間: 2020-06-07 16:32 <br>
* 作者:IT學習道場
*/
@ResponseBody
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler({Exception.class})
public AjaxVo<?> handleException(Exception e, HttpServletResponse response) {
//捕捉到的異常如果是自定義異常類,那么就返回自定義異常類中的錯誤碼和錯誤信息
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() + "請求方法";
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());
}
/**
* 如果開啟分布式事務,就設置response.status = 500,seata的tm(事務管理器)
* 就是感知到 TmTransactionException異常,發(fā)起事務回滾
*/
private void setRespErrStatus(HttpServletResponse response){
//如果開啟分布式事務,設置錯誤狀態(tài)碼,讓事務回滾
if (StringUtils.isNotBlank(RootContext.getXID())) {
response.setStatus(HttpCode.SERVER_ERROR.value());
}else {
response.setStatus(HttpCode.OK.value());
}
}
}
AjaxVo 是我們系統(tǒng)的全局響應統(tǒng)一數據結構
你get了嗎?這么好,這么優(yōu)質的公眾號,關注下,你將收獲滿滿
作者:IT學習道場
歡迎關注微信公眾號 : IT學習道場