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)