springcloud-alibaba (05)Seata实现分布式事务-个人笔记

news2024/12/31 7:37:18

前言

本文将介绍如何使用Seata实现分布式事务。将覆盖以下主题:

  • seata下载与安装
  • 如何配置和启动Seata服务器
  • 如何编写应用程序以使用Seata
  • 如何解决常见问题

本文只是我个人seata学习笔记,不是什么学习教程

如果你是一名Java开发人员,那么你一定听说过Seata。Seata是一个分布式事务解决方案,可以帮助开发人员在分布式系统中实现ACID事务,保证数据的一致性和可靠性。如果你还没有了解过Seata,那么建议你先去官方文档里看看,了解一下它的基本概念和用法。官方文档详细介绍了Seata的架构、设计原则和使用方法,对于想要深入学习Seata的开发人员来说是非常有帮助的。同时,我们也可以通过阅读其他开发人员的经验分享和案例分析,更好地掌握Seata在实际项目中的应用。

官方文档:seata中文文档

01 下载解压Seata

你要在Spring Cloud Alibaba中使用Seata,就一定要根据版本说明下载对应的Seata版本。因为不同版本的Spring Cloud Alibaba和Seata之间可能存在兼容性问题,如果使用不兼容的版本,就有可能会出现各种问题,如启动失败、事务不生效等。因此,在选择Seata版本时,一定要仔细查看官方文档中的版本说明和兼容性列表,确保所选择的版本与当前使用的Spring Cloud Alibaba版本兼容。这样才能保证Seata在Spring Cloud Alibaba中正常运行,发挥其应有的作用。

下载地址:Releases · seata/seata (github.com)

我下的版本:Release v1.5.2 · seata/seata (github.com)

在这里插入图片描述

下载到linux中并解压

你可以使用以下的命令来解压缩.tar.gz文件

tar -zxvf seata-server-1.5.2.tar.gz

其中:

  • -z表示解压缩gzip压缩的文件,
  • -x表示从压缩文件中提取文件,
  • -v表示详细输出提取的文件列表,
  • -f后面跟需要解压缩的文件名。

执行此命令后,你将在当前目录下得到一个新的目录 ‘seata’。

02 修改配置文件

主要修改:事务日志存储模式修改为DB+数据库连接信息+注册中心

修改 seata\conf\application.yml 的内容主要有以下三部分:

第一步:添加存储方式

以下步骤按照DB存储为例:

  store:
    mode: db
    db:
      datasource: druid
      db-type: mysql
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:3306/seata-01?rewriteBatchedStatements=true
      user: root
      password: 123456

store指定了Seata事务日志存储模式为数据库模式,采用了Druid连接池,数据库类型为MySQL。

  1. 创建 seata-01 数据库 :

在这里插入图片描述

  1. 创建表:

点查新-新建查询-sql语句复制到查询中-点击运行

sql语句地址

-- -------------------------------- The script used when storeMode is 'db' --------------------------------
-- the table to store GlobalSession data
CREATE TABLE IF NOT EXISTS `global_table`
(
    `xid`                       VARCHAR(128) NOT NULL,
    `transaction_id`            BIGINT,
    `status`                    TINYINT      NOT NULL,
    `application_id`            VARCHAR(32),
    `transaction_service_group` VARCHAR(32),
    `transaction_name`          VARCHAR(128),
    `timeout`                   INT,
    `begin_time`                BIGINT,
    `application_data`          VARCHAR(2000),
    `gmt_create`                DATETIME,
    `gmt_modified`              DATETIME,
    PRIMARY KEY (`xid`),
    KEY `idx_status_gmt_modified` (`status` , `gmt_modified`),
    KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(
    `branch_id`         BIGINT       NOT NULL,
    `xid`               VARCHAR(128) NOT NULL,
    `transaction_id`    BIGINT,
    `resource_group_id` VARCHAR(32),
    `resource_id`       VARCHAR(256),
    `branch_type`       VARCHAR(8),
    `status`            TINYINT,
    `client_id`         VARCHAR(64),
    `application_data`  VARCHAR(2000),
    `gmt_create`        DATETIME(6),
    `gmt_modified`      DATETIME(6),
    PRIMARY KEY (`branch_id`),
    KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
    `row_key`        VARCHAR(128) NOT NULL,
    `xid`            VARCHAR(128),
    `transaction_id` BIGINT,
    `branch_id`      BIGINT       NOT NULL,
    `resource_id`    VARCHAR(256),
    `table_name`     VARCHAR(32),
    `pk`             VARCHAR(36),
    `status`         TINYINT      NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',
    `gmt_create`     DATETIME,
    `gmt_modified`   DATETIME,
    PRIMARY KEY (`row_key`),
    KEY `idx_status` (`status`),
    KEY `idx_branch_id` (`branch_id`),
    KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

CREATE TABLE IF NOT EXISTS `distributed_lock`
(
    `lock_key`       CHAR(20) NOT NULL,
    `lock_value`     VARCHAR(20) NOT NULL,
    `expire`         BIGINT,
    primary key (`lock_key`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('AsyncCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryRollbacking', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('TxTimeoutCheck', ' ', 0);

在这里插入图片描述

第二步:修改注册中心

  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace:
      cluster: default
      username:
      password:

第三部:添加配置中心

seata:
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace:
      group: SEATA_GROUP
      username:
      password:

最终的application.yml文件内容:

server:
  port: 7091

spring:
  application:
    name: seata-server

logging:
  config: classpath:logback-spring.xml
  file:
    path: ${user.home}/logs/seata
  extend:
    logstash-appender:
      destination: 127.0.0.1:4560
    kafka-appender:
      bootstrap-servers: 127.0.0.1:9092
      topic: logback_to_logstash
      
console:
  user:
    username: seata
    password: seata      

seata:
  config:
    # support: nacos 、 consul 、 apollo 、 zk  、 etcd3
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace:
      group: SEATA_GROUP
      username:
      password:
      ##if use MSE Nacos with auth, mutex with username/password attribute
      #access-key: ""
      #secret-key: ""
      data-id: seataServer.properties
  registry:
    # support: nacos 、 eureka 、 redis 、 zk  、 consul 、 etcd3 、 sofa
    type: nacos
    #preferred-networks: 30.240.*
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace:
      cluster: default
      username:
      password:
      ##if use MSE Nacos with auth, mutex with username/password attribute
      #access-key: ""
      #secret-key: ""

  server:
    service-port: 8091 #If not configured, the default is '${server.port} + 1000'
    max-commit-retry-timeout: -1
    max-rollback-retry-timeout: -1
    rollback-retry-timeout-unlock-enable: false
    enable-check-auth: true
    enable-parallel-request-handle: true
    retry-dead-threshold: 130000
    xaer-nota-retry-timeout: 60000
    recovery:
      handle-all-session-period: 1000
    undo:
      log-save-days: 7
      log-delete-period: 86400000
    session:
      branch-async-queue-size: 5000 #branch async remove queue size
      enable-branch-async-remove: false #enable to asynchronous remove branchSession
  store:
    # support: file 、 db 、 redis
    mode: db
    #session:
     # mode: file
    #lock:
     # mode: file
    db:
      datasource: druid
      db-type: mysql
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://192.168.90.137:3306/seata-01?rewriteBatchedStatements=true
      user: root
      password: 123456
      min-conn: 5
      max-conn: 100
      global-table: global_table
      branch-table: branch_table
      lock-table: lock_table
      distributed-lock-table: distributed_lock
      query-limit: 100
      max-wait: 5000
  metrics:
    enabled: false
    registry-type: compact
    exporter-list: prometheus
    exporter-prometheus-port: 9898
  transport:
    rpc-tc-request-timeout: 30000
    enable-tc-server-batch-send-response: false
    shutdown:
      wait: 3
    thread-factory:
      boss-thread-prefix: NettyBoss
      worker-thread-prefix: NettyServerNIOWorker
      boss-thread-size: 1
  security:
    secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
    tokenValidityInMilliseconds: 1800000
    ignore:
      urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login      

这个配置文件的内容可能会因为Seata版本的不同而略有不同,具体的配置项也可能会有所变化。因此,在使用Seata时,建议先查看官方文档中的配置说明,根据实际情况进行配置。另外,Seata还提供了多种配置方式,如通过配置文件、通过环境变量、通过命令行参数等,可以根据自己的习惯和需求选择合适的方式进行配置。

02 配置上传

上面的代码已经改为db模式,并且用nacos作为注册中心了,所以我们接下来要做的就是将配置上传到nacos进行存储

第一步:修改config.txt文件

onfig.txt提供的默认参数太多,所以去掉了我用不着的配置,我自己的最终config.txt文件如下:

编辑:seata\script\config-center\config.txt文件,改成以下配置 :

#For details about configuration items, see https://seata.io/zh-cn/docs/user/configurations.html
#Transport configuration, for client and server
transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableTmClientBatchSendRequest=false
transport.enableRmClientBatchSendRequest=true
transport.enableTcServerBatchSendResponse=false
transport.rpcRmRequestTimeout=30000
transport.rpcTmRequestTimeout=30000
transport.rpcTcRequestTimeout=30000
transport.threadFactory.bossThreadPrefix=NettyBoss
transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
transport.threadFactory.shareBossWorker=false
transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
transport.threadFactory.clientSelectorThreadSize=1
transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
transport.threadFactory.bossThreadSize=1
transport.threadFactory.workerThreadSize=default
transport.shutdown.wait=3
transport.serialization=seata
transport.compressor=none

#Transaction routing rules configuration, only for the client
service.vgroupMapping.default_tx_group=default
#If you use a registry, you can ignore it
service.default.grouplist=127.0.0.1:8091
service.enableDegrade=false
service.disableGlobalTransaction=false

#Transaction rule configuration, only for the client

#For TCC transaction mode
tcc.fence.logTableName=tcc_fence_log
tcc.fence.cleanPeriod=1h

#Log rule configuration, for client and server
log.exceptionRate=100

第二步:执行nacos-config.sh文件

进到:seata\script\config-center\nacos

使用以下的命令来给文件添加执行权限:

chmod +x nacos-config.sh

在这里插入图片描述

然后运行脚本

./nacos-config.sh

启动参数说明如下:

在这里插入图片描述

在这里插入图片描述

成功设置了28个参数。提示你 SEATA 的配置已完成,可以启动 SEATA 服务器了。

执行成功后可以打开Nacos的控制台,在配置列表中,可以看到初始化了很多Group为SEATA_GROUP的配置。

03: 启动seata服务

启动 SEATA 服务器方式:

  1. 使用 SEATA 官方提供的脚本启动

在安装完 SEATA 后,可以切换到 SEATA 安装目录下的 bin 目录,执行以下命令启动 SEATA 服务器:

sh seata-server.sh

是启动 SEATA 服务器时的一个命令。

参数具体含义如下:

  • -p:指定 SEATA 服务器启动的端口号;
  • -h:指定 SEATA 服务器的 IP 地址;
  • -m:指定 SEATA 的事务日志存储模式:

启动 SEATA 服务器时需要用到这些参数,以指定 SEATA 服务器的启动配置,确保其能够正确地存储和管理分布式事务。

启动报错:

[root@localhost bin]# sh seata-server.sh -p 7091 -h 127.0.0.1 -m db
apm-skywalking not enabled
seata-server is starting, you can check the /download/seata/seata/logs/start.out
[root@localhost bin]# cat /download/seata/seata/logs/start.out
/download/jdks/jdk-11//bin/java  -server -Dloader.path=../lib -Xmx2048m -Xms2048m -Xmn1024m -Xss512k -XX:SurvivorRatio=10 -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m -XX:MaxDirectMemorySize=1024m -XX:-OmitStackTraceInFastThrow -XX:-UseAdaptiveSizePolicy -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/download/seata/seata/logs/java_heapdump.hprof -XX:+DisableExplicitGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=75 -Xloggc:/download/seata/seata/logs/seata_gc.log -verbose:gc -XX:+PrintGCDetails  -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M -Dio.netty.leakDetectionLevel=advanced -Dapp.name=seata-server -Dapp.pid=7277 -Dapp.home=/download/seata/seata -Dbasedir=/download/seata/seata -Dspring.config.location=/download/seata/seata/conf/application.yml -Dlogging.config=/download/seata/seata/conf/logback-spring.xml -jar /download/seata/seata/target/seata-server.jar -p 7091 -h 127.0.0.1 -m db
[0.000s][warning][gc] -Xloggc is deprecated. Will use -Xlog:gc:/download/seata/seata/logs/seata_gc.log instead.
Unrecognized VM option 'PrintGCDateStamps'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

报错信息:

  • Error: Could not create the Java Virtual Machine.
  • Error: A fatal exception has occurred. Program will exit.

原因:shell脚本获取jdk版本不对 解决:修改 seata-server.sh。 JAVA_MAJOR_VERSION= ( ( (JAVACMD -version 2>&1 | sed ‘1!d’ | sed -e ‘s/"//g’ | awk ‘{print $3}’ | awk -F ‘.’ ‘{print $1}’) print $2 改为 print $1。

在这里插入图片描述

最后再次启动 SEATA 服务器,启动后在 Nacos 的服务列表下面可以看到一个名为 seata-server的服务。

sh seata-server.sh

在这里插入图片描述

到这里,其实我们就完成了Seata服务端的注册和配置的统一管理了,这个做完之后,接下来的就是要结合微服框架进行整合,实现分布式事务场景了

04: 简单使用

下面是一个简单的 Spring Cloud Alibaba Seata 实例。

假设我们有一个微服务应用,其中包含两个服务:订单服务(order-server)和库存服务(inventory-server),需要使用 Seata 进行分布式事务处理。

创建两个订单服务(order-server)和库存服务(inventory-server) 并且注册到nacos中

在这里插入图片描述

第一步:添加依赖

在订单服务和库存服务的 pom.xml 中添加如下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.4</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.29</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.11</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
=======================================================================================================
<!--nacos客户端-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--seata的依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
=============================================================================================================

第二步:配置文件

在订单服务和库存服务中分别添加 Seata 配置信息:

=============================================inventory-server==============================================
spring:
  application:
    name: inventory-server
  datasource:
    druid:
      url: jdbc:mysql://localhost:3306/inventory-server?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.157.130:8848

seata:
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: 192.168.157.130:8848
      group: SEATA_GROUP
  config:
    type: nacos
    nacos:
      server-addr: 192.168.157.130:8848
      group: SEATA_GROUP
  tx-service-group: default_tx_group

mybatis:
  mapper-locations: classpath:/mapper/*.xml

server:
  port: 9410
  
=============================================inventory-server==============================================
spring:
  application:
    name: order-server
  datasource:
    druid:
      url: jdbc:mysql://localhost:3306/order-server?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.157.130:8848

seata:
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: 192.168.157.130:8848
      group: SEATA_GROUP
  config:
    type: nacos
    nacos:
      server-addr: 192.168.157.130:8848
      group: SEATA_GROUP
  tx-service-group: default_tx_group #自定义组名称,需要与seata-server中配置的对应


mybatis:
  mapper-locations: classpath:/mapper/*.xml

server:
  port: 9412

在这个配置文件中,Seata使用了Nacos作为服务注册中心和配置中心。registry.type和config.type都被设为了nacos,同时指定了Nacos的相关配置如服务器地址和应用名。tx-service-group指定了Seata事务组名为default_tx_group。mybatis.mapper-locations指定了MyBatis的XML映射文件位置。此外,还有应用名、数据源等其他配置。

注意:

seata:
  tx-service-group: default_tx_group

tx-service-group事务组名的值等于:seata\script\config-center\config.txt文件里面的:

service.vgroupMapping.default_tx_group=default 中的最后一段内容,default_tx_group

在这里插入图片描述

第三步:添加依赖

在订单服务和库存服务的 pom.xml 中添加如下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.4</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.29</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.11</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
=======================================================================================================
<!--nacos客户端-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--seata的依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
=============================================================================================================

第三步:编写接口

order-server

order:Controller

@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    OrderService orderService;

    @GetMapping("updateById")
    public Integer updateById(@RequestParam("id") Integer id,@RequestParam("name") String name){
        return orderService.updateById(id,name);
    }

}

order:mapper.xml

<update id="updateById" >
    update ordertable set name = #{name} where id =#{id}
</update>

order: 启动类

@SpringBootApplication
@MapperScan("com.buba.mapper")
@EnableDiscoveryClient  //启用服务注册与发现
@EnableFeignClients     //开启Fegin
public class OrderServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServerApplication.class,args);
        System.out.println("run");
    }
}

inventory-server

inventory:controller:

@RestController
@RequestMapping("/inventory")
public class InventoryController {

    @Autowired
    InventoryService inventoryService;

    @GetMapping("/updateById")
    public Integer updateById(@RequestParam("id") Integer id, @RequestParam("name") String name){
        return inventoryService.updateById(id,name);
    }

}

inventory:mapper.xml

<update id="updateById">
    update inventory set name = #{name} where id = #{id}
</update>

inventory: 启动类

@SpringBootApplication
@EnableDiscoveryClient  //启用服务注册与发现
@EnableFeignClients     //开启Fegin
public class InventoryServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(InventoryServerApplication.class,args);
        System.out.println("run");
    }
}

inventory: OrderFeign接口

@FeignClient(name = "order-server",path = "/order")
public interface OrderFeign {
    
    @GetMapping("updateById")
    Integer updateById(@RequestParam("id") Integer id, @RequestParam("name") String name);
}

inventory: Service

@Service
public class InventoryServiceImpl implements InventoryService {

    @Autowired
    InventoryMapper inventoryMapper;

    @Autowired
    OrderFeign orderFeign;

    @Override
    @GlobalTransactional
    public Integer updateById(Integer id, String name) {
        Integer number = orderFeign.getNumber(id,name);
        Integer num = inventoryMapper.updateById(id,name);
        return num+number;
    }
}

在这里插入图片描述

第四步:添加回滚日志表

两个库下都需要建各自的回滚日志表

CREATE TABLE `undo_log`
(
`id` BIGiNT(20) NOT NULL AUTO_INCREMENT,
`branch_id` BIGiNT(20) NOT NULL,
`xid` VARcHAR(100) NOT NULL,
`context` VARcHAR(128) NOT NULL,
`rollback_info` LONGBLOB NOT NULL,
`log_status` iNT(11) NOT NULL,
`log_created` DATETIME NOT NULL,
`log_modified` DATETIME NOT NULL,
`ext` VARcHAR(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = INNODB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8;

在这里插入图片描述

第五步:测试接口

nventory: Service

@Service
public class InventoryServiceImpl implements InventoryService {

    @Autowired
    InventoryMapper inventoryMapper;

    @Autowired
    OrderFeign orderFeign;

    @Override
    @GlobalTransactional
    public Integer updateById(Integer id, String name) {
        Integer number = orderFeign.getNumber(id,name);
        int i = 5/0;
        Integer num = inventoryMapper.updateById(id,name);
        return num+number;
    }
}

添加了一个运行时异常 java.lang.ArithmeticException: / by zero,模拟数据修改过程中的异常情况

debug模式启动 inventory-server,并且给int i = 5/0;打断点

在这里插入图片描述

访问inventory-server服务的http://localhost:9410/inventory/updateById接口

在这里插入图片描述

打开idea

在这里插入图片描述

先不要恢复程序,打开数据库查看ordertable表和undo_log

在这里插入图片描述

最后放开debug恢复程序,接口返回500

在这里插入图片描述

打开数据库的表-右键刷新,发现数据改回去了

在这里插入图片描述

当程序执行到int i = 5/0时,会抛出一个异常并进入异常处理流程,此时事务会回滚。

05: 报错笔记

(1):启动报错:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘globalTransactionScanner’ defined in class path resource [io/seata/spring/boot/autoconfigure/SeataAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.seata.spring.annotation.GlobalTransactionScanner]: Factory method ‘globalTransactionScanner’ threw exception; nested exception is java.lang.ExceptionInInitializerError

解决方法:

首先,它是由Spring框架引起的,它尝试通过工厂方法创建bean时遇到了一个错误。具体来说,是由于全局事务扫描器(globalTransactionScanner)的初始化失败导致的,这个扫描器又是在SeataAutoConfiguration类中定义的。

解决这个问题的方法非常简单,只需要将VM选项添加:–add-opens=java.base/java.lang=ALL-UNNAMED。这个命令会在JVM启动时添加指定的选项,让Java程序能够正确地使用某些类。如果您使用的是Java 9及以上版本,那么就需要使用这个命令才能避免出现这类错误。

VM选项添加:--add-opens=java.base/java.lang=ALL-UNNAMED

--add-opens=java.base/java.lang=ALL-UNNAMED

在这里插入图片描述

(2):启动报错:

  • io.seata.common.exception.FrameworkException: can not register RM,err:can not connect to services-server.
  • 2023-05-30 19:34:04.257 ERROR 7508 — [ main] i.s.c.r.netty.NettyClientChannelManager : 0101 can not connect to 192.168.157.130:8091 cause:can not register RM,err:can not connect to services-server.

解决方法:

此次报错是由Seata分布式事务框架引起的。该错误是在尝试注册资源管理器(RM)时无法连接到注册中心引起的。以下可能是引起错误的原因:

  1. Seata Server 没有正确启动或无法访问

  2. 注册中心配置不正确

  3. 业务应用程序与注册中心无法通信

  4. Seata 配置文件配置不正确

要解决这个问题,您需要检查详细的错误日志,以了解更多关于错误的信息,以便更进一步地排查和解决问题。

但本次经过检查后发现问题原因是端口号没打开。

可以使用firewall-cmd --zone=public --list-ports命令查看开放的防火墙端口。为确保Seata能够正常工作,可以使用以下命令打开Seata默认端口号8091:

firewall-cmd --zone=public --add-port=8091/tcp --permanent

最后,要记得刷新防火墙以重新加载端口号:

firewall-cmd --reload

(3):启动seata服务报错:

[root@localhost bin]# sh seata-server.sh -p 7091 -h 127.0.0.1 -m db
apm-skywalking not enabled
seata-server is starting, you can check the /download/seata/seata/logs/start.out
[root@localhost bin]# cat /download/seata/seata/logs/start.out
/download/jdks/jdk-11//bin/java  -server -Dloader.path=../lib -Xmx2048m -Xms2048m -Xmn1024m -Xss512k -XX:SurvivorRatio=10 -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m -XX:MaxDirectMemorySize=1024m -XX:-OmitStackTraceInFastThrow -XX:-UseAdaptiveSizePolicy -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/download/seata/seata/logs/java_heapdump.hprof -XX:+DisableExplicitGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=75 -Xloggc:/download/seata/seata/logs/seata_gc.log -verbose:gc -XX:+PrintGCDetails  -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M -Dio.netty.leakDetectionLevel=advanced -Dapp.name=seata-server -Dapp.pid=7277 -Dapp.home=/download/seata/seata -Dbasedir=/download/seata/seata -Dspring.config.location=/download/seata/seata/conf/application.yml -Dlogging.config=/download/seata/seata/conf/logback-spring.xml -jar /download/seata/seata/target/seata-server.jar -p 7091 -h 127.0.0.1 -m db
[0.000s][warning][gc] -Xloggc is deprecated. Will use -Xlog:gc:/download/seata/seata/logs/seata_gc.log instead.
Unrecognized VM option 'PrintGCDateStamps'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

报错信息:

  • Error: Could not create the Java Virtual Machine.
  • Error: A fatal exception has occurred. Program will exit.

提供的报错信息表明在启动Seata服务时无法创建虚拟机,并出现了致命异常。这可能是由于 shell脚本获取JDK版本的错误导致的。

你可以尝试修改seata-server.sh文件。将以下行:

JAVA_MAJOR_VERSION=$($JAVACMD -version 2>&1 | sed '1!d' | sed -e 's/"//g' | awk '{print $3}' | awk -F '.' '{print $1}') print $2

修改为:

JAVA_MAJOR_VERSION=$($JAVACMD -version 2>&1 | sed '1!d' | sed -e 's/"//g' | awk '{print $3}' | awk -F '.' '{print $1}') print $1

这将修复该错误并允许Seata服务启动,最后再次启动 SEATA 服务器

在这里插入图片描述

希望这个解决方法能够解决你的问题。有其他问题在评论区讨论-或者私信我-收到会在第一时间回复

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/593036.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

官方喊你来免费下载 Navicat Premium 16.2 Beta 中文版 | Redis 体验官火热招募中

今天&#xff0c;我们发布了 Navicat 16.2 Beta 中文版&#xff0c;它适用于 Windows、macOS 和 Linux 平台。届时&#xff0c;我们诚邀广大 Redis 用户及爱好者亲测 Beta 版&#xff0c;希望 Redis 新功能将为 Redis 相关工作者&#xff08;应用开发人员、DBA 和数据分析师等&…

【paddlecls】多机多卡-linux(二:环境搭建)

构建并进入 docker 容器后&#xff0c;我们进入下一步&#xff1a; 1. 退出/进入 docker 容器&#xff1a; 在进入 Docker 容器后&#xff0c;可使用组合键 Ctrl P Q 退出当前容器&#xff0c;同时不关闭该容器&#xff1b; 如需再次进入容器&#xff0c;可使用下述命令&am…

微信小程序项目实例——生活记账本

今日推荐&#x1f481;‍♂️ 2023五月天演唱会&#x1f3a4;&#x1f3a4;&#x1f3a4;大家一起冲冲冲&#x1f3c3;‍♂️&#x1f3c3;‍♂️&#x1f3c3;‍♂️ &#x1f52e;&#x1f52e;&#x1f52e;&#x1f52e;&#x1f52e;往期优质项目实例&#x1f52e;&…

蓝牙资讯|Counterpoint发布2023年Q1中国智能手表报告

根据市场调查机构 Counterpoint Research 公布的最新报告&#xff0c;2023 年第 1 季度中国智能手表出货量同比下降 28%&#xff0c;环比下降 16%&#xff0c;达到过去 12 个季度以来的最低水平。 本季度智能手表市场中&#xff0c;华为、苹果和小天才&#xff08;imoo&#…

想要入坑C++?当我拿出菱形虚拟继承,阁下又该如何应对

文章目录 &#x1f490;专栏导读&#x1f490;文章导读&#x1f337;继承的定义方式&#x1f337;继承方式与访问限定符&#x1f337;基类和派生类对象赋值转换&#x1f337;继承中的作用域&#x1f337;派生类的默认成员函数&#x1f337;继承与友元&#x1f337;继承与静态成…

基于 ESP32 通过 SMTP 服务器 来发送电子邮件信息

电子邮件在全球范围内被用作数字通信的重要组成部分。电子邮件主要用于官方通信目的,因为它最方便、成本效益高、保存记录、覆盖全球且环保。电子邮件是一种非常快捷的通信方式,只是您需要稳定的互联网连接。 在这个项目中,我们将使用ESP32开发板发送电子邮件(纯文本和 HTM…

因为计算机中丢失VCRUNTIME140怎么办?为什么会丢失VCRUNTIME140.dll

vcruntime140.dll是一个Windows动态链接库&#xff0c;其主要功能是为C/C编译的程序提供运行时支持。这个库在Microsoft Visual Studio 2015中被引入&#xff0c;其名称中的“140”代表版本号。在我们打开运行软件或者游戏程序的时候&#xff0c;电脑提示因为计算机中丢失VCRUN…

windows下编译roadrunner和作为laravel服务器实践

roadrunner源码地址&#xff1a;https://gitee.com/mirrors/RoadRunner?_fromgitee_search windows下编译roadrunner源码获得rr.exe可执行文件 将rr.exe拷贝到laravel目录下 .rr.yaml配置文件内容&#xff1a; version: 3 server: command: "php vendor/spiral/road…

基于Python+AIML+Tornado的智能聊天机器人(NLP+深度学习)含全部工程源码+语料库 适合个人二次开发

目录 前言总体设计系统整体结构图系统流程图 运行环境Python 环境Tornado 环境 模块实现1. 前端2. 后端3. 语料库4. 系统测试 其它资料下载 前言 本项目旨在利用AIML技术构建一个聊天机器人&#xff0c;实现用户通过聊天界面与机器人交互的功能。通过提供的工程源代码&#xf…

【业务功能篇07】Mysql 模糊查询

业务场景&#xff1a;我们对不同的业务逻辑进行数据处理时&#xff0c;多数是离不开需要模糊匹配的时候&#xff0c;比如要获取该表某个字段中&#xff0c;含有某个具体的字符内容&#xff0c;过滤出业务想要的数据。 这里介绍有这么几种&#xff1a; 一、MySQL通配符模糊查询(…

3D模型Web轻量化工具,如何监测矿藏开采安全与效率?

随着科技的进步&#xff0c;各个领地都在不断探索和应用新的技术来提高效率和准确性。HOOPS技术作为一种先进的3D可视化和模拟技术&#xff0c;正在采掘和地质科学领域发挥着重要的作用。本文将探讨HOOPS技术在采掘和地质科学中的具体应用&#xff0c;并分析其对这些领域的影响…

Maven高级1-分模块开发与依赖问题

1. 分模块开发与设计 将原始模块按照功能拆分成若干个子模块&#xff0c;方便模块间的相互调用&#xff0c;接口共享&#xff1b; 言简意赅就是把功能模块放出去&#xff0c;然后通过在pom文件中导入坐标找到&#xff1b; 注意拆出来的功能模块需要通过Maven指令安装模块选择in…

Python Web后端面试常考数据结构与算法(珍藏版)

本文将对Python web后端面试时常考数据结构与算法进行总结&#xff0c;适合即将找工作或面试的你。Python web后端常考数据结构包括: 常见的数据结构链表、队列、栈、二叉树、堆 使用内置的结构实现高级数据结构&#xff0c;比如内置的list/deque实现栈 LeetCode或者剑指Offe…

【配电网重构】高比例清洁能源接入下计及需求响应的配电网重构【IEEE33节点】(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

循环冗余计算

题目&#xff1a;若信息码字为111000110&#xff0c;生成多项式 x 5 x^5 x5 x 3 x^3 x3 x x x1&#xff0c;则计算crc校验码为()。 将生成多项式的系数作为除数&#xff08;101011&#xff09;&#xff1b; 获得方法1x50x41x30x21x1生成多项式的最高幂次数&#xff08;5&#…

怎样使用TikTok增加销售额?TikTok选品小tips?

哈喽everybody&#xff01;我又来给大家分享干货了&#xff01;今天为大家带来使用TikTok增加销售额和TikTok选品的小tips&#xff0c;让你运营TikTok Shop不迷茫&#xff0c;快快往下看吧&#xff01; 一、如何使用TikTok吸引客户、增加销售额 1.优化产品目录 与任何线上商店…

Python实现KNN算法(附源码)

本篇我们将讨论一种广泛使用的分类技术&#xff0c;称为k邻近算法&#xff0c;或者说K最近邻(KNN&#xff0c;k-Nearest Neighbor)。所谓K最近邻&#xff0c;是k个最近的邻居的意思&#xff0c;即每个样本都可以用它最接近的k个邻居来代表。 01、KNN算法思想 如果一个样本在特征…

绿色智慧档案顺丰环境一体化平台选型表

盛世宏博八防一体化监控系统选型表 序号 功能选择 1 恒温恒湿系统 温湿度监测 口Y&#xff1a;需要 口N&#xff1a;不需要 空调控制 口Y&#xff1a;需要 口N&#xff1a;不需要 加湿机控制 口Y&#xff1a;需要 口N&#xff1a;不需要 除湿…

KD05丨动量RSI策略

大家好&#xff0c;今天我们来分享魔改RSI策略&#xff0c;RSI即相对强弱指数&#xff0c;本质上就是一个动量指标&#xff0c;用于衡量一定时间内价格变动的速度及其变动的大小。它在0-100的范围内变动&#xff0c;通常以70和30作为过热和过冷的界限。要将RSI指标改为一个趋势…

Smartbi“三步走”构建智慧经营分析平台,实现国有企业监管报送和数智化转型

01. 现状与痛点 — 一直以来&#xff0c;国资国企都是促进我国经济高速发展的领头羊&#xff0c;但近年来受疫情冲击和国际经济下行影响&#xff0c;国资企业经营面临较大压力&#xff0c;同时为实现国有企业高质量发展&#xff0c;国务院国资委下发一系列政策要求&#xff…