概述
AT模式运行机制
AT模式的特点就是业务无侵入式,整体机制分二阶段提交
- 两阶段提交协议的演变:
Seata具体实现步骤
- TM端使用@GlobalTransaction进行全局事务开启、提交、回滚。
- TM开始RPC调用远程服务。
- RM端seata-client通过扩展DataSourceProxy,实现自动生成undo_log与TC上报。
- TM告知TC提交/回滚全局事务。
- TC通知RM各自执行commit/rollback操作,同时清楚undo_log。
编写AT模式代码
RM实现
创建订单和库存服务的DB、数据表
订单库:st_order
库存库:st_storage
-- 库存服务DB执行
CREATE TABLE `tab_storage` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`product_id` bigint(11) DEFAULT NULL COMMENT '产品id',
`total` int(11) DEFAULT NULL COMMENT '总库存',
`used` int(11) DEFAULT NULL COMMENT '已用库存',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
INSERT INTO `tab_storage` (`product_id`, `total`,`used`)VALUES ('1', '96', '4');
INSERT INTO `tab_storage` (`product_id`, `total`,`used`)VALUES ('2', '100','0');
-- 订单服务DB执行
CREATE TABLE `tab_order` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`user_id` bigint(11) DEFAULT NULL COMMENT '用户id',
`product_id` bigint(11) DEFAULT NULL COMMENT '产品id',
`count` int(11) DEFAULT NULL COMMENT '数量',
`money` decimal(11,0) DEFAULT NULL COMMENT '金额',
`status` int(1) DEFAULT NULL COMMENT '订单状态:0:创建中;1:已完成',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
各数据库加入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 AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
添加seata pom.xml依赖
demo1模型的pom.xml文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath/>
</parent>
......
<properties>
<springboot.verison>2.4.2.RELEASE</springboot.verison>
<java.version>1.8</java.version>
<mybatis.version>2.1.5</mybatis.version>
<tk-mapper.version>4.1.5</tk-mapper.version>
<seata.version>1.3.0</seata.version>
</properties>
......
<!--demo01父模块中添加依赖-->
<dependencyManagement>
<dependencies>
<!--Mybatis通用Mapper-->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>${tk-mapper.version}</version>
</dependency>
<!--SpringCloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--Spring Alibaba Cloud-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
子模块order-server和storage-service的pom文件。
<!--子模块order-service和storage-service的pom中添加nacos和seata依赖-->
<dependencies>
<!--nacos注册中心和配置中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<!--移除掉该starter中自带的依赖,该依赖版本较低-->
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--单独添加seata 1.3.0的依赖-->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>10.2.3</version>
</dependency>
<!--Mybatis通用Mapper-->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
nacos中增加事务群组配置
增加配置项:service.vgroupMapping.default-fescar-service-group,值为default(应该代表微服务集群名称,默认集群名称default)。
这个配置项,用来不同的微服务发现 协调者服务(seata-server)使用。
default-fescar-service-group格式为:{xxx}-fescar-service-group,多个微服务可以使用一个,也可以每个服务独立使用一个,为了简单这里多个微服务使用一个,下面yml配置会使用这个配置项。
yml配置
order-server的yml配置如下:
server:
port: 6770
spring:
application:
name: order-service
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://127.0.0.1:3306/st_order?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true
cloud:
nacos:
discovery:
server-addr: 192.168.230.128:8848
register-enabled: true
namespace: 62f0ae5b-38ea-4a2d-bb71-70ccbc0a8c10
config:
server-addr: 192.168.230.128:8848
enabled: true
file-extension: yaml
namespace: 62f0ae5b-38ea-4a2d-bb71-70ccbc0a8c10
seata:
enabled: true
application-id: ${spring.application.name}
# 事务群组(可以每个应用独立取名,也可以使用相同的名字),要与nacos中的service.vgroupMapping的后缀对应;
# 命名规则是固定的:vgroupMapping.[springcloud服务名]-fescar-service-group;
# 源码根据这个命名规则获取应用服务的信息;
tx-service-group: default-fescar-service-group
config:
type: nacos
# 需要和server在同一个注册中心下
nacos:
namespace: 62f0ae5b-38ea-4a2d-bb71-70ccbc0a8c10
serverAddr: 192.168.230.128:8848
# 需要server端(registry和config)、nacos配置client端(registry和config)保持一致
group: SEATA_GROUP
username: "nacos"
password: "nacos"
registry:
type: nacos
nacos:
# 需要和server端保持一致,即server在nacos中的名称,默认为seata-server
application: seata-server
server-addr: 192.168.230.128:8848
# 与nacos服务列表中的分组名称一致
group: DEFAULT_GROUP
namespace: 62f0ae5b-38ea-4a2d-bb71-70ccbc0a8c10
username: "nacos"
password: "nacos"
mybatis:
mapperLocations: classpath:mapper/*.xml
storage-server的yml如下:
server:
port: 6780
spring:
application:
name: storage-service
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://127.0.0.1:3306/st_storage?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true
cloud:
nacos:
discovery:
server-addr: 192.168.230.128:8848
register-enabled: true
namespace: 62f0ae5b-38ea-4a2d-bb71-70ccbc0a8c10
config:
server-addr: 192.168.230.128:8848
enabled: true
file-extension: yaml
namespace: 62f0ae5b-38ea-4a2d-bb71-70ccbc0a8c10
seata:
enabled: true
application-id: ${spring.application.name}
# 事务群组(可以每个应用独立取名,也可以使用相同的名字),要与服务端nacos-config.txt中service.vgroup_mapping的后缀对应
tx-service-group: default-fescar-service-group
config:
type: nacos
# 需要和server在同一个注册中心下
nacos:
namespace: 62f0ae5b-38ea-4a2d-bb71-70ccbc0a8c10
serverAddr: 192.168.230.128:8848
# 需要server端(registry和config)、nacos配置client端(registry和config)保持一致
group: SEATA_GROUP
username: "nacos"
password: "nacos"
registry:
type: nacos
nacos:
# 需要和server端保持一致,即server在nacos中的名称,默认为seata-server
application: seata-server
server-addr: 192.168.230.128:8848
# 与nacos服务列表中的分组名称一致
group: DEFAULT_GROUP
namespace: 62f0ae5b-38ea-4a2d-bb71-70ccbc0a8c10
username: "nacos"
password: "nacos"
mybatis:
mapperLocations: classpath:mapper/*.xml
若出现bootstarp.yml中文注释报以下错误的情况,则需要调整编码,如下:
Caused by: org.yaml.snakeyaml.error.YAMLException: java.nio.charset.MalformedInputException: Input length = 1
Caused by: java.nio.charset.MalformedInputException: Input length = 1
编写业务代码
package com.it235.seata.order;
import tk.mybatis.spring.annotation.MapperScan;
@RestController
@SpringBootApplication
@MapperScan("com.it235.seata.order.mapper")
@EnableDiscoveryClient
@EnableFeignClients
public class OrderServiceApplication {
@Autowired
private OrderService orderService;
@GetMapping("order/create")
public Boolean create(long userId , long productId){
Order order = new Order();
order.setCount(1)
.setMoney(BigDecimal.valueOf(88))
.setProductId(productId)
.setUserId(userId)
.setStatus(0);
return orderService.create(order);
}
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Override
public boolean create(Order order) {
log.info("创建订单开始");
int index = orderMapper.insert(order);
log.info("创建订单结束");
return index > 0;
}
}
@Table(name = "tab_order")
@Data
@Accessors(chain = true)
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long userId;
private Long productId;
private int count;
private BigDecimal money;
private int status;
}
@Repository
public interface OrderMapper extends Mapper<Order> {
}
浏览器访问模拟添加订单请求,http://localhost:6770/order/create,查看数据库,此时单个服务搭建完成
依葫芦画瓢,搭建库存服务,同时保证库存服务正常启动注册
TM实现
搭建business服务,pom、bootstrap.yml与RM基本一致,并提供FeignClient调用组件
# feign组件超时设置,用于查看seata数据库中的临时数据内容
feign:
client:
config:
default:
connect-timeout: 30000
read-timeout: 30000
FeignClient组件代码编写
@FeignClient(name = "storage-service")
@Component
public interface StorageClient {
@GetMapping("storage/change")
Boolean changeStorage(@RequestParam("productId") long productId ,@RequestParam("used") int used);
}
@FeignClient(name = "order-service")
@Component
public interface OrderClient {
@GetMapping("order/create")
Boolean create(@RequestParam("userId") long userId ,@RequestParam("productId") long productId);
}
调用层代码
@SpringBootApplication
@RestController
@EnableFeignClients
@EnableDiscoveryClient
public class BusinessServiceApplication {
@Autowired
private OrderClient orderClient;
@Autowired
private StorageClient storageClient;
@GetMapping("buy")
@GlobalTransactional
public String buy(long userId , long productId){
orderClient.create(userId , productId);
storageClient.changeStorage(userId , 1);
return "ok";
}
public static void main(String[] args) {
SpringApplication.run(BusinessServiceApplication.class, args);
}
}
参考
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/100052.html