SpringCloud Alibaba 九(Seata介绍二)

Seata 案例

我们会在项目中创建三个服务,仓储服务,订单服务,帐户服务。当用户下单时,会在订单服务中创建一个订单,然后通过远程调用仓储服务来扣减下单商品的库存,再通过远程调用账户服务来扣减用户账户里面的余额。该操作跨越三个数据库,有两次远程调用,很明显会有分布式事务问题。

架构图

在这里插入图片描述

1、我们需要新增三个数据库 seata_order、seata_account、seata_store 。
在这里插入图片描述
2、在三个库中新建表:
在seata_order库中新建order 表::

DROP TABLE IF EXISTS `order`;
CREATE TABLE `order` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` varchar(255) DEFAULT NULL,
  `commodity_code` varchar(255) DEFAULT NULL,
  `count` int(11) DEFAULT 0,
  `money` int(11) DEFAULT 0,
  `status` INT(1) DEFAULT NULL COMMENT '订单状态:0:创建中;1:已完结' ,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

在seata_account库中新建account表:

DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` varchar(255) DEFAULT NULL,
  `money` int(11) DEFAULT 0,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `seata_account`.`account`(`id`, `user_id`, `money`) VALUES (1, '1', 1000);

在seata_store库中新增 storage表

DROP TABLE IF EXISTS `storage`;
CREATE TABLE `storage` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `commodity_code` varchar(255) DEFAULT NULL,
  `count` int(11) DEFAULT 0,
  PRIMARY KEY (`id`),
  UNIQUE KEY (`commodity_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `seata_store`.`storage`(`id`, `commodity_code`, `count`) VALUES (1, '1', 100);

因为SEATA AT 模式需要 UNDO_LOG 表,这个需要在每一个业务库中新建表:

DROP TABLE IF EXISTS `undo_log`;
-- 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       NOT NULL COMMENT 'branch transaction id',
    `xid`           VARCHAR(128) 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 = InnoDB
  AUTO_INCREMENT = 1
  DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';

在这里插入图片描述

新建Module pcloud-alibaba-seata-order-service9001

在这里插入图片描述
在这里插入图片描述
修改pom文件:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>pcloud-alibaba</artifactId>
        <groupId>com.younger.pcloud.alibaba</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>pcloud-alibaba-seata-order-service9001</artifactId>

    <name>pcloud-alibaba-seata-order-service9001</name>

    <dependencies>
        <!--nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--seata-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <!--排除项目自带的包-->
            <exclusions>
                <exclusion>
                    <artifactId>seata-all</artifactId>
                    <groupId>io.seata</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-all</artifactId>
            <version>1.4.2</version>
        </dependency>
        <!--feign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--mysql-druid-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.37</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

</project>

新增 application.yml文件:

server:
  port: 9001

spring:
  application:
    name: seata-order-service
  cloud:
    alibaba:
      seata:
        #自定义事务组名称需要与seata-server中的对应
        tx-service-group: my_test_tx_group
    nacos:
      discovery:
        server-addr: localhost:8848
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/seata_order
    username: root
    password: mysql

feign:
  hystrix:
    enabled: false

logging:
  level:
    io:
      seata: info

mybatis:
  mapperLocations: classpath:mapper/*.xml

在这里插入图片描述
目录结构:
在这里插入图片描述
新增registry.conf文件:

registry.conf:

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa、custom
  type = "nacos"

  nacos {
    application = "seata-server"
    serverAddr = "127.0.0.1:8848"
    group = "SEATA_GROUP"
    namespace = ""
    username = ""
    password = ""
  }
  eureka {
    serviceUrl = "http://localhost:8761/eureka"
    weight = "1"
  }
  redis {
    serverAddr = "localhost:6379"
    db = "0"
    password = ""
    timeout = "0"
  }
  zk {
    serverAddr = "127.0.0.1:2181"
    sessionTimeout = 6000
    connectTimeout = 2000
    username = ""
    password = ""
  }
  consul {
    serverAddr = "127.0.0.1:8500"
    aclToken = ""
  }
  etcd3 {
    serverAddr = "http://localhost:2379"
  }
  sofa {
    serverAddr = "127.0.0.1:9603"
    region = "DEFAULT_ZONE"
    datacenter = "DefaultDataCenter"
    group = "SEATA_GROUP"
    addressWaitTime = "3000"
  }
  file {
    name = "file.conf"
  }
  custom {
    name = ""
  }
}

config {
  # file、nacos 、apollo、zk、consul、etcd3、springCloudConfig、custom
  type = "nacos"

  nacos {
    serverAddr = "127.0.0.1:8848"
    namespace = ""
    group = "SEATA_GROUP"
    username = ""
    password = ""
    dataId = "seata.properties"
  }
  consul {
    serverAddr = "127.0.0.1:8500"
    aclToken = ""
  }
  apollo {
    appId = "seata-server"
    apolloMeta = "http://192.168.1.204:8801"
    namespace = "application"
    apolloAccesskeySecret = ""
  }
  zk {
    serverAddr = "127.0.0.1:2181"
    sessionTimeout = 6000
    connectTimeout = 2000
    username = ""
    password = ""
    nodePath = "/seata/seata.properties"
  }
  etcd3 {
    serverAddr = "http://localhost:2379"
  }
  file {
    name = "file.conf"
  }
  custom {
    name = ""
  }
}

这个registry.conf配置文件可以拷贝,我们上一节配置的registry.conf文件。

新增启动类: SeataOrderMain9001

package com.younger.pcloud.alibaba;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;


@EnableFeignClients
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
@EnableAutoDataSourceProxy
public class SeataOrderMain9001 {

    public static void main(String[] args) {
        SpringApplication.run(SeataOrderMain9001.class, args);
    }
}

新增domain包:
新增order类:

package com.younger.pcloud.alibaba.domain;

public class Order {

    private Long id;

    private String userId;

    private String commodityCode;

    private Integer count;

    private Integer money;

    /**
     * 订单状态:0:创建中;1:已完结
     */
    private Integer status;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getCommodityCode() {
        return commodityCode;
    }

    public void setCommodityCode(String commodityCode) {
        this.commodityCode = commodityCode;
    }

    public Integer getCount() {
        return count;
    }

    public void setCount(Integer count) {
        this.count = count;
    }

    public Integer getMoney() {
        return money;
    }

    public void setMoney(Integer money) {
        this.money = money;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }
}

新增 Result类:

package com.younger.pcloud.alibaba.domain;

public class Result<T> {

    private Integer code;
    private String  message;
    private T       data;
	
	public Result() {
       
    }

    public Result(Integer code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    public Result(Integer code, String message) {
        this(code,message,null);
    }
	public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

}

新增config 包:
新增 MyBatisConfig类:

package com.younger.pcloud.alibaba.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan({"com.younger.pcloud.alibaba.dao"})
public class MyBatisConfig {


}

新增dao 包:

新增 OrderDao 类:

package com.younger.pcloud.alibaba.dao;

import com.younger.pcloud.alibaba.domain.Order;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper
public interface OrderDao {

    /**
     * 创建订单
     * @param order
     */
    void createOrder(Order order);


    /**
     * 修改订单状态
     */
    void update(@Param("userId") String userId, @Param("status") Integer status);
}

新增 mapper 包:
新增 OrderMapper.xml文件:

<?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.younger.pcloud.alibaba.dao.OrderDao">
    <insert id="createOrder">
        INSERT INTO `order` (`id`, `user_id`, `commodity_code`, `count`, `money`,`status`)
        VALUES (NULL, #{userId}, #{commodityCode}, #{count}, #{money},0);
    </insert>

    <update id="update">
        UPDATE `order`
        SET status = 1
        WHERE user_id = #{userId} AND status = #{status};
    </update>
</mapper>


新增service包:
新增 OrderService类:

package com.younger.pcloud.alibaba.service;

import com.younger.pcloud.alibaba.domain.Order;

public interface OrderService {

    /**
     * 创建订单
     */
    void createOrder(Order order);

}

新增fegin远程调用包:
新增 AccountService类:

package com.younger.pcloud.alibaba.service.fegin;

import com.younger.pcloud.alibaba.domain.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(value = "seata-account-service")
public interface AccountService {

    /**
     * 扣减账户余额
     * @param userId
     * @param money
     * @return
     */
    @PostMapping("/account/decrease")
    Result decrease(@RequestParam("userId") String userId, @RequestParam("money") Integer money);
}

新增StorageService类:

package com.younger.pcloud.alibaba.service.fegin;

import com.younger.pcloud.alibaba.domain.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(value = "seata-store-service")
public interface StorageService {

    /**
     * 扣减库存
     */
    @PostMapping(value = "/storage/decrease")
    Result decrease(@RequestParam("commodityCode") String commodityCode, @RequestParam("count") Integer count);

}

新增OrderService 实现类OrderServiceImpl :

package com.younger.pcloud.alibaba.service.impl;

import com.younger.pcloud.alibaba.dao.OrderDao;
import com.younger.pcloud.alibaba.domain.Order;
import com.younger.pcloud.alibaba.service.OrderService;
import com.younger.pcloud.alibaba.service.fegin.AccountService;
import com.younger.pcloud.alibaba.service.fegin.StorageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Resource
    private OrderDao orderDao;

    @Resource
    private StorageService storageService;

    @Resource
    private AccountService accountService;

    @Override
    public void createOrder(Order order) {

        log.info("开始创建订单....");
        //本应用创建订单
        orderDao.createOrder(order);

        //远程调用库存服务扣减库存
        log.info("order-service------扣减库存开始");
        storageService.decrease(order.getCommodityCode(),order.getCount());
        log.info("order-service------扣减库存结束");

        //远程调用账户服务扣减余额
        log.info("order-service-------中扣减用户余额开始");
        accountService.decrease(order.getUserId(),order.getMoney());
        log.info("order-service-------中扣减余额结束");

        //修改订单状态为已完成
        log.info("order-service-------中修改订单状态开始");
        orderDao.update(order.getUserId(),0);
        log.info("order-service-------中修改订单状态结束");

        log.info("订单完成!!!!!");
    }
}

新增Controller 包:

新增 OrderController 类:

package com.younger.pcloud.alibaba.controller;

import com.younger.pcloud.alibaba.domain.Order;
import com.younger.pcloud.alibaba.domain.Result;
import com.younger.pcloud.alibaba.service.OrderService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class OrderController {

    @Resource
    private OrderService orderService;

    @PostMapping("/order/create")
    public Result createOrder(@RequestBody Order order) {
        orderService.createOrder(order);
        return new Result(200,"订单创建成功");
    }
}

新建modul pcloud-alibaba-seata-store-service9002

目录结构:
在这里插入图片描述
修改pom文件:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <parent>
    <artifactId>pcloud-alibaba</artifactId>
    <groupId>com.younger.pcloud.alibaba</groupId>
    <version>1.0-SNAPSHOT</version>
  </parent>
  <modelVersion>4.0.0</modelVersion>

  <artifactId>pcloud-alibaba-seata-store-service9002</artifactId>

  <dependencies>
    <!--nacos-->
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!--seata-->
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
      <exclusions>
        <exclusion>
          <artifactId>seata-all</artifactId>
          <groupId>io.seata</groupId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>io.seata</groupId>
      <artifactId>seata-all</artifactId>
      <version>1.4.2</version>
    </dependency>
    <!--feign-->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>2.0.0</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.37</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid-spring-boot-starter</artifactId>
      <version>1.1.10</version>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>
  </dependencies>

</project>

新增application.yml文件:

server:
  port: 9002

spring:
  application:
    name: seata-store-service
  cloud:
    alibaba:
      seata:
        #自定义事务组名称需要与seata-server中的对应
        tx-service-group: my_test_tx_group
    nacos:
      discovery:
        server-addr: localhost:8848
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/seata_store
    username: root
    password: mysql

feign:
  hystrix:
    enabled: false

logging:
  level:
    io:
      seata: info

mybatis:
  mapperLocations: classpath:mapper/*.xml

拷贝 file.conf 和 registry.conf 文件到Resource目录下:

新增启动类 SeataStorageServiceMain9002:

package com.younger.pcloud.alibaba;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableAutoDataSourceProxy
public class SeataStorageServiceMain9002 {

    public static void main(String[] args) {

        SpringApplication.run(SeataStorageServiceMain9002.class ,args);
    }
}

从9001 拷贝 config 目录和registry.conf 到 9002

新增domain 目录:
新增Result 类:

package com.younger.pcloud.alibaba.domain;

public class Result<T> {

    private Integer code;
    private String  message;
    private T       data;

	 public Result() {
       
     }

    public Result(Integer code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    public Result(Integer code, String message) {
        this(code,message,null);
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

新增 Store 类:

package com.younger.pcloud.alibaba.domain;

public class Store {

    private Long id;

    private String commodityCode;

    private Integer count;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getCommodityCode() {
        return commodityCode;
    }

    public void setCommodityCode(String commodityCode) {
        this.commodityCode = commodityCode;
    }

    public Integer getCount() {
        return count;
    }

    public void setCount(Integer count) {
        this.count = count;
    }
}

新增dao包:
新增 StoreDao 类:

package com.younger.pcloud.alibaba.dao;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper
public interface StoreDao {


    /**
     * 扣减库存
     */
    void decrease(@Param("commodityCode") String commodityCode, @Param("count") Integer count);

}

新增 StoreMapper.xml 文件:

<?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.younger.pcloud.alibaba.dao.StoreDao">

    <update id="decrease">
        UPDATE storage
        SET count    = count - #{count}
        WHERE commodity_code = #{commodityCode}
    </update>
</mapper>

新增 service 包:
新增 StoreService类:

package com.younger.pcloud.alibaba.service;

public interface StoreService {
    /**
     * 扣减库存
     */
    void decrease(String commodityCode, Integer count);
}

新增 StoreService实现类 StoreServiceImpl类:

package com.younger.pcloud.alibaba.service.impl;

import com.younger.pcloud.alibaba.dao.StoreDao;
import com.younger.pcloud.alibaba.service.StoreService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
@Slf4j
public class StoreServiceImpl implements StoreService {

    @Resource
    private StoreDao storeDao;

    @Override
    public void decrease(String commodityCode, Integer count) {
        log.info("storage-service-------中扣减库存开始");
        storeDao.decrease(commodityCode,count);
        log.info("storage-service--------中扣减库存结束");

    }
}

新增controller 包:
新增 StoreController 类:

package com.younger.pcloud.alibaba.controller;

import com.younger.pcloud.alibaba.domain.Result;
import com.younger.pcloud.alibaba.service.StoreService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class StoreController {

    @Resource
    private StoreService storeService;

    /**
     * 扣减库存
     */
   @RequestMapping("/storage/decrease")
   public Result decrease(@RequestParam("commodityCode") String commodityCode, @RequestParam("count") Integer count) {
        storeService.decrease(commodityCode, count);
        return new Result(200,"库存扣减成功!");
    }

}

新建modul pcloud-alibaba-seata-account-service9003

修改pom文件:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>pcloud-alibaba</artifactId>
        <groupId>com.younger.pcloud.alibaba</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>pcloud-alibaba-seata-account-service9003</artifactId>

    <name>pcloud-alibaba-seata-account-service9003</name>

    <dependencies>
        <!--nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--seata-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>seata-all</artifactId>
                    <groupId>io.seata</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-all</artifactId>
            <version>1.4.2</version>
        </dependency>
        <!--feign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.37</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
</project>

新增 application.yml 文件:

server:
  port: 9003

spring:
  application:
    name: seata-account-service
  cloud:
    alibaba:
      seata:
        #自定义事务组名称需要与seata-server中的对应
        tx-service-group: my_test_tx_group
    nacos:
      discovery:
        server-addr: localhost:8848
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/seata_account
    username: root
    password: mysql

feign:
  hystrix:
    enabled: false

logging:
  level:
    io:
      seata: info

mybatis:
  mapperLocations: classpath:mapper/*.xml

新增启动类SeataAccountServiceMain9003:

package com.younger.pcloud.alibaba;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableAutoDataSourceProxy
public class SeataAccountServiceMain9003 {

    public static void main(String[] args) {
        SpringApplication.run(SeataAccountServiceMain9003.class,args);
    }
}

目录结构:
在这里插入图片描述
从9001 拷贝 config 目录和registry.conf 到 9003

新增domain 包:
新增 Account 类:

package com.younger.pcloud.alibaba.domain;

public class Account {

    private Long id;

    private String userId;

    private Integer money;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public Integer getMoney() {
        return money;
    }

    public void setMoney(Integer money) {
        this.money = money;
    }
}

新增 Result 类:

package com.younger.pcloud.alibaba.domain;

public class Result<T> {

    private Integer code;
    private String  message;
    private T       data;
 	 public Result() {
       
    }
    public Result(Integer code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    public Result(Integer code, String message) {
        this(code,message,null);
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

新增dao包:
新增 AccountDao 类:

package com.younger.pcloud.alibaba.dao;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper
public interface AccountDao {

    /**
     * 扣减账户余额
     */
    void decrease(@Param("userId") String userId, @Param("money") Integer money);

}

新增 AccountMapper.xml 文件:

<?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.younger.pcloud.alibaba.dao.AccountDao">
    <update id="decrease">
        UPDATE account
        SET
          money = money - #{money}
        WHERE
          user_id = #{userId};
    </update>
</mapper>

新增service 包:
新增 AccountService 类:

package com.younger.pcloud.alibaba.service;

import org.springframework.web.bind.annotation.RequestParam;

public interface AccountService {
    /**
     * 扣减账户余额
     * @param userId 用户id
     * @param money 金额
     */
    void decrease(@RequestParam("userId") String  userId, @RequestParam("money") Integer money);

}

新增 AccountService 的实现类AccountServiceImpl:

package com.younger.pcloud.alibaba.service.impl;

import com.younger.pcloud.alibaba.dao.AccountDao;
import com.younger.pcloud.alibaba.service.AccountService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;


@Service
@Slf4j
public class AccountServiceImpl implements AccountService {

    @Resource
    AccountDao accountDao;

    /**
     * 扣减账户余额
     */
    @Override
    public void decrease(String userId, Integer money) {
        log.info("account-service--------中扣减账户余额开始");
        accountDao.decrease(userId,money);
        log.info("account-service-------中扣减账户余额结束");
    }
}

新增 controller包:
新增 AccountController 类:

package com.younger.pcloud.alibaba.controller;

import com.younger.pcloud.alibaba.domain.Result;
import com.younger.pcloud.alibaba.service.AccountService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class AccountController {

    @Resource
    private AccountService accountService;

    /**
     * 扣减账户余额
     */
    @RequestMapping("/account/decrease")
    public Result decrease(@RequestParam("userId") String userId, @RequestParam("money") Integer money){
        accountService.decrease(userId,money);
        return new Result(200,"账户余额扣减成功!");
    }
}

启动项目测试:
在这里插入图片描述
在这里插入图片描述
通过日志,我们可以知道我们的事务已经注册成功了。

创建订单: http://localhost:9001/order/create

参数请求:

{
    "id":1,
    "userId":"1",
    "commodityCode":"1",
    "count":1,
    "money":55
}

在这里插入图片描述
我们服务之间的调用已经成功了。

我们看下表的数据:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
表里面的数据和我们预想的结果是一样的。

我们现在没有在我们的业务上面添加事务,我们测试一下超时会怎么样?
我们修改我们的AccountServiceImpl类:
在这里插入图片描述
可以看到我们在类上面添加了超时异常。
我们重启下项目测试:
测试:
在这里插入图片描述
此刻访问已经超时了。

我们看下数据库:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

可以看到,我们在已经创建了订单,并且库存已经抵扣了,但是用户没有支付成功。这个就是我们常见的分布式事务的问题。

我们在我们的业务类上面添加我们的分布式注解:@GlobalTransactional

我们修改 OrderServiceImpl类:
在这里插入图片描述
只需要在我们的业务类上添加一个 @GlobalTransactional注解即可,这个name是我们自己定义的,但是需要全局唯一。

我们重新启动项目看下:
测试:
在这里插入图片描述
执行也是超时的,我们在看下数据库:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
我们看到订单和库存都没有记录。说明我们的分布式事务已经成功了。

总结一下分布式事务的执行流程:
1、TM 开启分布式事务(TM 向 TC 注册全局事务记录)。
2、RM 向 TC 汇报资源准备状态。
3、TM 通知 TC 提交/回滚分布式事务。
4、TC 汇总事务信息,决定分布式事务是提交还是回滚。
5、TC 通知所有 RM 提交/回滚 资源,事务二阶段结束。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

youngerone123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值