45、创建Seata 的三个微服务代码编写,测试Seata的事务控制

业务需求:下订单-------》减库存------》扣余额-----》改状态

1、创建 seata-order-service2001 订单微服务构建

 编写pom.xml 添加依赖

<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-alibaba-seata</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>seata-all</artifactId>
                    <groupId>io.seata</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- seata -->
        <dependency>
            <artifactId>seata-all</artifactId>
            <groupId>io.seata</groupId>
            <version>0.9.0</version>
        </dependency>
        <!-- openfeign -->
        <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>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
    </dependencies>

编写application.yml 配置文件

server:
  port: 2001

spring:
  application:
    name: seata-order-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    alibaba:
      seata:
        tx-service-group: fsp_tx_group

  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://8.129.215.115:3307/seata_order?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456

feign:
  hystrix:
    enabled: false
logging:
  level:
    io:
      seata: info

mybatis-plus:
  global-config:
    db-config:
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
  mapper-locations: classpath:mapper/*Mapper.xml
  type-aliases-package: com.atguigu.springcloud.alibaba.domain

编写file.conf 文件

transport {
  type = "TCP"
  server = "NIO"
  heartbeat = true
  thread-factory {
    boss-thread-prefix = "NettyBoss"
    worker-thread-prefix = "NettyServerNIOWorker"
    server-executor-thread-prefix = "NettyServerBizHandler"
    share-boss-worker = false
    client-selector-thread-prefix = "NettyClientSelector"
    client-selector-thread-size = 1
    client-worker-thread-prefix = "NettyClientWorkerThread"
    boss-thread-size = 1
    worker-thread-size = 8
  }
  shutdown {
    wait = 3
  }
  serialization = "seata"
  compressor = "none"
}
service {
  vgroup_mapping.fsp_tx_group = "default"
  default.grouplist = "127.0.0.1:8091"
  enableDegrade = false
  disable = false
  max.commit.retry.timeout = "-1"
  max.rollback.retry.timeout = "-1"
}

client {
  async.commit.buffer.limit = 10000
  lock {
    retry.internal = 10
    retry.times = 30
  }
  report.retry.count = 5
  tm.commit.retry.count = 1
  tm.rollback.retry.count = 1
}

store {
  mode = "db"
  file {
    dir = "sessionStore"
    max-branch-session-size = 16384
    max-global-session-size = 512
    file-write-buffer-cache-size = 16384
    session.reload.read_size = 100
    flush-disk-mode = async
  }

  db {
    datasource = "dbcp"
    db-type = "mysql"
    driver-class-name = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://8.129.215.115:3307/seata"
    user = "root"
    password = "123456"
    min-conn = 1
    max-conn = 3
    global.table = "global_table"
    branch.table = "branch_table"
    lock-table = "lock_table"
    query-limit = 100
  }
}
lock {
  mode = "remote"

  local {
  }

  remote {
  }
}
recovery {
  committing-retry-period = 1000
  asyn-committing-retry-period = 1000
  rollbacking-retry-period = 1000
  timeout-retry-period = 1000
}

transaction {
  undo.data.validation = true
  undo.log.serialization = "jackson"
  undo.log.save.days = 7
  undo.log.delete.period = 86400000
  undo.log.table = "undo_log"
}

metrics {
  enabled = false
  registry-type = "compact"
  exporter-list = "prometheus"
  exporter-prometheus-port = 9898
}

support {
  spring {
    datasource.autoproxy = false
  }
}

编写registry.conf 文件

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

  nacos {
    serverAddr = "localhost:8848"
    namespace = ""
    cluster = "default"
  }
  eureka {
    serviceUrl = "http://localhost:8761/eureka"
    application = "default"
    weight = "1"
  }
  redis {
    serverAddr = "localhost:6379"
    db = "0"
  }
  zk {
    cluster = "default"
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
  }
  consul {
    cluster = "default"
    serverAddr = "127.0.0.1:8500"
  }
  etcd3 {
    cluster = "default"
    serverAddr = "http://localhost:2379"
  }
  sofa {
    serverAddr = "127.0.0.1:9603"
    application = "default"
    region = "DEFAULT_ZONE"
    datacenter = "DefaultDataCenter"
    cluster = "default"
    group = "SEATA_GROUP"
    addressWaitTime = "3000"
  }
  file {
    name = "file.conf"
  }
}

config {
  type = "file"

  nacos {
    serverAddr = "localhost"
    namespace = ""
  }
  consul {
    serverAddr = "127.0.0.1:8500"
  }
  apollo {
    app.id = "seata-server"
    apollo.meta = "http://192.168.1.204:8801"
  }
  zk {
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
  }
  etcd3 {
    serverAddr = "http://localhost:2379"
  }
  file {
    name = "file.conf"
  }
}

编写实体类domain

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "t_order")
public class Order {
    /**
     *   `id` bigint NOT NULL COMMENT '主键',
     *   `user_id` bigint DEFAULT NULL COMMENT '用户id',
     *   `product_id` bigint DEFAULT NULL COMMENT '产品id',
     *   `count` int DEFAULT NULL COMMENT '数量',
     *   `money` decimal(11,1) DEFAULT NULL COMMENT '金额',
     *   `status` int DEFAULT NULL COMMENT '状态0:创建中,1:已完成',
     */
    private Long id;
    private Long userId;
    private Long productId;
    private Integer count;
    private BigDecimal money;
    private Integer status;

}

编写Mapper接口

@Mapper
public interface OrderMapper extends BaseMapper<Order> {

    @Insert("insert into t_order (user_id,product_id,count,money,status) values(#{userId},#{productId},#{count},#{money},#{status}) ")
    Integer create (Order order);

    @Update(" update t_order set status = 1 where user_id = #{userId} ")
    Integer updateByStatus(@Param("userId") Long userId);
}

编写 OrderService 接口

public interface OrderService {

    Order create (Order order);
}

编写 OrderService 接口 的实现类

@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Resource
    private OrderMapper orderMapper;
    @Resource
    private AccountService accountService;
    @Resource
    private StorageService storageService;


    @Override
    @GlobalTransactional(name = "fsp_order_create",rollbackFor = Exception.class)
    public Order create(Order order) {
        log.info("-------------------》开始新建订单");
        orderMapper.insert(order);
        log.info("-------------> 订单微服务开始调库存,做扣减Count");
        storageService.decrease(order.getProductId(),order.getCount());
        log.info("-------------> 订单微服务开始调库存,做扣减end");

        log.info("-------------> 订单微服务开始调账户,做扣减Money");
        accountService.decrease(order.getUserId(),order.getMoney());
        log.info("-------------> 订单微服务开始调账户,做扣减end");

        log.info("-------------> 订单微服务修改订单状态开始");
        orderMapper.updateByStatus(order.getUserId());
        log.info("-------------> 订单微服务修改订单状态 end");

        log.info("下订单结束了 哈哈哈");
        return order;
    }
}

编写Feign 接口 StorageService

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

    @PostMapping(value = "/storage/decrease")
    CommonResult decrease(@RequestParam(name = "productId") Long productId, @RequestParam(name = "count") Integer count);
}

编写Feign 接口 AccountService

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

    @PostMapping(value = "/storage/decrease")
    CommonResult decrease(@RequestParam(name = "userId") Long userId, @RequestParam(name = "money") BigDecimal money);
}

编写controller 类

@RestController
@Slf4j
public class OrderController {

    @Resource
    private OrderService orderService;

    @GetMapping(value = "/order/create")
    public CommonResult create (Order order) {
        Order result = orderService.create(order);
        return new CommonResult(200,"下单成功!",result);
    }
}

编写主启动类

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderApplication2001 {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication2001.class, args);
    }
}

2、创建 seata-storage-service2002 订单微服务构建

 编写pom.xml

<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-alibaba-seata</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>seata-all</artifactId>
                    <groupId>io.seata</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- seata -->
        <dependency>
            <artifactId>seata-all</artifactId>
            <groupId>io.seata</groupId>
            <version>0.9.0</version>
        </dependency>
        <!-- openfeign -->
        <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>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
    </dependencies>

编写application.yml

server:
  port: 2002

spring:
  application:
    name: seata-storage-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    alibaba:
      seata:
        tx-service-group: fsp_tx_group

  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://8.129.215.115:3307/seata_storage?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456

feign:
  hystrix:
    enabled: false
logging:
  level:
    io:
      seata: info

mybatis-plus:
  global-config:
    db-config:
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
  mapper-locations: classpath:mapper/*Mapper.xml
  type-aliases-package: com.atguigu.springcloud.alibaba.domain

编写file.conf 和registry.conf 两个文件 都和 seata-order-service2001 是一样的

编写实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Storage {

    /**
     * `id` bigint NOT NULL COMMENT '主键',
     *   `product_id` bigint DEFAULT NULL COMMENT '产品id',
     *   `total` int DEFAULT NULL COMMENT '总共',
     *   `used` int DEFAULT NULL COMMENT '已使用',
     *   `resdiue` int DEFAULT NULL COMMENT '剩余数量',
     */

    private Long id;
    private Long productId;
    private Integer total;
    private Integer used;
    private Integer resdiue;
}

编写Mapper接口

@MapperScan
public interface StorageMapper extends BaseMapper<Storage> {

    @Update(" update t_storage set used = used + #{count},resdiue = resdiue - #{count} where product_id = #{productId}")
    Integer decrease (@Param("productId")Long productId , @Param("count") Integer count);
}

编写StorrageService 接口

public interface StorageService {

    Integer decrease (Long productId , Integer count);
}

编写StorrageService 接口 的实现类

@Service
public class StorageServiceImpl implements StorageService {

    @Autowired
    private StorageMapper storageMapper;

    @Override
    public Integer decrease(Long productId, Integer count) {
        Integer decrease = storageMapper.decrease(productId, count);
        return decrease;
    }
}

编写controller业务类

@RestController
@Slf4j
public class StorageController {
    @Resource
    private StorageService storageService;

    @PostMapping(value = "/storage/decrease")
    public CommonResult decrease(@RequestParam(name = "productId") Long productId, @RequestParam(name = "count") Integer count){
        Integer decrease = storageService.decrease(productId, count);
        return new CommonResult(200,"库存扣减成功!");
    }
}

编写主启动类

@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
public class StorageApplication2002 {
    public static void main(String[] args) {
        SpringApplication.run(StorageApplication2002.class, args);
    }
}

3、创建 seata-account-service2003 订单微服务构建

编写pom.xml

 <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-alibaba-seata</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>seata-all</artifactId>
                    <groupId>io.seata</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- seata -->
        <dependency>
            <artifactId>seata-all</artifactId>
            <groupId>io.seata</groupId>
            <version>0.9.0</version>
        </dependency>
        <!-- openfeign -->
        <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>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
    </dependencies>

编写application.yml

server:
  port: 2003

spring:
  application:
    name: seata-account-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    alibaba:
      seata:
        tx-service-group: fsp_tx_group

  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://8.129.215.115:3307/seata_account?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456

feign:
  hystrix:
    enabled: false
logging:
  level:
    io:
      seata: info

mybatis-plus:
  global-config:
    db-config:
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
  mapper-locations: classpath:mapper/*Mapper.xml
  type-aliases-package: com.atguigu.springcloud.alibaba.domian

编写file.conf 和registry.conf 两个文件 都和 seata-order-service2001 是一样的

编写主启动类

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class AccountApplication2003 {
    public static void main(String[] args) {
        SpringApplication.run(AccountApplication2003.class, args);
    }
}

编写实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "t_account")
public class Account {
    /**
     *   `id` bigint NOT NULL COMMENT '主键',
     *   `user_id` bigint DEFAULT NULL COMMENT '用户id',
     *   `total` int DEFAULT NULL COMMENT '总共余额',
     *   `used` int DEFAULT NULL COMMENT '花了多少钱',
     *   `residue` int DEFAULT NULL COMMENT '剩余多少',
     */

    private Long id;
    private Long userId;
    private BigDecimal total;
    private BigDecimal used;
    private BigDecimal residue;
}

编写Mapper接口

@Mapper
public interface AccountMapper extends BaseMapper<Account> {

    @Update(" update t_account set used = used + #{money}, residue = residue - #{money} where user_id = #{userId}")
    Integer decrease(@Param("userId") Long userId, @Param("money") BigDecimal money);
}

编写AccountService 接口

public interface AccountService {

    Integer decrease(Long userId, BigDecimal money);
}

编写AccountService 接口 的实现类

@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountMapper accountMapper;
    @Override
    public Integer decrease(Long userId, BigDecimal money) {
        try {
            TimeUnit.SECONDS.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Integer decrease = accountMapper.decrease(userId, money);
        return decrease;
    }
}

编写controller业务类

@RestController
@Slf4j
public class AccountController {
    @Resource
    private AccountService accountService;

    @PostMapping(value = "/storage/decrease")
    public CommonResult decrease (@RequestParam(name = "userId") Long userId, @RequestParam(name = "money")BigDecimal money) {
        Integer decrease = accountService.decrease(userId, money);
        return new CommonResult(200,"扣减余额成功!");
    }
}

6、测试 :首先启动Nacos  、Seata 服务器、启动 我们三个微服务

访问地址:http://localhost:2011/order/create?userId=2&productId=10&count=10&money=10 

如果其中一个地方报错,那么数据会发送错误,因为没得到事务的控制

@GlobalTransactional(name = "fsp_order_create",rollbackFor = Exception.class) 这个就是控制全局事务

 

posted @ 2021-07-03 15:54  shunnWcs  阅读(206)  评论(0)    收藏  举报