栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring Cloud Alibaba +Nacos+Seata 分布式事务案例

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring Cloud Alibaba +Nacos+Seata 分布式事务案例

组件及版本

Spring Cloud:Greenwich.RELEASE

Spring Cloud Alibaba:2.1.0.RELEASE

Spring boot: 2.1.3.RELEASE

Mybatis-Plus: 3.4.2

Nacos:2.2.0 官网 https://nacos.io/zh-cn/index.html

seata:0.9.0 官网 http://seata.io/zh-cn/index.html

Swagger:2.9.2

安装Nacos 下载
下载地址: https://github.com/alibaba/nacos/releases

下载zip格式的安装包,然后进行解压缩操作

启动
#切换目录
cd nacos/bin
#命令启动
startup.cmd -m standalone
 访问nacos

 

打开浏览器输入 http://localhost:8848/nacos ,即可访问服务, 默认密码是 nacos/nacos 

注意刚进入事是没有这些配置的(我是搭建完后截的图,这些配置后边会提到)

搭建微服务项目

项目结构:

 一共两个微服务,order通过product-service-client 提供的feign 接口调用 product 中的服务

公司项目一般也是这种结构被调用的服务提供一个客户端项目依赖,调用方依赖这个客户端项目实现服务调用。

父项目pom.xml



    4.0.0

    com.xuweichao
    springcloudalibaba-seata
    pom
    1.0-SNAPSHOT
    
        order-service
        product-service
    

    springcloudalibaba-seata
    
    http://www.example.com

    
    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.3.RELEASE
    


    
    
        1.8
        UTF-8
        UTF-8
        Greenwich.RELEASE
        2.1.0.RELEASE
    
    
        
            cn.hutool
            hutool-all
            5.7.7
        
        
            org.projectlombok
            lombok
            1.18.2
        

        
            io.springfox
            springfox-swagger2
            2.9.2
        
        
            io.springfox
            springfox-swagger-ui
            2.9.2
        

    
    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import
            
            
                com.alibaba.cloud
                spring-cloud-alibaba-dependencies
                ${spring-cloud-alibaba.version}
                pom
                import
            
        
    

order-service 微服务pom.xml


    
        springcloudalibaba-seata
        com.xuweichao
        1.0-SNAPSHOT
    
    4.0.0
    order-service

    
        
            com.xuweichao
            product-service-client
            1.0-SNAPSHOT
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            mysql
            mysql-connector-java
            5.1.6
        
        
            com.baomidou
            mybatis-plus-boot-starter
            3.4.2
        
        
            com.baomidou
            mybatis-plus-extension
            3.3.0
            compile
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-config
        
        
            com.alibaba.cloud
            spring-cloud-alibaba-nacos-discovery
        
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-seata
        
    
    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                
                    1.8
                    1.8
                
            
        
        
            
                src/main/java
                
                    ***.xml
                
            

        
    

product-service-client pom.xml


    
        product-service
        com.xuweichao
        1.0-SNAPSHOT
    
    4.0.0

    com.xuweichao
    product-service-client
    jar
    
        
            com.baomidou
            mybatis-plus-annotation
            3.3.0
            compile
        
        
            com.baomidou
            mybatis-plus-extension
            3.3.0
            compile
        
        
            org.springframework.cloud
            spring-cloud-starter-openfeign
        
    



数据库建表:订单表 产品表

CREATE TABLE `order_base` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(10) DEFAULT NULL,
  `p_id` bigint(20) DEFAULT NULL,
  `p_name` varchar(50) DEFAULT NULL,
  `p_price` double(10,2) DEFAULT NULL,
  `number` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8



CREATE TABLE `product_base` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) DEFAULT NULL,
  `price` double(10,2) DEFAULT NULL,
  `stock` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4

使用 代码生成工具生成数据库试题对象和 service

order-service 微服务 添加配置 application.yml

spring:
  application:
    name: order-service
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    password: xxx
    url: jdbc:mysql://xxx.xxx.xxx.xxx:3306/database-name?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
    username: root

server:
  port: 18080
swagger:
  enable: true
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

product-service 微服务 添加配置 application.yml

spring:
  application:
    name: product-service
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    password: xxx
    url: jdbc:mysql://xxx.xxx.xxx.xxx:3306/database-name?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
    username: root

server:
  port: 18080
swagger:
  enable: true
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

product-service-server 添加接口:

@Slf4j
@Api(value = "Productbase 相关接口", tags = "Productbase 相关接口")
@RestController
@RequestMapping("product")
public class ProductbaseController implements ProductServiceClient {
    @Autowired
    public IProductbaseService productbaseService;

    @Override
    @ApiOperation(value = "详情")
    @GetMapping("detail/{id}")
    public Productbase  getProductbase(@PathVariable Long id) {
        log.info("获取的参数:===>>" + id);
        return productbaseService.getById(id);
    }

    @Override
    @ApiOperation(value = "修改库存")
    @GetMapping("{id}")
    public Boolean productUpdateStock(@PathVariable Long id) {
        log.info("获取的参数:===>>" + id);
        Productbase productbase = productbaseService.getById(id);
        productbase.setStock(productbase.getStock()-1);
        return productbaseService.updateById(productbase);
    }

}

在product-service-client 添加 Feign 服务接口

@Component
@FeignClient(name = "product-service",path = "/product/")
public interface ProductServiceClient {
    @GetMapping("detail/{id}")
    Productbase getProductbase(@PathVariable("id") Long id);

    @GetMapping("/{id}")
    Boolean productUpdateStock(@PathVariable("id") Long id);

}

order-service 中创建接口

@Slf4j
@Api(value = "Orderbase 相关接口", tags = "Orderbase 相关接口")
@RestController
@RequestMapping("order")
public class OrderbaseController {
    @Resource
    public IOrderbaseService orderbaseService;

    @ApiOperation(value = "添加", notes = "添加")
    @GetMapping("{pid}")
    public Boolean orderbaseSave(@PathVariable Long pid) {
        log.info("获取的参数:===>>" + pid);
        return orderbaseService.createOrder(pid);
    }
}

OrderServiceImpl:

@Service
public class OrderbaseServiceImpl extends ServiceImpl implements IOrderbaseService {
    @Autowired
    private ProductServiceClient productServiceClient;

  
    @Override
    public Boolean createOrder(Long pid) {
        Productbase productbase = productServiceClient.getProductbase(pid);
        Orderbase orderbase=new Orderbase();
        orderbase.setNumber(2);
        orderbase.setPId(productbase.getId());
        orderbase.setPName(productbase.getName());
        orderbase.setPPrice(productbase.getPrice());
        orderbase.setUserName("超超");
        this.save(orderbase);
       
        productServiceClient.productUpdateStock(orderbase.getPId());
        return Boolean.TRUE;
    }
}

这里是模拟的创建订单和减库存的操作

安装Seata 下载
下载地址: https://github.com/seata/seata/releases/v0.9.0/

进入conf 目录修改配置文件register.conf,我们这里是用nacos 作为注册和配置中心的,所以精简一下配置

registry {
  
  type = "nacos"
  nacos {
    serverAddr = "localhost"
    namespace = "public"
    cluster = "default"
  }
}
config {
  type = "nacos"
  nacos {
    serverAddr = "localhost"
    namespace = "public"
	cluster = "default"
  }
}

初始Seata配置

进入 seata-0.9.0serversrcmainresources 目录 找到 nacos-config.txt 文件

 第一个箭头哪里代表事务组

这里的语法为: service.vgroup_mapping.${your - service - gruop}=default ,中间的 ${your - service - gruop} 为自己定义的服务组名称, 这里需要我们在程序的配置文件中配置。

store 那里改成自己的数据源配置,并建表

-- the table to store GlobalSession data
DROP TABLE IF EXISTS `global_table`;
CREATE TABLE `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`)
);

-- the table to store BranchSession data
DROP TABLE IF EXISTS `branch_table`;
CREATE TABLE `branch_table` (
  `branch_id` BIGINT NOT NULL,
  `xid` VARCHAr(128) NOT NULL,
  `transaction_id` BIGINT ,
  `resource_group_id` VARCHAr(32),
  `resource_id` VARCHAr(256) ,
  `lock_key` VARCHAr(128) ,
  `branch_type` VARCHAr(8) ,
  `status` TINYINT,
  `client_id` VARCHAr(64),
  `application_data` VARCHAr(2000),
  `gmt_create` DATETIME,
  `gmt_modified` DATETIME,
  PRIMARY KEY (`branch_id`),
  KEY `idx_xid` (`xid`)
);

-- the table to store lock data
DROP TABLE IF EXISTS `lock_table`;
CREATE TABLE `lock_table` (
  `row_key` VARCHAr(128) NOT NULL,
  `xid` VARCHAr(96),
  `transaction_id` LONG ,
  `branch_id` LONG,
  `resource_id` VARCHAr(256) ,
  `table_name` VARCHAr(32) ,
  `pk` VARCHAr(36) ,
  `gmt_create` DATETIME ,
  `gmt_modified` DATETIME,
  PRIMARY KEY(`row_key`)
);

创建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=3 DEFAULT CHARSET=utf8

# 初始化 seata 的 nacos 配置 # 注意 : 这里要保证 nacos 是已经正常运行的
cd conf
nacos-config.sh 127.0.0.1

执行之后 在nacos 中就能看上最上边图中展示的效果了。

启动seata,进入 seata-server-0.9.0seatabin 目录执行

seata-server.bat -p 9000 -m file

将register.conf 文件分别拷贝到 order-service 和product-service-server 微服务 的resources 目录下

添加服务注册发现和 Seata事务组

分别在两个微服务resources目录下创建 bootstrap.yml 

spring:
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        namespace: public
        group: SEATA_GROUP
      discovery:
        server-addr: 127.0.0.1:8848
    alibaba:
      seata:
        tx-service-group: product_service_group

spring:
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        namespace: public
        group: SEATA_GROUP
      discovery:
        server-addr: 127.0.0.1:8848
    alibaba:
      seata:
        tx-service-group: order_service_group

在 ProductApplication 和OrderApplication 中添加注解 @EnableDiscoveryClient 用户服务发现

并在OrderApplication 添加 @EnableFeignClients(basePackages = "com.xxx") 开启feign服务调用。

修改creatOrder()方法添加注解 @GlobalTransactional 

   @GlobalTransactional
    @Override
    public Boolean createOrder(Long pid) {
        Productbase productbase = productServiceClient.getProductbase(pid);
        Orderbase orderbase=new Orderbase();
        orderbase.setNumber(2);
        orderbase.setPId(productbase.getId());
        orderbase.setPName(productbase.getName());
        orderbase.setPPrice(productbase.getPrice());
        orderbase.setUserName("超超");
        this.save(orderbase);
        productServiceClient.productUpdateStock(orderbase.getPId());
        return Boolean.TRUE;
    }

 在服务调用方order-service中添加数据源代理配置

Seata 是通过代理数据源实现事务分支的,所以需要配置 io.seata.rm.datasource.DataSourceProxy 的 Bean ,且是 @Primary 默认的数据源,否则事务不会回滚,无法实现分布式事务
@Configuration
public class DataSourceProxyConfig {
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DruidDataSource druidDataSource() {
        return new DruidDataSource();
    }

    @Primary
    @Bean
    public DataSourceProxy dataSource(DruidDataSource druidDataSource) {
        return new DataSourceProxy(druidDataSource);
    }
}

 product-base 中加入两条数据

 

然后分别启动两个微服务,在nacos界面服务列表可以看到 服务都注册成功了

 浏览器或者swagger执行

http://localhost:18080/order/1

可以看到执行成功

 

 

 订单表添加成功,库存也减了。然后任务增加一个异常测试一下事务的回滚。

 执行后可以看到订单和库存都没有成功。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/341084.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号