SpringCloud实战四十三:seata的应用

  作者:记性不好的阁主

业务需求

下订单->减库存->扣余额->改订单状


新建订单 Order- Module

新建库存 Storage -Module

新建账户 Account-Module


  • 2001订单服务


1、创建订单接口


package com.laoxu.springcloud.controller;


import com.laoxu.springcloud.domain.Order;
import com.laoxu.springcloud.entities.CommonResult;
import com.laoxu.springcloud.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

@Autowired
private OrderService orderService;

@GetMapping("/order/create")
public CommonResult create(Order order){
orderService.create(order);
return new CommonResult(200, "订单创建成功!");
}

}



2、服务层


package com.laoxu.springcloud.service.impl;

import com.laoxu.springcloud.dao.OrderDao;
import com.laoxu.springcloud.domain.Order;
import com.laoxu.springcloud.service.AccountService;
import com.laoxu.springcloud.service.OrderService;
import com.laoxu.springcloud.service.StorageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

@Autowired
private OrderDao orderDao;

@Autowired
private StorageService storageService;

@Autowired
private AccountService accountService;

@Override
public void create(Order order) {
log.info("--->开始创建订单");
// 1.创建订单
orderDao.create(order);
log.info("--->订单微服务开始调用库存,做扣减Count");
// 2.扣减库存
storageService.decrease(order.getProductId(), order.getCount());
log.info("--->订单微服务开始调用库存,做扣减end");

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

// 4.修改订单的状态,从零到1,代表已经完成
log.info("--->修改订单状态开始");
orderDao.update(order.getUserId(), 0);
log.info("--->修改订单状态结束");

log.info("--->下单结束");

}
}


其中 StorageService 和 AccountService 为feign调用的微服务


  • 2002库存服务


扣减库存接口



package com.laoxu.springcloud.controller;


import com.laoxu.springcloud.entities.CommonResult;
import com.laoxu.springcloud.service.StorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class StorageController {

@Autowired
private StorageService storageService;

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

}



  • 2003账户服务


扣减账户接口


package com.laoxu.springcloud.controller;


import com.laoxu.springcloud.entities.CommonResult;
import com.laoxu.springcloud.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;

@RestController
public class AccountController {

@Autowired
private AccountService accountService;

@GetMapping("/account/decrease")
public CommonResult decrease(@RequestParam("userId") Long userId,@RequestParam("money") BigDecimal money){
accountService.decrease(userId, money);
return new CommonResult(200, "扣减账户成功!");
}

}



  • 准备多个数据库


订单库


/*
Navicat MySQL Data Transfer

Source Server : 腾讯1G2
Source Server Type : MySQL
Source Server Version : 50727
Source Host : 106.54.196.44:3306
Source Schema : seata_order

Target Server Type : MySQL
Target Server Version : 50727
File Encoding : 65001

Date: 25/10/2020 15:10:54
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_order
-- ----------------------------
DROP TABLE IF EXISTS `t_order`;
CREATE TABLE `t_order` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`user_id` bigint(11) NULL DEFAULT NULL COMMENT '用户id',
`product_id` bigint(11) NULL DEFAULT NULL COMMENT '产品id',
`count` int(11) NULL DEFAULT NULL COMMENT '数量',
`money` decimal(11, 0) NULL DEFAULT NULL COMMENT '金额',
`status` int(1) NULL DEFAULT NULL COMMENT '订单状态:0:创建中;1:已完结',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 25 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for 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) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime(0) NOT NULL,
`log_modified` datetime(0) NOT NULL,
`ext` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;


库存库


/*
Navicat MySQL Data Transfer

Source Server : 腾讯1G2
Source Server Type : MySQL
Source Server Version : 50727
Source Host : 106.54.196.44:3306
Source Schema : seata_store

Target Server Type : MySQL
Target Server Version : 50727
File Encoding : 65001

Date: 25/10/2020 15:11:02
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_storage
-- ----------------------------
DROP TABLE IF EXISTS `t_storage`;
CREATE TABLE `t_storage` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`product_id` bigint(11) NULL DEFAULT NULL COMMENT '产品id',
`total` int(11) NULL DEFAULT NULL COMMENT '总库存',
`used` int(11) NULL DEFAULT NULL COMMENT '已用库存',
`residue` int(11) NULL DEFAULT NULL COMMENT '剩余库存',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of t_storage
-- ----------------------------
INSERT INTO `t_storage` VALUES (1, 1, 100, 0, 100);

-- ----------------------------
-- Table structure for 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) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime(0) NOT NULL,
`log_modified` datetime(0) NOT NULL,
`ext` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;


账户库


/*
Navicat MySQL Data Transfer

Source Server : 腾讯1G2
Source Server Type : MySQL
Source Server Version : 50727
Source Host : 106.54.196.44:3306
Source Schema : seata_account

Target Server Type : MySQL
Target Server Version : 50727
File Encoding : 65001

Date: 25/10/2020 15:10:38
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_account
-- ----------------------------
DROP TABLE IF EXISTS `t_account`;
CREATE TABLE `t_account` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`user_id` bigint(11) NULL DEFAULT NULL COMMENT '用户id',
`total` decimal(10, 0) NULL DEFAULT NULL COMMENT '总额度',
`used` decimal(10, 0) NULL DEFAULT NULL COMMENT '已用余额',
`residue` decimal(10, 0) NULL DEFAULT NULL COMMENT '剩余可用额度',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of t_account
-- ----------------------------
INSERT INTO `t_account` VALUES (1, 1, 1000, 0, 1000);

-- ----------------------------
-- Table structure for 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) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime(0) NOT NULL,
`log_modified` datetime(0) NOT NULL,
`ext` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;


创建好后的初始数据










  • 测试创建订单接口


访问:http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100



订单表中生成一条订单记录

用户1购买了10件商品1花了100元,状态为1已完成




库存表中更新库存

商品1的库存减少了10件,还剩90件




账户表中更新账户

用户1的余额扣除100元,还剩900元









订单创建成功!


  • 测试接口超时报错


在账户订单中添加延迟函数




由于openfeign默认超时时间是1秒,则调用时会报超时异常






  • 查看数据库中情况


在订单表中插入了一条订单记录,此时状态为未支付




但是库存中的库存却已经被减去了





而且账户还被扣去了100元





  • 此时在订单创建业务方法上方添加注解@GlobalTransactional 指定遇到哪些运行时异常回滚事务,这里是任何运行时异常Exception



  • 此时再次访问:http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100
  • 此时库中数据不变,因为事务被回滚,数据库不做任何操作



相关推荐

评论 抢沙发

表情

分类选择