环境:springboot2.3.11.RELEASE + spring cloud Hoxton.SR8 + spring cloud alibaba 2.2.5.RELEASE + seata1.3.0
前提:安装并启动了nacos服务
Seata注册中心及配置中心说明
配置中心
什么是配置中心?配置中心可以说是一个"大衣柜",内部放置着各种配置文件,你可以通过自己所需进行获取配置加载到对应的客户端.比如Seata Client端(TM,RM),Seata Server(TC),会去读取全局事务开关,事务会话存储模式等信息.
Seata的配置中心与Spring cloud的配置中心区别是?在广义上来说,并无区别,只不过Spring cloud的配置中心仅是作用于它们自身的组件,而Seata的配置中心也是一样是作用于Seata自身.(注:Spring cloud的配置中心与Seata无关)
注册中心
什么是注册中心?注册中心可以说是微服务架构中的”通讯录“,它记录了服务和服务地址的映射关系。在分布式架构中,服务会注册到这里,当服务需要调用其它服务时,就到这里找到服务的地址,进行调用.比如Seata Client端(TM,RM),发现Seata Server(TC)集群的地址,彼此通信.
Seata的注册中心与Dubbo,Spring cloud的注册中心区别是?在广义上来说,并无区别,只不过Dubbo与Spring cloud的注册中心仅是作用于它们自身的组件,而Seata的注册中心也是一样是作用于Seata自身.(注:Dubbo与Spring cloud的注册中心与Seata无关)
Seata服务配置
1、在%SEATA_HOME%目录下新建config.txt文件
service.vgroupMapping.dt-group=default
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&serverTimezone=GMT%2B8
store.db.user=root
store.db.password=xxxxxx
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000
2、执行脚本
通过如下地址下载脚本执行
在windows系统下可以通过 git bash 来执行shell脚本
sh nacos-config.sh -h nacos的ip -p nacos端口 -g nacos配置文件的组 -t 你的namespace号(若是public可省略此选项) -u nacos用户名 -w nacos密码
3、数据库初始化脚本
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 AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
-- the table to store GlobalSession data
CREATE TABLE IF NOT EXISTS `global_table`
(
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`status` TINYINT NOT NULL,
`application_id` VARCHAR(32),
`transaction_service_group` VARCHAR(32),
`transaction_name` VARCHAR(128),
`timeout` INT,
`begin_time` BIGINT,
`application_data` VARCHAR(2000),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`xid`),
KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(
`branch_id` BIGINT NOT NULL,
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`resource_group_id` VARCHAR(32),
`resource_id` VARCHAR(256),
`branch_type` VARCHAR(8),
`status` TINYINT,
`client_id` VARCHAR(64),
`application_data` VARCHAR(2000),
`gmt_create` DATETIME(6),
`gmt_modified` DATETIME(6),
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
`row_key` VARCHAR(128) NOT NULL,
`xid` VARCHAR(96),
`transaction_id` BIGINT,
`branch_id` BIGINT NOT NULL,
`resource_id` VARCHAR(256),
`table_name` VARCHAR(32),
`pk` VARCHAR(36),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`row_key`),
KEY `idx_branch_id` (`branch_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
完成以上步骤后查看nacos配置
接下来进行项目开发及配置
项目结构
图片
两个子模块:
dt-account-service 用户模块
dt-storage-service 库存模块
父工程依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>${seata.version}</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
account帐号模块
核心配置文件
spring:
cloud:
nacos:
password: nacos
username: nacos
discovery:
server-addr: 118.24.111.33:8848
namespace: ""
group: dt-group
---
seata:
tx-service-group: dt-group
registry:
type: nacos
nacos:
application: seata-server #这里要与seata中的registry.conf配置的application一致
group: dt-group
namespace: ""
server-addr: 118.24.111.33:8848
username: nacos
password: nacos
config:
type: nacos
nacos:
namespace: ""
group: dt-group
server-addr: 118.24.111.33:8848
username: nacos
password: nacos
AccountService服务类
@Service
public class AccountService {
private static final String ERROR_USER_ID = "1002";
@Resource
private AccountMapper accountMapper ;
@Resource
private StorageFeignClient storageFeignClient;
@Transactional(rollbackFor = Exception.class)
@GlobalTransactional
public void debit(String userId, BigDecimal num, String commodityCode, int orderCount) {
System.out.println(RootContext.getXID()) ;
accountMapper.updateAccount(num, userId) ;
storageFeignClient.deduct(commodityCode, orderCount);
try {
TimeUnit.MILLISECONDS.sleep(new Random().nextInt(100)) ;
} catch (InterruptedException e) {
e.printStackTrace();
}
if (ERROR_USER_ID.equals(userId)) {
throw new RuntimeException("account branch exception");
}
}
}
这里模拟了抛出异常当输入的userId为1002时抛出异常,就为了测试所有服务的事务是否回滚了。注意这里需要添加@GlobalTransactional注解
Feign接口
@FeignClient(name = "storage-service", url = "127.0.0.1:8802")
public interface StorageFeignClient {
@GetMapping("/storage/deduct")
void deduct(@RequestParam("commodityCode") String commodityCode, @RequestParam("count") Integer count);
}
storage库存子模块
该子模块的配置与account模块的配置基本一致
StorageService
@Service
public class StorageService {
@Resource
private StorageMapper storageMapper ;
@Transactional
public void deduct(String commodityCode, int count) {
System.out.println(RootContext.getXID()) ;
storageMapper.updateStorage(count, commodityCode) ;
}
}
测试
启动seata,nacos,account,storage服务后查看nacos
图片
seata服务也已经注册上来了。
数据库初始化数据
图片
图片
正常请求
图片
数据变化
图片
当传入userId=1002时
图片
account模块控制台
图片
storage模块控制台
图片
数据库数据
图片
数据没有任何变化说明回滚了