点击左上角蓝字,关注“锅外的大佬”

专注分享国外最新技术内容

1.简介

本文主要介绍SpringBoot2.1.5 + Dubbo 2.7.3 + Mybatis 3.4.2 + Nacos 1.1.3 +Seata 0.8.0整合来实现Dubbo分布式事务管理,使用Nacos 作为 Dubbo和Seata的注册中心和配置中心,使用 MySQL 数据库和 MyBatis来操作数据。

如果你还对SpringBootDubboNacosSeataMybatis 不是很了解的话,这里我为大家整理个它们的官网网站,如下

  • SpringBoot:https://spring.io/projects/spring-boot

  • Dubbo:http://dubbo.apache.org/en-us/

  • Nacos:https://nacos.io/zh-cn/docs/quick-start.html

  • Seata:https://github.com/seata/seata/wiki/Home_Chinese

  • MyBatis:http://www.mybatis.org/mybatis-3/zh/index.html

在这里我们就不一个一个介绍它们是怎么使用和原理,详细请学习官方文档,在这里我将开始对它们进行整合,完成一个简单的案例,来让大家了解Seata来实现Dubbo分布式事务管理的基本流程。

2.环境准备

2.1 下载nacos并安装启动

nacos下载:https://github.com/alibaba/nacos/releases/tag/1.1.3

Nacos 快速入门:https://nacos.io/en-us/docs/quick-start.html

sh startup.sh -m standalone

在浏览器打开Nacos web 控制台:http://192.168.10.200:8848/nacos/index.html

输入nacos的账号和密码 分别为nacos:nacos

这zhihou 后naocs 就正常启动了。

2.2 下载seata0.8.0 并安装启动

2.2.1 在 Seata Release 下载最新版的 Seata Server 并解压得到如下目录:

.├──bin├──conf├──file_store└──lib

2.2.2 修改 conf/registry.conf 配置,

目前seata支持如下的file、nacos 、apollo、zk、consul的注册中心和配置中心。这里我们以nacos 为例。将 type 改为 nacos

registry {  # file nacos  type = "nacos"  nacos {    serverAddr = "192.168.10.200"    namespace = "public"    cluster = "default"  }  file {    name = "file.conf"  }}config {  # file、nacos 、apollo、zk、consul  type = "nacos"  nacos {    serverAddr = "192.168.10.200"    namespace = "public"    cluster = "default"  }  file {    name = "file.conf"  }}
  • serverAddr = "192.168.10.200"   :nacos 的地址

  • namespace = "public" :nacos的命名空间默认为public

  • cluster = "default"  :集群设置未默认 default

2.2.3 修改 conf/nacos-config.txt配置

transport.type=TCPtransport.server=NIOtransport.heartbeat=truetransport.thread-factory.boss-thread-prefix=NettyBosstransport.thread-factory.worker-thread-prefix=NettyServerNIOWorkertransport.thread-factory.server-executor-thread-prefix=NettyServerBizHandlertransport.thread-factory.share-boss-worker=falsetransport.thread-factory.client-selector-thread-prefix=NettyClientSelectortransport.thread-factory.client-selector-thread-size=1transport.thread-factory.client-worker-thread-prefix=NettyClientWorkerThreadtransport.thread-factory.boss-thread-size=1transport.thread-factory.worker-thread-size=8transport.shutdown.wait=3service.vgroup_mapping.order-service-seata-service-group=defaultservice.vgroup_mapping.account-service-seata-service-group=defaultservice.vgroup_mapping.storage-service-seata-service-group=defaultservice.vgroup_mapping.business-service-seata-service-group=defaultservice.enableDegrade=falseservice.disable=falseservice.max.commit.retry.timeout=-1service.max.rollback.retry.timeout=-1client.async.commit.buffer.limit=10000client.lock.retry.internal=10client.lock.retry.times=30store.mode=dbstore.file.dir=file_store/datastore.file.max-branch-session-size=16384store.file.max-global-session-size=512store.file.file-write-buffer-cache-size=16384store.file.flush-disk-mode=asyncstore.file.session.reload.read_size=100store.db.driver-class-name=com.mysql.jdbc.Driverstore.db.datasource=dbcpstore.db.db-type=mysqlstore.db.url=jdbc:mysql://192.168.10.200:3306/seata?useUnicode=truestore.db.user=lidongstore.db.password=cwj887766@@store.db.min-conn=1store.db.max-conn=3store.db.global.table=global_tablestore.db.branch.table=branch_tablestore.db.query-limit=100store.db.lock-table=lock_tablerecovery.committing-retry-period=1000recovery.asyn-committing-retry-period=1000recovery.rollbacking-retry-period=1000recovery.timeout-retry-period=1000transaction.undo.data.validation=truetransaction.undo.log.serialization=jacksontransaction.undo.log.save.days=7transaction.undo.log.delete.period=86400000transaction.undo.log.table=undo_logtransport.serialization=seatatransport.compressor=nonemetrics.enabled=falsemetrics.registry-type=compactmetrics.exporter-list=prometheusmetrics.exporter-prometheus-port=9898

这里主要修改了如下几项:

  • store.mode :存储模式 默认file  这里我修改为db 模式 ,并且需要两个表global_tablebranch_table

  • store.db.driver-class-name:默认没有,会报错。添加了 com.mysql.jdbc.Driver

  • store.db.datasource=dbcp :数据源 dbcp

  • store.db.db-type=mysql : 存储数据库的类型为mysql

  • store.db.url=jdbc:mysql://192.168.10.200:3306/seata?useUnicode=true : 修改为自己的数据库urlport数据库名称

  • store.db.user=lidong :数据库的账号

  • store.db.password=cwj887766@@ :数据库的密码

  • service.vgroup_mapping.order-service-seata-service-group=default

  • service.vgroup_mapping.account-service-seata-service-group=default

  • service.vgroup_mapping.storage-service-seata-service-group=default

  • service.vgroup_mapping.business-service-seata-service-group=default

也可以在 Nacos 配置页面添加,data-id 为 service.vgroup_mapping.${YOUR_SERVICE_NAME}-seata-service-group, group 为 SEATA_GROUP, 如果不添加该配置,启动后会提示no available server to connect

注意: 配置文件末尾有空行,需要删除,否则会提示失败,尽管实际上是成功的

global_table的表结构

CREATE TABLE `global_table` (  `xid` varchar(128) NOT NULL,  `transaction_id` bigint(20) DEFAULT NULL,  `status` tinyint(4) NOT NULL,  `application_id` varchar(64) DEFAULT NULL,  `transaction_service_group` varchar(64) DEFAULT NULL,  `transaction_name` varchar(64) DEFAULT NULL,  `timeout` int(11) DEFAULT NULL,  `begin_time` bigint(20) DEFAULT NULL,  `application_data` varchar(2000) DEFAULT NULL,  `gmt_create` datetime DEFAULT NULL,  `gmt_modified` datetime DEFAULT NULL,  PRIMARY KEY (`xid`),  KEY `idx_gmt_modified_status` (`gmt_modified`,`status`),  KEY `idx_transaction_id` (`transaction_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

branch_table的表结构

CREATE TABLE `branch_table` (  `branch_id` bigint(20) NOT NULL,  `xid` varchar(128) NOT NULL,  `transaction_id` bigint(20) DEFAULT NULL,  `resource_group_id` varchar(32) DEFAULT NULL,  `resource_id` varchar(256) DEFAULT NULL,  `lock_key` varchar(128) DEFAULT NULL,  `branch_type` varchar(8) DEFAULT NULL,  `status` tinyint(4) DEFAULT NULL,  `client_id` varchar(64) DEFAULT NULL,  `application_data` varchar(2000) DEFAULT NULL,  `gmt_create` datetime DEFAULT NULL,  `gmt_modified` datetime DEFAULT NULL,  PRIMARY KEY (`branch_id`),  KEY `idx_xid` (`xid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2.2.4 将 Seata 配置添加到 Nacos 中

使用命令如下

cd confsh nacos-config.sh localhost

成功后会提示

init nacos config finished, please start seata-server

在 Nacos 管理页面应该可以看到有 59 个 Group 为SEATA_GROUP的配置

在这里插入图片描述

2.2.5 启动 Seata Server

使用db 模式启动

 cd .. sh ./bin/seata-server.sh

这时候在 Nacos 的服务列表下面可以看到一个名为serverAddr的服务

3 案例分析

参考官网中用户购买商品的业务逻辑。整个业务逻辑由4个微服务提供支持:

  • 库存服务:扣除给定商品的存储数量。

  • 订单服务:根据购买请求创建订单。

  • 帐户服务:借记用户帐户的余额。

  • 业务服务:处理业务逻辑。

请求逻辑架构

3.1  github地址

springboot-dubbo-seata:https://github.com/lidong1665/springboot-dubbo-seata

  • samples-common :公共模块

  • samples-account :用户账号模块

  • samples-order :订单模块

  • samples-storage :库存模块

  • samples-business :业务模块

3.2 账户服务:AccountDubboService

/** * @Author: lidong * @Description  账户服务接口 * @Date Created in 2019/9/5 16:37 */public interface AccountDubboService {    /**     * 从账户扣钱     */    ObjectResponse decreaseAccount(AccountDTO accountDTO);}

3.3 订单服务:OrderDubboService

/** * @Author: lidong * @Description  订单服务接口 * @Date Created in 2019/9/5 16:28 */public interface OrderDubboService {    /**     * 创建订单     */    ObjectResponse createOrder(OrderDTO orderDTO);}

3.4  库存服务:StorageDubboService

/** * @Author: lidong * @Description  库存服务 * @Date Created in 2019/9/5 16:22 */public interface StorageDubboService {    /**     * 扣减库存     */    ObjectResponse decreaseStorage(CommodityDTO commodityDTO);}

3.5 业务服务:BusinessService

/** * @Author: lidong * @Description * @Date Created in 2019/9/5 17:17 */public interface BusinessService {    /**     *  处理业务服务     * @param businessDTO     * @return     */    ObjectResponse handleBusiness(BusinessDTO businessDTO);}

业务逻辑的具体实现主要体现在 订单服务的实现和业务服务的实现

订单服务的实现

@Servicepublic class TOrderServiceImpl extends ServiceImpl implements ITOrderService {    @Reference(version = "1.0.0")    private AccountDubboService accountDubboService;    /**     * 创建订单     * @Param:  OrderDTO  订单对象     * @Return:  OrderDTO  订单对象     */    @Override    public ObjectResponse createOrder(OrderDTO orderDTO) {        ObjectResponse response = new ObjectResponse<>();        //扣减用户账户        AccountDTO accountDTO = new AccountDTO();        accountDTO.setUserId(orderDTO.getUserId());        accountDTO.setAmount(orderDTO.getOrderAmount());        ObjectResponse objectResponse = accountDubboService.decreaseAccount(accountDTO);        //生成订单号        orderDTO.setOrderNo(UUID.randomUUID().toString().replace("-",""));        //生成订单        TOrder tOrder = new TOrder();        BeanUtils.copyProperties(orderDTO,tOrder);        tOrder.setCount(orderDTO.getOrderCount());        tOrder.setAmount(orderDTO.getOrderAmount().doubleValue());        try {            baseMapper.createOrder(tOrder);        } catch (Exception e) {            response.setStatus(RspStatusEnum.FAIL.getCode());            response.setMessage(RspStatusEnum.FAIL.getMessage());            return response;        }        if (objectResponse.getStatus() != 200) {            response.setStatus(RspStatusEnum.FAIL.getCode());            response.setMessage(RspStatusEnum.FAIL.getMessage());            return response;        }        response.setStatus(RspStatusEnum.SUCCESS.getCode());        response.setMessage(RspStatusEnum.SUCCESS.getMessage());        return response;    }}

整个业务的实现逻辑

@Service@Slf4jpublic class BusinessServiceImpl implements BusinessService{    @Reference(version = "1.0.0")    private StorageDubboService storageDubboService;    @Reference(version = "1.0.0")    private OrderDubboService orderDubboService;    private boolean flag;    /**     * 处理业务逻辑     * @Param:     * @Return:     */    @GlobalTransactional(timeoutMills = 300000, name = "dubbo-gts-seata-example")    @Override    public ObjectResponse handleBusiness(BusinessDTO businessDTO) {        log.info("开始全局事务,XID = " + RootContext.getXID());        ObjectResponse objectResponse = new ObjectResponse<>();        //1、扣减库存        CommodityDTO commodityDTO = new CommodityDTO();        commodityDTO.setCommodityCode(businessDTO.getCommodityCode());        commodityDTO.setCount(businessDTO.getCount());        ObjectResponse storageResponse = storageDubboService.decreaseStorage(commodityDTO);        //2、创建订单        OrderDTO orderDTO = new OrderDTO();        orderDTO.setUserId(businessDTO.getUserId());        orderDTO.setCommodityCode(businessDTO.getCommodityCode());        orderDTO.setOrderCount(businessDTO.getCount());        orderDTO.setOrderAmount(businessDTO.getAmount());        ObjectResponse response = orderDubboService.createOrder(orderDTO);        //打开注释测试事务发生异常后,全局回滚功能//        if (!flag) {//            throw new RuntimeException("测试抛异常后,分布式事务回滚!");//        }        if (storageResponse.getStatus() != 200 || response.getStatus() != 200) {            throw new DefaultException(RspStatusEnum.FAIL);        }        objectResponse.setStatus(RspStatusEnum.SUCCESS.getCode());        objectResponse.setMessage(RspStatusEnum.SUCCESS.getMessage());        objectResponse.setData(response.getData());        return objectResponse;    }}

3.6 使用seata的分布式事务解决方案处理dubbo的分布式事务

在这里插入图片描述

我们只需要在业务处理的方法handleBusiness添加一个注解 @GlobalTransactional

@GlobalTransactional(timeoutMills = 300000, name = "dubbo-gts-seata-example")    @Override    public ObjectResponse handleBusiness(BusinessDTO businessDTO) {        }
  • timeoutMills: 超时时间

  • name :事务名称

3.7 准备数据库

注意: MySQL必须使用InnoDB engine.

创建数据库  并导入数据库脚本

DROP DATABASE IF EXISTS seata;CREATE DATABASE seata;USE seata;DROP TABLE IF EXISTS `t_account`;CREATE TABLE `t_account` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `user_id` varchar(255) DEFAULT NULL,  `amount` double(14,2) DEFAULT '0.00',  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;-- ------------------------------ Records of t_account-- ----------------------------INSERT INTO `t_account` VALUES ('1', '1', '4000.00');-- ------------------------------ 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) DEFAULT NULL,  `user_id` varchar(255) DEFAULT NULL,  `commodity_code` varchar(255) DEFAULT NULL,  `count` int(11) DEFAULT '0',  `amount` double(14,2) DEFAULT '0.00',  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=64 DEFAULT CHARSET=utf8;-- ------------------------------ Records of t_order-- ------------------------------ ------------------------------ Table structure for t_storage-- ----------------------------DROP TABLE IF EXISTS `t_storage`;CREATE TABLE `t_storage` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `commodity_code` varchar(255) DEFAULT NULL,  `name` varchar(255) DEFAULT NULL,  `count` int(11) DEFAULT '0',  PRIMARY KEY (`id`),  UNIQUE KEY `commodity_code` (`commodity_code`)) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;-- ------------------------------ Records of t_storage-- ----------------------------INSERT INTO `t_storage` VALUES ('1', 'C201901140001', '水杯', '1000');-- ------------------------------ Table structure for undo_log-- 注意此处0.3.0+ 增加唯一索引 ux_undo_log-- ----------------------------DROP TABLE IF EXISTS `undo_log`;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;-- ------------------------------ Records of undo_log-- ----------------------------SET FOREIGN_KEY_CHECKS=1;

会看到如上的4个表

+-------------------------+| Tables_in_seata         |+-------------------------+| t_account               || t_order                 || t_storage               || undo_log                |+-------------------------+

这里为了简化我将这个三张表创建到一个库中,使用三个数据源来实现。

3.8 我们以账号服务samples-account为例 ,分析需要注意的配置项目

3.8.1 引入的依赖

<?xml version="1.0" encoding="UTF-8"?>         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    4.0.0            org.springframework.boot        spring-boot-starter-parent        2.1.5.RELEASE                springboot-dubbo-seata    pom    springboot-dubbo-seata    io.seata    1.0.0    samples-account            2.1.5.RELEASE        1.8        1.1.10        1.3.2        2.3        0.2.3        1.16.22        2.7.3        1.1.3        0.8.0        4.1.32.Final                            org.springframework.boot            spring-boot-starter-web            ${springboot.verison}                            org.springframework.boot            spring-boot-starter            ${springboot.verison}                            org.springframework.boot            spring-boot-starter-test            ${springboot.verison}                            com.alibaba            druid            ${druid.version}                            org.mybatis.spring.boot            mybatis-spring-boot-starter            ${mybatis.version}                            com.baomidou            mybatis-plus            ${mybatis-plus.version}                            org.apache.dubbo            dubbo            ${dubbo.version}                                                spring                    org.springframework                                                        org.apache.dubbo            dubbo-spring-boot-starter            ${dubbo.version}                                    org.apache.dubbo            dubbo-config-spring            ${dubbo.version}                            org.apache.dubbo            dubbo-registry-nacos            ${dubbo.version}                                    io.seata            seata-all            ${seata.version}                            com.alibaba.nacos            nacos-client            ${nacos-client.verison}                                    com.alibaba.boot            nacos-config-spring-boot-starter            ${nacos.version}                                                nacos-client                    com.alibaba.nacos                                                        org.springframework.boot            spring-boot-maven-plugin            ${springboot.verison}                            org.projectlombok            lombok            ${lombok.version}                            io.netty            netty-all            ${netty.version}                            com.alibaba.spring            spring-context-support            1.0.2                            org.apache.httpcomponents            httpclient            4.5                            mysql            mysql-connector-java            5.1.47                                                    org.springframework.boot                spring-boot-maven-plugin                                        org.apache.maven.plugins                maven-deploy-plugin                                    true                                                        org.apache.maven.plugins                maven-compiler-plugin                                    ${java.version}                    ${java.version}                                        

注意:

  • seata-all: 这个是seata 所需的主要依赖

  • dubbo-spring-boot-starter:   springboot dubbo的依赖

其他的就不一一介绍,其他的一目了然,就知道是干什么的。

3.8.2  application.properties配置

server.port=8102spring.application.name=dubbo-account-example#====================================Dubbo配置===============================================dubbo.application.id= dubbo-account-exampledubbo.application.name= dubbo-account-exampledubbo.protocol.id=dubbodubbo.protocol.name=dubbodubbo.registry.id=dubbo-account-example-registrydubbo.registry.address=nacos://192.168.10.200:8848dubbo.protocol.port=20880dubbo.application.qosEnable=falsedubbo.config-center.address=nacos://192.168.10.200:8848dubbo.metadata-report.address=nacos://192.168.10.200:8848#====================================mysql 配置============================================spring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.datasource.url=jdbc:mysql://192.168.10.200:3306/seata?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=truespring.datasource.username=lidongspring.datasource.password=cwj887766@@#=====================================mybatis 配置======================================mybatis.mapper-locations=classpath*:/mapper/*.xml

3.8.3 registry.conf 配置 (naocs的配置)

registry {  # file nacos  type = "nacos"  nacos {    serverAddr = "192.168.10.200"    namespace = "public"    cluster = "default"  }  file {    name = "file.conf"  }}config {  # file、nacos 、apollo、zk、consul  type = "nacos"  file {    name = "file.conf"  }  nacos {    serverAddr = "192.168.10.200"    namespace = "public"    cluster = "default"  }}

3.8.5 SeataAutoConfig 配置

package io.seata.samples.integration.account.config;import com.alibaba.druid.pool.DruidDataSource;import io.seata.rm.datasource.DataSourceProxy;import io.seata.spring.annotation.GlobalTransactionScanner;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;import org.mybatis.spring.SqlSessionFactoryBean;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Primary;import org.springframework.core.io.support.PathMatchingResourcePatternResolver;/** * @Author: llidong * @Description  seata global configuration * @Date Created in 2019/9/05 10:28 */@Configurationpublic class SeataAutoConfig {    /**     * autowired datasource config     */    @Autowired    private DataSourceProperties dataSourceProperties;    /**     * init durid datasource     *     * @Return: druidDataSource  datasource instance     */    @Bean    @Primary    public DruidDataSource druidDataSource(){        DruidDataSource druidDataSource = new DruidDataSource();        druidDataSource.setUrl(dataSourceProperties.getUrl());        druidDataSource.setUsername(dataSourceProperties.getUsername());        druidDataSource.setPassword(dataSourceProperties.getPassword());        druidDataSource.setDriverClassName(dataSourceProperties.getDriverClassName());        druidDataSource.setInitialSize(0);        druidDataSource.setMaxActive(180);        druidDataSource.setMaxWait(60000);        druidDataSource.setMinIdle(0);        druidDataSource.setValidationQuery("Select 1 from DUAL");        druidDataSource.setTestOnBorrow(false);        druidDataSource.setTestOnReturn(false);        druidDataSource.setTestWhileIdle(true);        druidDataSource.setTimeBetweenEvictionRunsMillis(60000);        druidDataSource.setMinEvictableIdleTimeMillis(25200000);        druidDataSource.setRemoveAbandoned(true);        druidDataSource.setRemoveAbandonedTimeout(1800);        druidDataSource.setLogAbandoned(true);        return druidDataSource;    }    /**     * init datasource proxy     * @Param: druidDataSource  datasource bean instance     * @Return: DataSourceProxy  datasource proxy     */    @Bean    public DataSourceProxy dataSourceProxy(DruidDataSource druidDataSource){        return new DataSourceProxy(druidDataSource);    }    /**     * init mybatis sqlSessionFactory     * @Param: dataSourceProxy  datasource proxy     * @Return: DataSourceProxy  datasource proxy     */    @Bean    public SqlSessionFactory sqlSessionFactory(DataSourceProxy dataSourceProxy) throws Exception {        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();        factoryBean.setDataSource(dataSourceProxy);        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()                .getResources("classpath*:/mapper/*.xml"));        return factoryBean.getObject();    }    /**     * init global transaction scanner     *     * @Return: GlobalTransactionScanner     */    @Bean    public GlobalTransactionScanner globalTransactionScanner(){        return new GlobalTransactionScanner("account-gts-seata-example", "account-service-seata-service-group");    }}

其中:

@Bean    public GlobalTransactionScanner globalTransactionScanner(){        return new GlobalTransactionScanner("account-gts-seata-example", "account-service-seata-service-group");    }

GlobalTransactionScanner: 初始化全局的事务扫描器

 /**     * Instantiates a new Global transaction scanner.     *     * @param applicationId  the application id     * @param txServiceGroup the default server group     */    public GlobalTransactionScanner(String applicationId, String txServiceGroup) {        this(applicationId, txServiceGroup, DEFAULT_MODE);    }
  • applicationId :为应用id 这里我传入的是account-gts-seata-example

  • txServiceGroup: 默认server的分组 这里我传入的是account-service-seata-service-group 这个和我们前面在nacos 配置的是保存一致。

  • DEFAULT_MODE:默认的事务模式 为AT_MODE + MT_MODE

3.8.6 AccountExampleApplication 启动类的配置

package io.seata.samples.integration.account;import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.boot.context.config.ConfigFileApplicationListener;@SpringBootApplication(scanBasePackages = "io.seata.samples.integration.account")@MapperScan({"io.seata.samples.integration.account.mapper"})@EnableDubbo(scanBasePackages = "io.seata.samples.integration.account")public class AccountExampleApplication {    public static void main(String[] args) {        SpringApplication.run(AccountExampleApplication.class, args);    }}
  • @EnableDubbo等同于 @DubboComponentScan@EnableDubboConfig组合

  • @DubboComponentScan 扫描 classpaths 下类中添加了 @Service@Reference 将自动注入到spring beans中。

  • @EnableDubboConfig 扫描的dubbo的外部化配置。

4 启动所有的sample模块

启动 samples-accountsamples-ordersamples-storagesamples-business

并且在nocos的控制台查看注册情况: http://192.168.10.200:8848/nacos/#/serviceManagement

在这里插入图片描述

我们可以看到上面的服务都已经注册成功。

5 测试

5. 1 发送一个下单请求

使用postman 发送 :http://localhost:8104/business/dubbo/buy

参数:

{    "userId":"1",    "commodityCode":"C201901140001",    "name":"fan",    "count":50,    "amount":"100"}

返回

{    "status": 200,    "message": "成功",    "data": null}

这时候控制台:

2019-09-05 12:17:34.097  INFO 21860 --- [nio-8104-exec-4] i.s.s.i.c.controller.BusinessController  : 请求参数:BusinessDTO(userId=1, commodityCode=C201901140001, name=fan, count=50, amount=100)2019-09-05 12:17:34.146  INFO 21860 --- [nio-8104-exec-4] i.seata.tm.api.DefaultGlobalTransaction  : Begin new global transaction [192.168.10.200:8091:2021380638]2019-09-05 12:17:34.150  INFO 21860 --- [nio-8104-exec-4] i.s.s.i.c.service.BusinessServiceImpl    : 开始全局事务,XID = 192.168.10.200:8091:20213806382019-09-05 12:17:36.966  INFO 21860 --- [nio-8104-exec-4] i.seata.tm.api.DefaultGlobalTransaction  : [192.168.10.200:8091:2021380638] commit status:Committed

事务提交成功,

我们来看一下数据库数据变化

t_accountt_order

t_storage

数据没有问题。

5.2 测试回滚

我们samples-businessBusinessServiceImplhandleBusiness2 下面的代码去掉注释

if (!flag) {  throw new RuntimeException("测试抛异常后,分布式事务回滚!");}

使用postman 发送 :http://localhost:8104/business/dubbo/buy2

.响应结果:

{    "timestamp": "2019-09-05T04:29:34.178+0000",    "status": 500,    "error": "Internal Server Error",    "message": "测试抛异常后,分布式事务回滚!",    "path": "/business/dubbo/buy"}

控制台

2019-09-05 12:29:32.836  INFO 17264 --- [nio-8104-exec-2] i.s.s.i.c.controller.BusinessController  : 请求参数:BusinessDTO(userId=1, commodityCode=C201901140001, name=fan, count=50, amount=100)2019-09-05 12:29:32.843  INFO 17264 --- [nio-8104-exec-2] i.s.common.loader.EnhancedServiceLoader  : load ContextCore[null] extension by class[io.seata.core.context.ThreadLocalContextCore]2019-09-05 12:29:32.848  INFO 17264 --- [nio-8104-exec-2] i.s.common.loader.EnhancedServiceLoader  : load TransactionManager[null] extension by class[io.seata.tm.DefaultTransactionManager]2019-09-05 12:29:32.849  INFO 17264 --- [nio-8104-exec-2] io.seata.tm.TransactionManagerHolder     : TransactionManager Singleton io.seata.tm.DefaultTransactionManager@461585ac2019-09-05 12:29:32.859  INFO 17264 --- [nio-8104-exec-2] i.s.common.loader.EnhancedServiceLoader  : load LoadBalance[null] extension by class[io.seata.discovery.loadbalance.RandomLoadBalance]2019-09-05 12:29:32.893  INFO 17264 --- [nio-8104-exec-2] i.seata.tm.api.DefaultGlobalTransaction  : Begin new global transaction [192.168.10.200:8091:2021380674]2019-09-05 12:29:32.897  INFO 17264 --- [nio-8104-exec-2] i.s.s.i.c.service.BusinessServiceImpl    : 开始全局事务,XID = 192.168.10.200:8091:20213806742019-09-05 12:29:34.143  INFO 17264 --- [nio-8104-exec-2] i.seata.tm.api.DefaultGlobalTransaction  : [192.168.10.200:8091:2021380674] rollback status:Rollbacked2019-09-05 12:29:34.158 ERROR 17264 --- [nio-8104-exec-2] 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.RuntimeException: 测试抛异常后,分布式事务回滚!] with root causejava.lang.RuntimeException: 测试抛异常后,分布式事务回滚! at io.seata.samples.integration.call.service.BusinessServiceImpl.handleBusiness(BusinessServiceImpl.java:60) ~[classes/:na] at io.seata.samples.integration.call.service.BusinessServiceImpl$$FastClassBySpringCGLIB$$2ab3d645.invoke() ~[classes/:na]  at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.1.7.RELEASE.jar:5.1.7.RELEASE]  at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]  at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]  at io.seata.spring.annotation.GlobalTransactionalInterceptor$1.execute(GlobalTransactionalInterceptor.java:104) ~[seata-all-0.8.0.jar:0.8.0]    at io.seata.tm.api.TransactionalTemplate.execute(TransactionalTemplate.java:64) ~[seata-all-0.8.0.jar:0.8.0]    at io.seata.spring.annotation.GlobalTransactionalInterceptor.handleGlobalTransaction(GlobalTransactionalInterceptor.java:101) ~[seata-all-0.8.0.jar:0.8.0]  at io.seata.spring.annotation.GlobalTransactionalInterceptor.invoke(GlobalTransactionalInterceptor.java:76) ~[seata-all-0.8.0.jar:0.8.0]    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]  at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]    at io.seata.samples.integration.call.service.BusinessServiceImpl$$EnhancerBySpringCGLIB$$cb43f7ab.handleBusiness() ~[classes/:na]   at io.seata.samples.integration.call.controller.BusinessController.handleBusiness(BusinessController.java:37) ~[classes/:na]    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_144]  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_144]    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_144]    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_144] at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]   at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1039) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]   at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE] at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE] at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]  at javax.servlet.http.HttpServlet.service(HttpServlet.java:660) ~[tomcat-embed-core-9.0.19.jar:9.0.19]  at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.19.jar:9.0.19]  at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.19.jar:9.0.19] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.19.jar:9.0.19] at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.19.jar:9.0.19]   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.19.jar:9.0.19] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.19.jar:9.0.19] at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.19.jar:9.0.19] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.19.jar:9.0.19] at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]   at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.19.jar:9.0.19] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.19.jar:9.0.19] at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.19.jar:9.0.19] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.19.jar:9.0.19] at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]  at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.19.jar:9.0.19] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.19.jar:9.0.19] at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:200) ~[tomcat-embed-core-9.0.19.jar:9.0.19]   at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.19.jar:9.0.19] at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490) [tomcat-embed-core-9.0.19.jar:9.0.19] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.19.jar:9.0.19]  at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.19.jar:9.0.19]   at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.19.jar:9.0.19]   at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.19.jar:9.0.19]    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) [tomcat-embed-core-9.0.19.jar:9.0.19] at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-9.0.19.jar:9.0.19]   at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:836) [tomcat-embed-core-9.0.19.jar:9.0.19]    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1747) [tomcat-embed-core-9.0.19.jar:9.0.19]    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.19.jar:9.0.19]    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_144]   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_144]   at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.19.jar:9.0.19] at java.lang.Thread.run(Thread.java:748) [na:1.8.0_144]

我们查看数据库数据,已经回滚,和上面的数据一致。

到这里一个简单的案例基本就分析结束。感谢你的学习。

● Spring Boot 默认的指标数据从哪来的?

● Spring WebClient vs. RestTemplate

● Sentinel 网关流量控制之Spring Cloud Gateway实战

右上角按钮分享给更多人哦~

来都来了,点个在看再走吧~~~

c++ 使用nacos_使用SpringBoot和Seata实现Dubbo分布式事务管理相关推荐

  1. SpringBoot+Nacos+Seata实现Dubbo分布式事务管理

    SpringBoot+Nacos+Seata实现Dubbo分布式事务管理 https://www.shangmayuan.com/a/a3ba056126ba45db9b8dfd5b.html 源码下 ...

  2. SpringBoot+Zookeeper+Seata实现Dubbo分布式事务管理

    面我已经写过一篇SpringBoot+Nacos+Seata实现Dubbo分布式事务管理的文章,今天为什么还要写这篇呢,是因为好多公司还在用Zookeeper作为Dubbo的注册中心和配置中心在大规模 ...

  3. 老板,明年我用Seata搞定分布式事务管理的规范化建设 | 中篇

    辞旧迎新,22年要结束了,明年做什么想好了嘛?要不要用 Seata 搞定公司分布式事务管理的规范化建设? 欢迎关注微信公众号「架构染色」交流和学习 一.背景 在上一篇<明年用Seata搞定分布式 ...

  4. Springboot+nacos+seata实现简单分布式事务经验分享:二

    Springboot+nacos+seata实现简单的分布式事务 上一篇文章把三个服务都注册进nacos中了,这次就开始写业务代码 首先先创建三个数据库,每个数据库都需要有一张记录表,一张回滚表 or ...

  5. SpringBoot+ Dubbo + Mybatis + Nacos +Seata整合来实现Dubbo分布式事务

    作者:请叫我东子 https://blog.csdn.net/u010046908/article/details/100536439 1.简介"本文主要介绍SpringBoot2.1.5 ...

  6. druid seata 配置_分布式事务解决方案——Seata使用

    在微服务开发过程中分布式事务一直是一个比较重要的问题,之前对于分布式事务的解决方法一般会通过MQ的最终一致性来解决,尤其是RocketMQ的事务消息,感兴趣的可以看Spring Boot整合Rocke ...

  7. Spring Cloud Alibaba 高级特性 分布式事务:Alibaba Seata 如何实现分布式事务

    本讲咱们要解决分布式事务这一技术难题,这一讲咱们将介绍三方面内容: 讲解分布式事务的解决方案: 介绍 Alibaba Seata 分布式事务中间件: 分析 Seata 的 AT 模式实现原理. 分布式 ...

  8. Spring-Boot + Atomikos 实现跨库的分布式事务管理

    一.何为事务 定义:事务是指多个操作单元组成的合集,多个单元操作是整体不可分割的,要么都操作成功,要么都不成功. 其必须遵循的四个原则(ACID): 原子性(Atomicity -- 美 [ˌætəˈ ...

  9. 3分钟搞定SpringBoot+Mybatis+druid多数据源和分布式事务

    在一些复杂的应用开发中,一个应用可能会涉及到连接多个数据源,所谓多数据源这里就定义为至少连接两个及以上的数据库了. 下面列举两种常用的场景: 一种是读写分离的数据源,例如一个读库和一个写库,读库负责各 ...

  10. 一文详解 SpringBoot 多数据源中的分布式事务

    作者 | 小涛 责编 | maozz 虽然现在微服务越来越流行,我们的系统随之也拆分出来好多的模块功能.这样做的目的其实就是为了弥补单体架构中存在的不足. 随着微服务的拆分,肯定设计到分库分表,但这之 ...

最新文章

  1. CodeGen用户定义的扩展令牌
  2. 我花了三个小时写了一道题的六千字题解....(POJ 2888 Magic Bracelet)
  3. Spring Boot加载配置文件
  4. I/0口输入输出实验 将P1口的某一位作为输入使用,连接一个按键,当按键按下时使发光二极管亮,否则发光二极管熄灭
  5. IMU-Allan方差分析
  6. 决策树算法小结(一) ID3原理及代码实现
  7. 一个奇怪的MySQL慢查询,打懵了一群不懂业务的DBA!
  8. 删除已配对的View Security Server、View Replica Server
  9. EP主机分销PHP源码,最新EP主机分销全解密源码+详细搭建教程
  10. 北疆游记 - 照片在左边相册
  11. 计算机键盘上的句号键在哪,电脑键盘句号是哪个键
  12. html2pdf无法导出图片解决方案(2020版)
  13. java数青蛙_LeetCode 1419. 数青蛙
  14. iOS GUI 规范
  15. ​Spring Cloud中统一异常处理是怎么做的?
  16. SVG 矩形rect 学习
  17. 8.14.2. Designing JSON documents
  18. 网站dns服务器不能用,Win7网络诊断“DNS服务器可能不可用”怎么解决?
  19. 如何打造一个抗住千万级流量短信服务(续)
  20. Android 10开发之 保存、读取图片

热门文章

  1. react navigation传值给上一页面
  2. python 2.7版本解决TypeError: 'encoding' is an invalid keyword argument for this function
  3. 201521123033《Java程序设计》第3周学习总结
  4. idea 根据数据库表自动创建持久化类
  5. 【是程序猿就干了这碗心灵鸡汤】我们这一代人的困惑
  6. HTML5学习笔记简明版(1):HTML5介绍与语法
  7. 实习技术员的基本功(十)
  8. RocketMQ消费端消息回退(消费重试)机制源码解析
  9. WINDOWS API 函数(超长,值得学习)
  10. Java多线程之FutureTask