springcloud之seata在微服务模块全局异常捕捉后导致事务不滚优雅
作者 | IT学习道场
来源 | IT学习道场(ID:itlearndojo)
seata几个核心角色如下:事务协调者(TC) :管理全局的分支事务的状态,用于全局性事务的提交和
事务管理者(TM) :用于开启、提交或回滚事务。
资源管理器(RM) :用于分支事务上的资源管理,向 TC 注册分支事务,上报
分支事务的状态,接收 TC 的命令来提交或者回滚分支事务
异常捕获了,就相当你把事务try/catch了,seata的原理是入全局事务入口
(加了 @GlobalTransactional )方法,TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID,XID 在微服务调用链路的上下文中传播,在各自的微服务中RM会判断请求中是否有XID,有就开启一个分支事务
根据分支事务的情况,TM 向 TC 发起针对 XID 的全局提交或回滚决议,
TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。
下面分析事务捕获,也就是说,分支事务没异常,则事务提交,不会回滚。
微服务中全局异常,会使得分支事务httpResponse中的status状态 = 200,TM就是认为事务是正常的,执行提交哦,
这里解决方案就是在全局异常处理中,判断下是否开启了seata的全局事务,如果开启,则设置httpResponse中的status状态 = 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异常,发起事务回滚 private void setRespErrStatus(HttpServletResponse response){ //如果开启分布式事务,设置错误状态码,让事务回滚 if (StringUtils.isNotBlank(RootContext.getXID())) { response.setStatus(HttpCode.SERVER_ERROR.value()); }else { response.setStatus(HttpCode.OK.value());AjaxVo 是我们系统的全局响应统一数据结构
你get了吗?
-
在 Java 中创建 微服务jowvid • 4215浏览 • 0回复
-
在 鸿蒙(HarmonyOS)环境下, 优雅 的完成Http访问网络【教程】ZZR老师 • 2.8w浏览 • 21回复
-
docker-compose 构建 springcloud 微服务 项目footballboy • 7668浏览 • 0回复
-
GPS 模块 分析 之 on函数挖墙脚的农民工 • 6312浏览 • 3回复
-
微服务 Spring Cloud Alibaba分布式 事务 解决框架 Seata 概念入门篇ImCrow • 2830浏览 • 0回复
-
微服务 SpringCloud 整合 Seata 解决分布式 事务ImCrow • 4340浏览 • 0回复
-
「 SpringCloud 」使用注解校验 微服务 消息参数ponymar • 4267浏览 • 0回复
-
「 SpringCloud 」Nacos发现、配置和管理 微服务ponymar • 2607浏览 • 0回复
-
「 SpringCloud 」集成OpenFeign用于 微服务 间调用ponymar • 3128浏览 • 0回复
-
「 SpringCloud 」集成Gateway实现 微服务 路由转发ponymar • 7618浏览 • 0回复
-
「 SpringCloud 」 微服务 集成Reids缓存ponymar • 2147浏览 • 0回复
-
「 SpringCloud 」 微服务 数据权限的设计与实现ponymar • 1.0w浏览 • 0回复
-
微服务 之 :独立 服务reiallen • 2048浏览 • 0回复
-
全局 异常 捕获 导致 Micrometer错误数丢失peng_hui • 1792浏览 • 0回复
-
微服务 7:通信 之 RPCtop_tony • 1764浏览 • 0回复
-
微服务 6:通信 之 网关top_tony • 1466浏览 • 0回复
-
微服务 架构:Nacos本地缓存 PK 微服务 优雅 下线梦叶草789 • 1918浏览 • 0回复
-
redis 在 微服务 领域的贡献荔枝岛岛主 • 831浏览 • 0回复
-
Go 微服务 实战 之 如何使用 go-micro 写 微服务 应用宇宙之一粟 • 5146浏览 • 4回复