2LCN模式 · SpringCloud微服务实战 · 看云

导航

本节代码地址


1. 原理介绍:

LCN模式是通过代理Connection的方式实现对本地事务的操作,然后在由TxManager统一协调控制事务。当本地事务提交回滚或者关闭连接时将会执行假操作,该代理的连接将由LCN连接池管理。

2. 模式特点:

  • 该模式对代码的嵌入性为低。
  • 该模式仅限于本地存在连接对象且可通过连接对象控制事务的模块。
  • 该模式下的事务提交与回滚是由本地事务方控制,对于数据一致性上有较高的保障。
  • 该模式缺陷在于代理的连接需要随事务发起方一共释放连接,增加了连接占用的时间。

3. 环境准备

中间件 版本
MySQL(可以参照容器化章节) 5.7+
Redis(可以参照容器化章节) 4.0+

3.1 数据库准备

新建fw-txmanager数据库,并且执行以下脚本创建异常表,表名不能变。

CREATE TABLE `t_tx_exception` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `group_id` varchar(64) DEFAULT NULL,
  `unit_id` varchar(32) DEFAULT NULL,
  `mod_id` varchar(128) DEFAULT NULL,
  `transaction_state` tinyint(4) DEFAULT NULL,
  `registrar` tinyint(4) DEFAULT NULL,
  `remark` varchar(4096) DEFAULT NULL,
  `ex_state` tinyint(4) DEFAULT NULL COMMENT '0 未解决 1已解决',
  `create_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='tx-manager异常表'

4. TxManager 搭建

LCN模式的控制是有TxManager同意控制事务,因此首先需要将TxManager搭建起来。

4.1 新建模块

新建 fw-cloud-transaction-lcn-txmanager 用来构建TxManager

4.2 maven 配置

这里我们需要引入txlcn-tm的包,因为在主项目种控制了依赖版本,因此这里不用输入版本号,实际版本号是5.0.2.RELEASE

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- tx-lcn manager依赖 -->
    <dependency>
        <groupId>com.codingapi.txlcn</groupId>
        <artifactId>txlcn-tm</artifactId>
    </dependency>
</dependencies>

4.3 应用配置

我们这里需要配置TxManager的数据库地址,配置TxManager 的Redis 信息,因为TX-LCN非常依赖Redis,因此生产建议配置Redis Cluster。
除了以上两个至关重要的配置,我们还可以设置TxManager 的登录密码,Socket 端口,日志记录等。

spring.application.name=TransactionManager
server.port=7970
#datasource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://${dbIp}:3306/fw_txmanager?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2b8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.hikari.connection-timeout=20000
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.max-lifetime=1200000
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.maximum-pool-size=12
spring.datasource.hikari.idle-timeout=300000

# 数据库方言
hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect


# 第一次运行可以设置为: create, 为TM创建持久化数据库表
#spring.jpa.hibernate.ddl-auto=validate

#redis
spring.redis.database=0
spring.redis.host= ${redisIp}
spring.redis.port= 6379
spring.redis.password= 123456
#tx-lcn
# TM监听Socket端口. 默认为 ${server.port} - 100
tx-lcn.manager.port=8888
# TM后台登陆密码,默认值为codingapi
tx-lcn.manager.admin-key=123456

# TM监听IP. 默认为 127.0.0.1
tx-lcn.manager.host=127.0.0.1

# 心跳检测时间(ms). 默认为 300000
tx-lcn.manager.heart-time=300000

# 分布式事务执行总时间(ms). 默认为36000
tx-lcn.manager.dtx-time=8000

# 参数延迟删除时间单位ms  默认为dtx-time值
tx-lcn.message.netty.attr-delay-time=${tx-lcn.manager.dtx-time}

# 事务处理并发等级. 默认为机器逻辑核心数5倍
tx-lcn.manager.concurrent-level=160

# 分布式事务锁超时时间 默认为-1,当-1时会用tx-lcn.manager.dtx-time的时间
tx-lcn.manager.dtx-lock-time=${tx-lcn.manager.dtx-time}

# 雪花算法的sequence位长度,默认为12位.
tx-lcn.manager.seq-len=12

# 异常回调开关。开启时请制定ex-url
tx-lcn.manager.ex-url-enabled=false

# 事务异常通知(任何http协议地址。未指定协议时,为TM提供内置功能接口)。默认是邮件通知
tx-lcn.manager.ex-url=/provider/email-to

4.4 配置启动类

需要在启动类上加上@EnableTransactionManagerServer注解,表示开启TxManager

@SpringBootApplication
@EnableTransactionManagerServer
public class FwTxManagerApplication {
    public static void main(String[] args) {
        SpringApplication.run(FwTxManagerApplication.class, args);
    }
}

4.5 项目启动

浏览器输入http://localhost:7970/
621b643197dd1e4143c3236be52e56b1_MD5.png

密码就是我们在配置文件中设置的tx-lcn.manager.admin-key
5c896bde78fc76c264892d0bb65a4fcb_MD5.png

4.6 后台使用说明

4.6.1 首页信息

主要是TxManager的配置信息

4.6.2 异常记录

事务ID:事务组标示

事务单元ID:参与事务单元标示

TxClient标示:模块标示

异常情况:【未知】【TxManager通知事务】【TxClient查询事务状态】【事务发起方通知事务组】, 这几种异常情况

异常状态:解决和未解决。对于系统未作出补偿的异常记录,需要系统管理员 【操作】查看当场信息,做出手动补偿

时间:发生时间

操作:查看异常时信息

4.6.3 系统日志

  • 日志分类-TAG:
    • transaction: TxClient下达命令记录
    • manager: TxManager执行协调记录
  • 日志内容:
    • create group: TxClient创建事务组命令到达TxManager
    • start join group: TxClient 开始加入事务组
    • over join group: TxClient 成功加事务组
    • notify group 1: TxManager通知TxClient提交本第事务(0回滚)
    • notify unit exception: TxManager通知事务单元失败(此处会记录异常信息)
    • notify group over:通知事务组结束(对于个别不能通知到的单元,会记下异常记录,不影响其它通知,异常的等待补偿)

5. 新建LCN 模块

本模块中我们将新建两个模块,我们将模拟电商购物环节中的下单->发货的过程,两个服务通过Feign进行远程调用。两个模块的名称分别为fw-cloud-transaction-lcn-lcn-orderfw-cloud-transaction-lcn-lcn-send

5.1 maven 依赖

两个模块所使用的依赖也是相同的,这里可以看到,我们依赖了上面封装的fw-cloud-transaction-base-dao,不需要再自己写基本的mapper等,因为是基于Eureka 服务发现和Feign 远程调用的,因此引入了响应的包。其中最重要的就是引入txlcn-tctxlcn-txmsg-netty包,否者不能实现和TxManager的通信及事务控制。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- base-dao -->
    <dependency>
        <groupId>com.yisu.cloud</groupId>
        <artifactId>fw-cloud-transaction-base-dao</artifactId>
        <version>${version}</version>
    </dependency>
    <!-- TC依赖 -->
    <dependency>
        <groupId>com.codingapi.txlcn</groupId>
        <artifactId>txlcn-tc</artifactId>
    </dependency>
    <!-- TC和TM通信依赖于Netty -->
    <dependency>
        <groupId>com.codingapi.txlcn</groupId>
        <artifactId>txlcn-txmsg-netty</artifactId>
    </dependency>
    <!--服务发现-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!--feign-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <!-- 数据连接池-->
    <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>HikariCP</artifactId>
        <version>${HikariCP.version}</version>
    </dependency>
</dependencies>

并在MySql中新建fw_transaction库,并执行以下脚本,供fw-cloud-transaction-lcn-lcn-orderfw-cloud-transaction-lcn-lcn-send使用

CREATE TABLE `fw_trade_log` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `status` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '订单状态 1.待支付 2.待发货 3.待收货 4.订单完成 5.订单关闭',
  `status_dsc` varchar(100) CHARACTER SET utf8 DEFAULT '' COMMENT '状态描述',
  `product_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '商品id',
  `product_name` varchar(100) NOT NULL DEFAULT '' COMMENT '商品名称',
  `user_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '用户id',
  `order_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '订单id',
  `order_amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '订单总额',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表'

5.2 fw-cloud-transaction-lcn-lcn-order模块

5.2.1 订单接口创建

再订单模块中创建一个订单接口,并在接口中设置支付成功的方法


public interface OrderService{

    
    void saveAndPayOrder(String productName);

}

5.2.2 订单接口实现

订单在支付成功以后会远程调用发货服务进行发货,需要在Service 上加上@LcnTransaction,开启LCN模式的分布式事务控制,并且设置订单的状态为待支付


@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Autowired
    private FwTradeLogService fwTradeLogService;

    @Autowired
    private RemoteSendServiceFeign remoteSendServiceFeign;

    @LcnTransaction
    @Override
    public void saveAndPayOrder(String productName) {

        FwTradeLog fwTradeLog =new FwTradeLog(StatusEnum.TWO);
        fwTradeLog.setProductId(System.currentTimeMillis());
        fwTradeLog.setProductName(productName);
        fwTradeLogService.save(fwTradeLog);
        log.info("[订单状态{}]=>{},当前商品id=>{},商品名称=>{}",fwTradeLog.getOrderId(), StatusEnum.TWO.getDesc(),fwTradeLog.getProductId(),fwTradeLog.getProductName());

        remoteSendServiceFeign.sendOrder(fwTradeLog);

    }

}

5.2.3 Feign 接口配置

这里我们配置远程调用的服务名称是fw-transaction-lcn-send,并且设置了fallback,并且指定了远程调用的方法是sendOrder。

@FeignClient(value = "fw-transaction-lcn-send", fallbackFactory = RemoteSendServiceFallback.class)
public interface RemoteSendServiceFeign {

    @PostMapping("send")
    void sendOrder(@RequestBody  FwTradeLog tradeLog);
}

5.2.4 Fallback 接口配置

在fallback输出异常日志

@Component
@Slf4j
public class RemoteSendServiceFallback implements FallbackFactory<RemoteSendServiceFeign> {

    @Override
    public RemoteSendServiceFeign create(Throwable throwable) {

        return tradeLog -> log.error("远程调用失败",throwable);
    }
}

5.2.5 Controller 控制层

配置订单服务对外的接口,方便演示


@RestController
public class OrderController {

    @Autowired
    private OrderService orderService;

    
    @GetMapping("saveOrder")
    public FwResult saveOrder(String productName){
         orderService.saveAndPayOrder(productName);
         return FwResult.ok();
    }


}

5.2.6 启动类配置

这里需要加上@EnableDistributedTransaction开启分布式的调用,加上@EnableFeignClients开启Feign的远程调用,加上@EnableDiscoveryClient开启服务的注册和发现。

@EnableDistributedTransaction
@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
public class FwTransactionLcnOrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(FwTransactionLcnOrderApplication.class, args);
    }


}

5.2.7 应用配置

配置中需要配置数据库和Redis的基本信息,以及TxManager的连接信息,并且设置了Eureka的连接信息和开启Feign的远程调用

server:
  port: 9002
spring:
  application:
    name: fw-transaction-lcn-order
  #数据库配置  start
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://${dbIp}:3306/fw_transaction?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
    hikari:
      connection-timeout: 20000
      auto-commit: true
      max-lifetime: 1200000
      minimum-idle: 5
      maximum-pool-size: 12
      idle-timeout: 300000
  redis:
    port: 6379
    host: ${redisIp}
    database: 0
    password: 123456
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
tx-lcn:
  client:
    manager-address: 127.0.0.1:8888
  logger:
    enabled: true
feign:
  hystrix:
    enabled: true
hystrix:
  shareSecurityContext: true #设置为true,这样做会自动配置Hystrix并发策略插件挂钩,将SecurityContext从主线程传输到Hystrix命令使用的线程

5.3 fw-cloud-transaction-lcn-lcn-send模块

5.3.1 发货接口创建

配置发货接口,并且创建发货的方法


public interface SendService {

    
    void sendOrder(FwTradeLog fwTradeLog);

}

5.3.2 发货接口实现

这里在实现方法上加上@LcnTransaction,并且设置订单的状态为待收货


@Service
@Slf4j
public class SendServiceImpl implements SendService {

    @Autowired
    private FwTradeLogService fwTradeLogService;

    @LcnTransaction
    @Override
    public void sendOrder(FwTradeLog fwTradeLog) {
        fwTradeLog.setStatus(StatusEnum.THREE.getValue());
        fwTradeLog.setStatusDsc(StatusEnum.THREE.getDesc());
        fwTradeLogService.save(fwTradeLog);
        log.info("[订单状态{}]=>{},当前商品id=>{},商品名称=>{}",fwTradeLog.getOrderId(), StatusEnum.THREE.getDesc(),fwTradeLog.getProductId(),fwTradeLog.getProductName());
    }
}

5.3.3 发货控制层实现

提供给订单模块调用的接口,注意方法名和请求方式要保持一致


@RestController
public class SendController {

    @Autowired
    private SendService sendService;

    
    @PostMapping("send")
    public void sendOrder(@RequestBody FwTradeLog tradeLog) {
         sendService.sendOrder(tradeLog);
    }
}

5.3.4 启动类

需要加上@EnableDistributedTransaction注解开启分布式事务,加上@EnableDiscoveryClient注解开启服务的注册与发现


@EnableDistributedTransaction
@SpringBootApplication
@EnableDiscoveryClient
public class FwTransactionLcnSendApplication {
    public static void main(String[] args) {
        SpringApplication.run(FwTransactionLcnSendApplication.class, args);
    }


}

5.3.5 应用配置

配置中需要配置数据库和Redis的基本信息,以及TxManager的连接信息,并且设置了Eureka的连接信息和开启Feign的远程调用

server:
  port: 9003
spring:
  application:
    name: fw-transaction-lcn-send
  #数据库配置  start
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://${dbIp}:3306/fw_transaction?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
    hikari:
      connection-timeout: 20000
      auto-commit: true
      max-lifetime: 1200000
      minimum-idle: 5
      maximum-pool-size: 12
      idle-timeout: 300000
  redis:
    port: 6379
    host: ${redisIp}
    database: 0
    password: 123456
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
tx-lcn:
  client:
    manager-address: 127.0.0.1:8888

5.4 启动服务

分别启动Eureka、fw-transaction-lcn-send、fw-transaction-lcn-order
227d357e6c5b36f3b17f7d035f6ab732_MD5.png

TxManager 可以看到两个事务注册上来
659040ec6b1d9d4ce1a41306a8873294_MD5.png

可以看到订单服务的日志如下

2020-04-05 21:48:22.292  INFO 3829 --- [nio-9002-exec-1] c.y.t.l.o.service.impl.OrderServiceImpl  : [订单状态348582]=>待发货,当前商品id=>1586094502191,商品名称=>Mac pro 2019款

发货服务的日志如下

2020-04-05 21:48:23.202  INFO 3841 --- [nio-9003-exec-1] c.y.t.l.s.service.impl.SendServiceImpl   : [订单状态348582]=>待收货,当前商品id=>1586094502191,商品名称=>Mac pro 2019款

数据库记录两条记录
c0479f4ced6ccd964c1c57292defc86e_MD5.png

现在让订单服务的方法抛出异常
直接在方法中加一个抛异常的语句int i=1/0;,如下所示

@LcnTransaction
@Override
public void saveAndPayOrder(String productName) {

    FwTradeLog fwTradeLog =new FwTradeLog(StatusEnum.TWO);
    fwTradeLog.setProductId(System.currentTimeMillis());
    fwTradeLog.setProductName(productName);
    fwTradeLogService.save(fwTradeLog);
    log.info("[订单状态{}]=>{},当前商品id=>{},商品名称=>{}",fwTradeLog.getOrderId(), StatusEnum.TWO.getDesc(),fwTradeLog.getProductId(),fwTradeLog.getProductName());

    remoteSendServiceFeign.sendOrder(fwTradeLog);

    int i=1/0;
}

重启订单服务
Postman输入localhost:9002/saveOrder
f15a6f74d633c63562e5c42743b75573_MD5.png

可以看到订单服务的日志如下,可以看到com.codingapi.txlcn.tc.core相关的记录了LCN的方法

2020-04-05 21:57:58.580 ERROR 3900 --- [nio-9002-exec-1] c.c.txlcn.tc.core.DTXServiceExecutor     : business code error @group(8a377445b6537)
2020-04-05 21:57:58.640 ERROR 3900 --- [nio-9002-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.ArithmeticException: / by zero] with root cause

java.lang.ArithmeticException: / by zero
	at com.yisu.transaction.lcn.order.service.impl.OrderServiceImpl.saveAndPayOrder(OrderServiceImpl.java:41) ~[classes/:na]
	at com.yisu.transaction.lcn.order.service.impl.OrderServiceImpl$$FastClassBySpringCGLIB$$33d70106.invoke(<generated>) ~[classes/:na]
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:769) ~[spring-aop-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747) ~[spring-aop-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88) ~[spring-aop-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at com.codingapi.txlcn.tc.core.DTXLocalControl.doBusinessCode(DTXLocalControl.java:44) ~[txlcn-tc-5.0.2.RELEASE.jar:5.0.2.RELEASE]
	at com.codingapi.txlcn.tc.core.DTXServiceExecutor.transactionRunning(DTXServiceExecutor.java:91) ~[txlcn-tc-5.0.2.RELEASE.jar:5.0.2.RELEASE]
	at com.codingapi.txlcn.tc.aspect.weave.DTXLogicWeaver.runTransaction(DTXLogicWeaver.java:96) ~[txlcn-tc-5.0.2.RELEASE.jar:5.0.2.RELEASE]
	at com.codingapi.txlcn.tc.aspect.TransactionAspect.runWithLcnTransaction(TransactionAspect.java:93) ~[txlcn-tc-5.0.2.RELEASE.jar:5.0.2.RELEASE]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644) ~[spring-aop-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633) ~[spring-aop-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70) ~[spring-aop-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:175) ~[spring-aop-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747) ~[spring-aop-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93) ~[spring-aop-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747) ~[spring-aop-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689) ~[spring-aop-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at com.yisu.transaction.lcn.order.service.impl.OrderServiceImpl$$EnhancerBySpringCGLIB$$4958f81e.saveAndPayOrder(<generated>) ~[classes/:na]
	at com.yisu.transaction.lcn.order.controller.OrderController.saveOrder(OrderController.java:26) ~[classes/:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888) ~[spring-webmvc-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) ~[spring-webmvc-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.27.jar:9.0.27]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:108) ~[spring-boot-actuator-2.2.1.RELEASE.jar:2.2.1.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1579) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
	at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]

发货服务的日志如下,可以看到发送被调用到了

2020-04-05 21:57:58.541  INFO 3841 --- [nio-9003-exec-3] c.y.t.l.s.service.impl.SendServiceImpl   : [订单状态705197]=>待收货,当前商品id=>1586095077723,商品名称=>Mac pro 2020款


数据库记录并没有,说明TxManager 进行了回滚
3edff946480bc4731a71e24e84a560ec_MD5.png