多数据源配置(baomidou多数据源)中Seata的AT模式使用

项目application.yml配置

seata:
  enabled: true
  application-id: tmccloud-server-biz #应用名称,一般配置为与 ${spring.application.name}相同即可
  enable-auto-data-source-proxy: false #使用了baomidou多数据源则配置为false,单数据源则配置为true
  tx-service-group: default_tx_group
  service:
    vgroup-mapping:
      default_tx_group: default
  registry:
    type: nacos  #注册中心 nacos
    nacos:
      application: seata-server
      server-addr: localhost:8848
      namespace: seata
      group: DEFAULT_GROUP
      username: nacos
      password: nacos
  config:
    type: nacos # 配置中心 nacos
    nacos:
      server-addr: localhost:8848
      namespace: seata
      group: DEFAULT_GROUP
      data-id: seataServer.properties
      username: nacos
      password: nacos

多数据源配置(spring.datasource.dynamic)

spring: 
   datasource:
    #配置hikari连接池
    hikari:
      minimum-idle: 5 #最小连接数
      maximum-pool-size: 10  #池中最大连接数
      connection-timeout: 20000 #连接超时时间
      idle-timeout: 30000 # 空闲等待时间 ms
      max-lifetime: 1800000 #30分钟
    #动态数据源配置
    dynamic:
      primary: business
      strict: true   #接入seata需要此参数
      seata: true    #接入seata需要此参数
      seata-mode: AT #接入seata需要此参数

baomidou多数据源引入pom依赖

<!-- 实现对dynamic-datasource的自动化配置, 版本需升级到3.5.1及以上  -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>

配置seata的XID请求头拦截器

import feign.RequestInterceptor;
import feign.RequestTemplate;
import io.seata.core.context.RootContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
 
@Slf4j
@Component
public class SeataFeignRequestInterceptor implements RequestInterceptor {
 
    @Override
    public void apply(RequestTemplate template) {
        String xid = RootContext.getXID();
        if (StringUtils.isNotBlank(xid)) {
            template.header(RootContext.KEY_XID, xid);
            log.debug("Seata XID {} added to Feign request header", xid);
        }
    }
}

 

接入seata的客户端服务的数据库表中,需要新增undo_log日志表

CREATE TABLE `undo_log` (
  `branch_id` bigint NOT NULL COMMENT 'branch transaction id',
  `xid` varchar(128) NOT NULL COMMENT 'global transaction id',
  `context` varchar(128) NOT NULL COMMENT 'undo_log context,such as serialization',
  `rollback_info` longblob NOT NULL COMMENT 'rollback info',
  `log_status` int NOT NULL COMMENT '0:normal status,1:defense status',
  `log_created` datetime(6) NOT NULL COMMENT 'create datetime',
  `log_modified` datetime(6) NOT NULL COMMENT 'modify datetime',
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='AT transaction mode undo table'

全局异常处理器改造

package com.riskmage.rmccloud.baseserver.web.exception;
 
 
import com.riskmage.rmcbase.BusinessException;
import com.riskmage.rmcbase.Resp;
import com.riskmage.rmccloud.baseserver.base.exception.DistributedTransactionException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
 
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
 
/**
 * 全局异常处理器
 */
@ControllerAdvice
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
 
    /**
     * JSR303 异常
     * @param e
     * @param request
     * @return
     */
    @ExceptionHandler({BindException.class})
    public Resp<?> handleBindException(BindException e, HttpServletRequest request) {
        log.error(e.getMessage());//记录完整错误信息
        List<ObjectError> errors = e.getBindingResult().getAllErrors();
        ObjectError err= errors.get(0);
        String msg = err.getDefaultMessage();
        return  Resp.failure("999999",msg);
    }
    /**
     * JSR303 异常
     * @param e
     * @param request
     * @return
     */
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public Resp<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
        log.error(e.getMessage());//记录完整错误信息
        List<ObjectError> errors = e.getBindingResult().getAllErrors();
        ObjectError err= errors.get(0);
        String msg = err.getDefaultMessage();
        return  Resp.failure("999999",msg);
    }
    /**
     * JSR303 异常
     * @param e
     * @param request
     * @return
     */
    @ExceptionHandler({ConstraintViolationException.class})
    public Resp<?> handleConstraintViolationExceptionException(ConstraintViolationException e, HttpServletRequest request) {
        log.error(e.getMessage());
        ConstraintViolation c  = (ConstraintViolation)e.getConstraintViolations().toArray()[0];
        String msg =c.getMessage();
        return  Resp.failure("999999",msg);
    }
 
    /**
     * BusinessException 自定义业务异常处理
     * @param e
     * @param request
     * @return
     */
    @ExceptionHandler({BusinessException.class})
    public Resp<?> handleBusinessException(BusinessException e, HttpServletRequest request) {
        log.error(e.getMsg());
        // 检查是否在分布式事务上下文中
        String xid = request.getHeader("TX_XID");
        if (StringUtils.isNotEmpty(xid)) {
            log.error("handleBusinessException得到TX_XID:{}",xid);
            throw new DistributedTransactionException(e.getMsg(), e);
        }
        return new Resp<>(e.getCode(), e.getMsg(), e.getData());
    }
 
    /**
     * Exception 异常
     * @param e
     * @param request
     * @return
     */
    @ExceptionHandler({Exception.class})
    public Resp<?> handleException(Exception e, HttpServletRequest request) {
        log.error(e.getMessage(),e);
 
        // 检查是否在分布式事务上下文中
        String xid = request.getHeader("TX_XID");
        if (StringUtils.isNotEmpty(xid)) {
            log.error("捕获到分布式事务中的异常:{}",e.getMessage());
            throw new DistributedTransactionException(e);
        }
        if(e instanceof HttpRequestMethodNotSupportedException){
           return Resp.failure("999998","客户端http请求方式有误,请检查!");
        }
        return Resp.failure("999997","抱歉,服务器繁忙请稍候再试!");
    }
 
    // 处理分布式事务相关异常 - 不捕获,直接抛出
    @ExceptionHandler(DistributedTransactionException.class)
    public void handleDistributedTransactionException(DistributedTransactionException e) throws DistributedTransactionException {
        log.error("分布式事务异常: {}", e.getMessage());
        throw e;
    }
}

新增DistributedTransactionException 自定义分布式事务异常类

 
/**
 * 自定义异常 (分布式事务异常)
 */
public class DistributedTransactionException extends RuntimeException {
    public DistributedTransactionException() {
        super();
    }
 
    public DistributedTransactionException(String message) {
        super(message);
    }
 
    public DistributedTransactionException(String message, Throwable cause) {
        super(message, cause);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值