【Seata微服务分布式事务】2.Seata微服务分布式事务项目搭建
订单项目数据库:
/*Navicat MySQL Data Transfer
Source Server : 本地mysql
Source Server Version : 50726
Source Host : localhost:3306
Source Database : seata-order
Target Server Type : MYSQL
Target Server Version : 50726
File Encoding : 65001
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for order
-- ----------------------------
DROP TABLE IF EXISTS `order`;
CREATE TABLE `order` (
`id` int(20) NOT NULL AUTO_INCREMENT COMMENT "主键Id",
`user_id` int(20) DEFAULT NULL COMMENT "用户Id",
`pay_money` decimal(11,0) DEFAULT NULL COMMENT "付款金额",
`product_id` int(20) DEFAULT NULL COMMENT "商品Id",
`status` int(11) DEFAULT NULL COMMENT "状态",
`count` int(11) DEFAULT NULL COMMENT "商品数量",
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=291 DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC COMMENT="订单表";
-- ----------------------------
-- 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) 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 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of undo_log
-- ----------------------------
库存项目数据库:
/*Navicat MySQL Data Transfer
Source Server : 本地mysql
Source Server Version : 50726
Source Host : localhost:3306
Source Database : seata-product
Target Server Type : MYSQL
Target Server Version : 50726
File Encoding : 65001
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for product
-- ----------------------------
DROP TABLE IF EXISTS `product`;
CREATE TABLE `product` (
`id` int(20) NOT NULL COMMENT "主键",
`product_id` int(11) DEFAULT NULL COMMENT "商品Id",
`price` decimal(11,0) DEFAULT NULL COMMENT "价格",
`count` int(11) DEFAULT NULL COMMENT "库存数量",
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC COMMENT="仓储服务";
-- ----------------------------
-- Records of product
-- ----------------------------
INSERT INTO `product` VALUES ("1", "1", "50", "93");
INSERT INTO `product` VALUES ("2", "2", "30", "99");
-- ----------------------------
-- 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) 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 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of undo_log
-- ----------------------------
搭建扣库存项目
引依赖
依赖在文章最上面
写注解
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
package com.tuling;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
@EnableDiscoveryClient
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class Tulingvip06MsAlibabaStoreApplication {
public static void main(String[] args) {
SpringApplication.run(Tulingvip06MsAlibabaStoreApplication.class, args);
}
}
写配置添加代理数据源配置
package com.tuling.seata.config;import com.zaxxer.hikari.HikariDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
@Configuration
@MapperScan(basePackages = {"com.tuling.seata.mapper"})
public class MyBatisConfig {
/**
* 从配置文件获取属性构造datasource,注意前缀,这里用的是druid,根据自己情况配置,
* 原生datasource前缀取"spring.datasource"
*
* @return
*/
@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
public DataSource hikariDataSource() {
return new HikariDataSource();
}
/**
* 构造datasource代理对象,替换原来的datasource
*
* @param hikariDataSource
* @return
*/
@Primary
@Bean("dataSource")
public DataSourceProxy dataSourceProxy(DataSource hikariDataSource) {
return new DataSourceProxy(hikariDataSource);
}
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSourceProxy dataSourceProxy) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath:/mybatis/mapper/**/*.xml"));
sqlSessionFactoryBean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:/mybatis/mybatis-config.xml"));
sqlSessionFactoryBean.setTypeAliasesPackage("com.tuling.seata.domin");
sqlSessionFactoryBean.setDataSource(dataSourceProxy);
return sqlSessionFactoryBean;
}
}
改配置
#注册中心名称spring.application.name=nacos-seata-storage-server
server.port=8082
#数据库连接
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=60000
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.pool-name=DatebookHikariCP
spring.datasource.hikari.maximum-pool-size=30
spring.datasource.hikari.jdbc-url=jdbc:mysql://127.0.0.1:3306/seata-product?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.hikari.username=root
spring.datasource.hikari.password=123456
spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver
#事物组
spring.cloud.alibaba.seata.tx-service-group=prex_tx_group
#nacos注册中心地址
spring.cloud.nacos.discovery.server-addr=47.112.217.177:8847
logging.level.com.tuling.seata.mapper=debug
从服务器复制配置
从服务器复制file.conf和registry.conf文件到resources目录,不要修改任何东西。
业务层代码
只上核心代码,最后上源码
Impl减库存代码
@Override public boolean reduceCount(Integer productId, Integer amount) throws Exception {
log.info("商品Id:{},商品数量:{}",productId,amount);
log.info("当前 XID: {}", RootContext.getXID());
// 检查库存
checkStock(productId, amount);
log.info("开始扣减 {} 库存", productId);
Integer record = productMapper.reduceCount(productId,amount);
log.info("结束扣减 {} 库存结果:{}", productId, record > 0 ? "操作成功" : "扣减库存失败");
return false;
}
搭建下订单项目
引依赖
和扣库存项目一样
写注解
和扣库存项目一样
写配置添加代理数据源配置
和扣库存项目一样
从服务器复制配置
和扣库存项目一样
项目配置
spring.application.name=nacos-seata-order-serverserver.port=8081
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=60000
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.pool-name=DatebookHikariCP
spring.datasource.hikari.maximum-pool-size=30
spring.datasource.hikari.jdbc-url=jdbc:mysql://127.0.0.1:3306/seata-order?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.hikari.username=root
spring.datasource.hikari.password=123456
spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver
spring.cloud.alibaba.seata.tx-service-group=prex_tx_group
spring.cloud.nacos.discovery.server-addr=47.112.217.177:8847
logging.level.com.tuling.seata.mapper=debug
ribbon.eager-load.enabled=true
feign.client.config.default.connectTimeout=5000
feign.client.config.default.readTimeout=5000
核心业务代码
微服务发起者需要写全局事务注解(这里是order服务为发起者),name的参数值不要重复
//@GlobalTransactional(name = "prex-create-order",rollbackFor = Exception.class)
//@GlobalTransactional(name = "prex-create-order",rollbackFor = Exception.class) @Transactional
@Override
public void createOrder(Order order) {
log.info("当前 XID: {}", RootContext.getXID());
log.info("下单开始,用户:{},商品:{},数量:{},金额:{}", order.getUserId(), order.getProductId(), order.getCount(), order.getPayMoney());
//创建订单
order.setStatus(0);
orderMapper.saveOrder(order);
log.info("保存订单{}", order);
//远程调用库存服务扣减库存
log.info("扣减库存开始");
remoteStorageService.reduceCount(order.getProductId(), order.getCount());
log.info("扣减库存结束");
/* //远程调用账户服务扣减余额
log.info("扣减余额开始");
remoteAccountService.reduceBalance(order.getUserId(), order.getPayMoney());
log.info("扣减余额结束");*/
//弄个异常
List<String> l = new ArrayList<>();
l.get(100);
//修改订单状态为已完成
log.info("修改订单状态开始");
orderMapper.updateOrderStatusById(order.getId(),1);
log.info("修改订单状态结束");
log.info("下单结束");
}
这里我们先使用spring的普通事物管理@Transactional方便测试看效果,在代码中加个异常代码。
启动项目测试
原数据:
访问测试:http://127.0.0.1:8081/order/create?userId=1&productId=1&count=1&payMoney=50
异常导致订单数据被回滚,但是库存已经减。
现在把订单项目@GlobalTransactional注解去掉,重启订单项目
@GlobalTransactional(name = "prex-create-order",rollbackFor = Exception.class)
访问测试:http://127.0.0.1:8081/order/create?userId=1&productId=1&count=1&payMoney=50
这次结果订单没有添加,库存也没有减少。
去掉订单项目异常代码,重启订单项目
访问测试:http://127.0.0.1:8081/order/create?userId=1&productId=1&count=1&payMoney=50
源码:https://gitee.com/hekang_admin/tulingvip06-ms-alibaba-nacos-seata.git
以上是 【Seata微服务分布式事务】2.Seata微服务分布式事务项目搭建 的全部内容, 来源链接: utcz.com/z/515065.html