【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-server

server.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

回到顶部