当前位置: 代码迷 >> 综合 >> springboot集成dubbo2.7.8+nacos1.3.2+seata1.4.0 分布式事务解决方案
  详细解决方案

springboot集成dubbo2.7.8+nacos1.3.2+seata1.4.0 分布式事务解决方案

热度:88   发布时间:2023-12-04 23:32:04.0

序言:

分布式事务对于现在的分布式的项目来说很常见的一个问题,此次采用的是阿里开源的seata,seata官网,

此次集成不需要其seata在客户端配置相关的file.conf 、registry.conf   配置文件。

不需要配置相关的配置文件时需要引入:seata-spring-boot-starter 的pom依赖,这个是我推荐使用的集成方式。

在此工程中实践分布式事务的流程是为:

 ———today服务创建订单,yesterday服务进行扣减余额,若yesterday服务扣减余额发生异常,则两个服务所执行的SQL数据进行回滚。

一、工程中的代码部分:

1、父工程的依赖:

<spring-boot.version>2.3.3.RELEASE</spring-boot.version>
<spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
<dubbo.version>2.7.8</dubbo.version>
<seata.version>1.4.0</seata.version><dependencyManagement><dependencies><!--spring boot--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency><!--spring cloud alibaba--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency><!--seata-分布式事务管理--><dependency><groupId>io.seata</groupId><artifactId>seata-spring-boot-starter</artifactId><version>${seata.version}</version></dependency></dependencies></dependencyManagement>

2、子工程的依赖:

说明:即today服务和yesterday服务需要注册到nacos中的接口所在的工程,today和yesterday都会引入此工程

        <dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>${dubbo.version}</version></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>

3、today服务和yesterday服务的启动类:

import io.seata.spring.annotation.datasource.EnableAutoDataSourceProxy;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
//开启dubbo
@EnableDubbo
//扫描工程中所依赖的包中的bean,将注册到springioc中
@ComponentScan("com.day")
//开启分布式事务的代理,将事务交给seata
@EnableAutoDataSourceProxy
//参考了pig-cloud开源项目中的动态数据源
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class XXXXXXXApplication {public static void main(String[] args) {SpringApplication.run(DayTodayApplication.class, args);}}

4、today服务和yesterday服务的配置文件:

默认的配置文件:application.properties

公共的配置参数的配置文件:application-common.properties

当前开发环境的配置文件:application-dev.properties

其seata配置信息主要参考了seata官网:http://seata.io/zh-cn/docs/user/registry/nacos.html

在这里主要写出dubbo的配置信息和seata的配置信息:

#dubbo配置
dubbo.application.name=today-provider
dubbo.config-center.timeout=500000
dubbo.registry.address=nacos://${nacos.discovery.server-addr}
dubbo.protocol.name=dubbo
dubbo.protocol.port=20886
dubbo.scan.base-packages=com.day.api.provider.today
dubbo.consumer.check=false
dubbo.application.qos-enable=false
dubbo.application.qos-accept-foreign-ip=false
#seata配置
nacos.discovery.server-addr=47.104.78.115:8848
#seata-config
seata.application-id=${spring.application.name}
seata.tx-service-group=day_today_tx_group
seata.service.vgroup-mapping.day_today_tx_group=seata-days
seata.service.grouplist.seata-days=127.0.0.1:8091
seata.registry.type=nacos
seata.registry.nacos.application=seata-server
seata.registry.nacos.server-addr=${nacos.discovery.server-addr}
seata.registry.nacos.group=SEATA_GROUP
seata.registry.nacos.username=nacos
seata.registry.nacos.password=nacos

5、today服务中调用yesterday服务的代码:

控制层:
@RestController
@Api(tags = "今天的控制层信息")
@RequestMapping("/today")
public class TodayController {private static final Logger log = LoggerFactory.getLogger(TodayController.class);@DubboReference(group = DubboNacosGroup.YESTERDAY_DUBBO_NACOS)private YesterdayProvider yesterdayProvider;@Autowiredprivate IOrderService orderService;@ApiOperation(value = "测试分布式事务")@RequestMapping(value = "testDistributedTransaction", method = RequestMethod.POST)public JsonResult testDistributedTransaction() {Order order = new Order();order.setUserId("1001");order.setAmount(300d);return JsonResult.success(orderService.saveOrder(order));}
}
service层:/*** 用于测试分布式事务功能** @param order* @author wangjunming* @since 2020/11/8 14:46*/@Override@GlobalTransactional(rollbackFor = Exception.class)public boolean saveOrder(Order order) {log.info("开始创建订单");final boolean save = save(order);boolean updataAmount = false;if(save){log.info("完成创建订单");log.info("开始扣减余额");AccountApiEntity accountApiEntity = new AccountApiEntity();accountApiEntity.setUserId(order.getUserId());accountApiEntity.setAmount(order.getAmount());updataAmount = yesterdayProvider.updataAmountByUserId(accountApiEntity);log.info("完成扣减余额");}log.info("开始更新订单状态");
//        int i = 12/0;boolean update = false;if(updataAmount){order.setStatus(Integer.valueOf("1"));update = update(order);log.info("完成更新订单状态");}return save && updataAmount && update;}

6、yesterday服务注册到nacos服务中的接口和类:

其中使用到的model和接口都在第二步中所创建的子工程中。这个时候需要调用在yesterday服务中的用户余额服务进行扣减余额。

接口:
public interface YesterdayProvider {/*** 用于测试分布式事务** @author wangjunming* @since 2020/11/8 14:52*/boolean updataAmountByUserId(AccountApiEntity accountApiEntity);}
实现类:
import com.day.api.config.DubboNacosGroup;
import com.day.api.entitys.AccountApiEntity;
import com.day.api.provider.yesterday.YesterdayProvider;
import com.day.yesterday.persistence.entity.Account;
import com.day.yesterday.persistence.service.IAccountService;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
@DubboService(group = DubboNacosGroup.YESTERDAY_DUBBO_NACOS)
public class YesterdayProviderImpl implements YesterdayProvider {@Autowiredprivate IAccountService accountService;/*** 用于测试分布式事务** @param accountApiEntity 扣减金额* @author wangjunming* @since 2020/11/8 14:52*/@Overridepublic boolean updataAmountByUserId(AccountApiEntity accountApiEntity) {Account account = new Account();account.setUserId(accountApiEntity.getUserId());account.setAmount(accountApiEntity.getAmount());return accountService.updataAmountByUserId(account) != null;}}

7、yesterday服务中调用用户服务进行扣减余额

    /*** 用于测试分布式事务,业务为根据用户ID扣减相应的余额** @param account 扣减金额* @author wangjunming* @since 2020/11/8 14:18*/@Override@GlobalTransactional(rollbackFor = Exception.class)public Account updataAmountByUserId(Account account){final Account selOneAccount = selOne(account);if (selOneAccount == null) {log.error("余额不足!");return null;}if (account.getAmount().compareTo(selOneAccount.getAmount()) > 0) {log.error("余额不足!");return null;}final double amountPoor = selOneAccount.getAmount() - account.getAmount();selOneAccount.setAmount(amountPoor);final boolean update = accountMapper.updateById(selOneAccount) > 0;int i = 12/0;return update ? selOneAccount : null;}

--------------------------至此在代码方面的工作到此结束。

二、部署seata的server端的部分:

8、初始化seata服务端和各个服务端所需要的数据库

————获取seata服务端(server)初始化SQL语句的官网地址:https://github.com/seata/seata/blob/1.4.0/script/server/db/mysql.sql   

-- 创建数据库:
CREATE DATABASE `seata`;
-- -------------------------------- 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_gmt_modified_status` (`gmt_modified`, `status`),KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDBDEFAULT CHARSET = utf8;-- 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 = InnoDBDEFAULT CHARSET = utf8;-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(`row_key`        VARCHAR(128) NOT NULL,`xid`            VARCHAR(96),`transaction_id` BIGINT,`branch_id`      BIGINT       NOT NULL,`resource_id`    VARCHAR(256),`table_name`     VARCHAR(32),`pk`             VARCHAR(36),`gmt_create`     DATETIME,`gmt_modified`   DATETIME,PRIMARY KEY (`row_key`),KEY `idx_branch_id` (`branch_id`)
) ENGINE = InnoDBDEFAULT CHARSET = utf8;

————创建today服务中的数据库和订单表:

# -----------创建订单表---------------------------------------------------------------
# 创建  seata_order 数据库
CREATE DATABASE `seata_order`;
# 创建订单表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for t_order
-- ----------------------------
DROP TABLE IF EXISTS `t_order`;
CREATE TABLE `t_order`
(`id`             int(11)                                                 NOT NULL AUTO_INCREMENT,`order_no`       varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`user_id`        varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`commodity_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`status`         int(5)                                                  NULL DEFAULT 0,`amount`         double(14, 2)                                           NULL DEFAULT 0.00,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDBAUTO_INCREMENT = 1CHARACTER SET = utf8COLLATE = utf8_general_ciROW_FORMAT = Dynamic;

————创建yesterday服务中的数据库和用户钱包表:

# --------------创建用户钱包表---------------------------------------------------------
#创建 seata_pay 数据库
CREATE DATABASE `seata_pay`;
#创建用户钱包表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for account
-- ----------------------------
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account`
(`id`      int(11)                                                 NOT NULL AUTO_INCREMENT,`user_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`amount`  double(14, 2)                                           NULL DEFAULT 0.00,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDBAUTO_INCREMENT = 3CHARACTER SET = utf8COLLATE = utf8_general_ciROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of account
-- ----------------------------
#初始化数据
INSERT INTO `account` 
VALUES (1, '1', 4000.00);
INSERT INTO `account`
VALUES (2, '1001', 50000.00);
SET FOREIGN_KEY_CHECKS = 1;

以及在today服务的数据库(seata_order)和yesterday服务的数据库(seata_pay)所创建的undo_log表

获取创建(client端)表的SQL语句官网地址:https://github.com/seata/seata/blob/1.4.0/script/client/at/db/mysql.sql

-- for AT mode you must to init this sql for you business database. the seata server not need it.
CREATE TABLE IF NOT EXISTS `undo_log`
(`branch_id`     BIGINT(20)   NOT NULL COMMENT 'branch transaction id',`xid`           VARCHAR(100) 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(11)      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 = InnoDBAUTO_INCREMENT = 1DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';

9、配置seata的server端所需要的配置信息

下载seata的server端地址:https://github.com/seata/seata/releases/tag/v1.4.0     

其配置信息参考:http://seata.io/zh-cn/docs/user/registry/nacos.html     

在其解压后修改conf文件下的   file.conf      和     registry.conf    

1.修改   file.conf    ,其中红色部分为此次修改的内容(就是将刚刚创建好的数据库):

## transaction log store, only used in seata-server  
store {
  ## store mode: file、db、redis
  mode = "db"

  ## database store property
  db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
    datasource = "druid"
    ## mysql/oracle/postgresql/h2/oceanbase etc.
    dbType = "mysql"
    driverClassName = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://127.0.0.1:3306/seata"
    user = "root"
    password = "123456"

    minConn = 5
    maxConn = 100
    globalTable = "global_table"
    branchTable = "branch_table"
    lockTable = "lock_table"
    queryLimit = 100
    maxWait = 5000
  }

}

2.修改   registry.conf    文件,其中红色部分是此次所需要修改的地方,以及与项目中配置文件的对应关系,在下方也有图片标注:

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"
  loadBalance = "RandomLoadBalance"
  loadBalanceVirtualNodes = 10

  nacos {
    application = "seata-server"
    serverAddr = "127.0.0.1:8848"
    group = "SEATA_GROUP"
    namespace = ""
    cluster = "seata-days"

# 重点说明:cluster = "seata-days"

# 其项目配置文件中的 seata.service.grouplist.seata-days=127.0.0.1:8091  ,此配置信息的  seata-days  ,需要对应此配置文件中  cluster = "seata-days"  中  的    "seata-days"   保持一致。
# 其项目配置文件中的 seata.service.vgroup-mapping.day_today_tx_group=seata-days    ,此配置信息的  seata-days ,需要对应此配置文件中 cluster = "seata-days"  中  的    "seata-days"  ,保持一致,

# 也就是 与  seata.service.grouplist.seata-days=127.0.0.1:8091  ,此配置信息的  seata-days   需要保持一致。
    username = "nacos"
    password = "nacos"

  }
   
}

此配置文件与项目的配置信息对应关系:

 

10、启动seata服务端

注意:在启动之前必须启动成功nacos1.3.0,本地则以单机模式启动nacos

启动seata服务:seata-server.bat  

在nacos(本地启动nacos命令:startup.cmd -m standalone )中注册的seata服务是:

————————至此则创建完成数据库,以及成功部署seata服务。

三、测试分布式事务是否成功

11、启动today服务和yesterday服务,并查看是否已注册

12、测试:

抛出异常:

AT事务在此得到的效果,

 

——————至此springboot集成dubbo2.7.8+nacos1.3.2+seata1.4.0  分布式事务解决方案,已实现效果。

docker中安装nacos1.3.1

参考:
1、https://www.cnblogs.com/binz/p/12295346.html
2、https://www.jianshu.com/p/e053f016371a

命令:
1.docker search nacos
2.docker pull nacos/nacos-server:1.3.1
3.docker run -d -p 8848:8848 -e MODE=standalone -v /usr/nacos/properties/custom.properties:/home/nacos/init.d/custom.properties -v /usr/nacos/logs:/home/nacos/logs --restart always --name nacos nacos/nacos-server:1.3.1


项目中使用的整合参考:

springboot集成dubbo2.7.8+nacos1.3.2

参考:   https://blog.csdn.net/lwb314/article/details/108233863

springboot整合mybatis-plus3.3.0+mybatis-plus动态数据源+druid1.1.22

参考:   https://gitee.com/log4j/pig/tree/master/pig-common/pig-common-datasource      此子工程。

 

此项目的github地址: www.github.com/qjyn1314/days    

  相关解决方案