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學習道場