springcloudalibaba~~使用seata做分布式事务控制

本文介绍如何使用Seata实现分布式事务控制,通过搭建基于Spring Cloud的微服务集群,并结合Nacos作为服务注册中心,实现跨服务的一致性事务管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

alibaba使用seata做分布式事务控制

1、数据库准备

分布式事务的话肯定是跟数据库有关的,数据库我这里使用的是mysql8版本

首先新建数据库seata_db_one,seata_db_two,seata_db_three
在这里插入图片描述
seata_db_one里面有order表
在这里插入图片描述

seata_db_two里面有storage表
在这里插入图片描述

seata_db_three里面有account表
在这里插入图片描述

2、微服务准备

我们的业务逻辑是首先下订单(订单微服务),然后减库存(库存微服务),然后减账户余额(账户微服务)

新建三个模块做对应的微服务service-seata9290(订单),service-seata9290(库存),service-seata9290(账户)

引入依赖,pom

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency>
        <groupId>io.seata</groupId>
        <artifactId>seata-spring-boot-starter</artifactId>
        <version>1.3.0</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.3</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

service-seata9290

配置文件

mybatis:
  type-aliases-package: com.zhu.model
  mapper-locations: classpath:mapper/*.xml
  configuration:
    map-underscore-to-camel-case: true

spring:
  datasource:
    url: jdbc:mysql://*****:3306/seata_db_one?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
    username: ***
    password: ***
    driver-class-name: com.mysql.cj.jdbc.Driver
  application:
    name: service-seata9290
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848

server:
  port: 9290

启动类添加注解

@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
@RestController
public class HelloController {

   @Resource
   private OrderService orderService;

   @Resource
   private OpenFeign openFeign;

   @RequestMapping("/hello")
   public String hello(@RequestParam(value = "id",required = false)Long id){
      orderService.insert();//添加订单
      openFeign.storageHello();//减少库存,每次减1
      orderService.update(id);//更改订单状态
      return "hello";
   }
}
@Mapper
public interface OrderMapper {
   public void insert(Order order);
   public void update(Order order);
}
@Data
public class Order {
   private long id;
   private int flag;
   private String name;
}
@Component
@FeignClient(value = "service-seata9291")
public interface OpenFeign {
   @RequestMapping("/storage/hello")
   public void storageHello();
}
@Service
public class OrderService {

   @Resource
   private OrderMapper orderMapper;

   public void insert(){
      Order order =new Order();
      order.setFlag(0);
      order.setName("订单");
      orderMapper.insert(order);
   }
   public void update(long id){
      Order order =new Order();
      order.setId(id);
      orderMapper.update(order);
   }
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhu.mapper.OrderMapper">

    <update id="update" parameterType="com.zhu.model.Order">
        update `order` set flag = 1 where id=#{id,jdbcType=BIGINT}
    </update>
    <insert id="insert" parameterType="com.zhu.model.Order">
        insert into `order` (flag,`name`) values (#{flag},#{name})
    </insert>
</mapper>

在这里插入图片描述

service-seata9291

配置文件

mybatis:
  type-aliases-package: com.zhu.model
  mapper-locations: classpath:mapper/*.xml
  configuration:
    map-underscore-to-camel-case: true

spring:
  datasource:
    url: jdbc:mysql://*****/seata_db_two?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
    username: ***
    password: ***
    driver-class-name: com.mysql.cj.jdbc.Driver
  application:
    name: service-seata9291
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848

server:
  port: 9291

启动类添加注解

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@RestController
public class StorageController {

   @Resource
   private StorageService storageService;

   @Resource
   private AccountOpenFeign accountOpenFeign;

   @RequestMapping("/storage/hello")
   public void storageHello() {
      storageService.decri(1);//减少库存
      accountOpenFeign.accountMoney();//扣钱
   }
}
@Mapper
public interface StorageMapper {
   void decri(Storage storage);
}
@Data
public class Storage {
   private Long id;
   private String name;
   private String num;
}
@FeignClient(value = "service-seata9292")
public interface AccountOpenFeign {
   @RequestMapping("/accountHello")
   public void accountMoney();
}
@Service
public class StorageService {

   @Resource
   private StorageMapper storageMapper;

   public void decri(int num) {
      Storage storage=new Storage();
      storage.setId(1L);
      storageMapper.decri(storage);
   }
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhu.mapper.StorageMapper">

    <update id="decri" parameterType="com.zhu.model.Storage">
        update storage set num=num - 1 where id=#{id,jdbcType=BIGINT}
    </update>
</mapper>

在这里插入图片描述

service-seata9292

配置文件

mybatis:
  type-aliases-package: com.zhu.model
  mapper-locations: classpath:mapper/*.xml
  configuration:
    map-underscore-to-camel-case: true

spring:
  datasource:
    url: jdbc:mysql://****:3306/seata_db_three?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
    username: ***
    password: ***
    driver-class-name: com.mysql.cj.jdbc.Driver
  application:
    name: service-seata9292
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848

server:
  port: 9292

启动类注解

@SpringBootApplication
@EnableDiscoveryClient
@RestController
public class AccountController {
   @Resource
   private AccountService accountService;

   @RequestMapping("/accountHello")
   public void accountMoney() {
      accountService.decriMoney();
   }
}
@Mapper
public interface AccountMapper {
   void decri();
}
@Data
public class Account {
   private Long id;
   private String name;
   private double money;
}
@Service
public class AccountService {
   @Resource
   private AccountMapper accountMapper;

   public void decriMoney(){
      accountMapper.decri();
   }
}

在这里插入图片描述

3、测试

数据库里面记得附上初始值

调用http://localhost:9290/hello?id=14(id是数据库下一条id的值)发现调用成功,数据库也会相应更改

现在在service-seata9292的AccountService类里面的decriMoney方法加上代码,如下

public void decriMoney(){
   int a =10/0;//这里会抛异常,那么数据更改就会不一致
   accountMapper.decri();
}

重新启动nacos和多个微服务,http://localhost:9290/hello?id=15,发现订单新建了,但是状态并未更改,库存减少了,金额并未减少

显然这样是不行的,我们需要做到共同进退,也就是加上分布式事务

我们在service-seata9290的HelloController类的hello方法上添加@GlobalTransactional注解

@RequestMapping("/hello")
@GlobalTransactional
   public String hello(@RequestParam(value = "id",required = false)Long id){
      orderService.insert();//添加订单
      openFeign.storageHello();//减少库存,每次减1
      orderService.update(id);//更改订单状态
      return "hello";
   }

再次测试就会是正常的流程了,那怕中途异常,也会全部回滚

seate使用确实是很简单的,只需要在开始加上@GlobalTransactional注解就行了

从数据库来看,确实是达到了一致性,但是发现后台会一直报错

ERROR 5584 --- [imeoutChecker_2] i.s.c.r.netty.NettyClientChannelManager  : no available service 'null' found, please make sure registry config correct

也就是seata的服务端没有启动,找不到才报的错

4、seata服务端配置

首先官网下载服务https://github.com/seata/seata/releases
在这里插入图片描述
下载后解压
在这里插入图片描述
我们先修改配置文件

在conf目录下,我们主要是修改file.conf和registry.conf这两个文件

file.conf是修改seata的存储的,这里使用数据库存储

store {
  ## store mode: file、db、redis
  mode = "db"  ##选择db,所以只需要更改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.cj.jdbc.Driver"
    url = "jdbc:mysql://xxxx:3306/seata"
    user = "root"
    password = "xxx"
    minConn = 5
    maxConn = 30
    globalTable = "global_table"
    branchTable = "branch_table"
    lockTable = "lock_table"
    queryLimit = 100
    maxWait = 5000
  }
}  

registry.conf是服务注册与配置中心的相关的文件

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos" ##这里使用的是nacos,下面下面只需要配置nacos

  nacos {
    application = "seata-server"
    serverAddr = "127.0.0.1:8848"
    group = "SEATA_GROUP"  ##分组不同可能会导致服务与客户端无法连接,所以推荐将所有的group设置为一样的
    namespace = "public"
    cluster = "default"
    username = "nacos"
    password = "nacos"
  }
}


config {
  # file、nacos 、apollo、zk、consul、etcd3
  type = "nacos" ##配置中心使用的是nacos,下面下面只需要配置nacos

  nacos {
    serverAddr = "127.0.0.1:8848"
    namespace = "public"
    group = "SEATA_GROUP" ##分组不同可能会导致服务与客户端无法连接,所以推荐将所有的group设置为一样的
    username = "nacos"
    password = "nacos"
    cluster = "default"
  }
}  

现按nacos配置中心还没有seata的配置,还需要将seata的配置推送到nacos
在这里插入图片描述
下载源码,解压后用idea打开,打开如图所示的config.txt文件,这里面是seata-server的所有配置
在这里插入图片描述
修改配置
在这里插入图片描述

打开文件夹目录…\seata-1.3.0\script\config-center\nacos,右键点击Git Bash Here,输入sh nacos-config.sh命令

显示一下信息表示配置推送成功

=========================================================================
 Complete initialization parameters,  total-count:79 ,  failure-count:0
=========================================================================
 Init nacos config finished, please start seata-server.

现在将seata-sever启动起来,进入到D:\seata-server-1.3.0\seata\bin双击seata-server.bat,即可启动服务,启动后我们进入nacos查看(nacos一定是先启动)
在这里插入图片描述
服务启动成功,再去配置列表查看配置是否推送成功
在这里插入图片描述
去client端配置seata

spring:
  datasource:
    url: jdbc:mysql://xxx:3306/seata_db_three?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
    username: 
    password: 
    driver-class-name: com.mysql.cj.jdbc.Driver ##mysql8驱动
  application:
    name: service-seata9292
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        group: SEATA_GROUP ##分组不同可能会导致服务与客户端无法连接,所以推荐将所有的group设置为一样的
        
seata:
  application-id: ${spring.application.name}
  enabled: true
  tx-service-group: my_test_tx_group ##前面记录的,需要填写一样
  enable-auto-data-source-proxy: true
  service:
    vgroup-mapping:
      my_test_tx_group: default
    grouplist:
      default: localhost:8091
  config:
    type: nacos
    nacos:
      namespace: ""
      serverAddr: localhost:8848
      group: SEATA_GROUP  ##分组不同可能会导致服务与客户端无法连接,所以推荐将所有的group设置为一样的
      userName: "nacos"
      password: "nacos"
  registry:
    type: nacos
    nacos:
      application: seata-server #名称与服务端在nacos注册中心的名称一样
      serverAddr: localhost:8848
      group: SEATA_GROUP  ##分组不同可能会导致服务与客户端无法连接,所以推荐将所有的group设置为一样的
      namespace: ""
      userName: "nacos"
      password: "nacos"
      cluster: default

再次启动nacos,seata-seaver,service-seata9292,就会发现service-seata9292控制台就不会报错了

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值