使用Spring Boot实现分布式事务
在微服务架构中,分布式事务是一个重要的概念,它用于确保在分布式系统中各个服务之间的数据一致性。分布式事务的实现相对复杂,因为它需要跨越多个服务、数据库或消息队列来维护数据的一致性。本文将详细介绍如何使用Spring Boot实现分布式事务,重点介绍如何使用Spring Cloud和Seata框架来管理分布式事务。
一、分布式事务的基本概念
分布式事务是指在多个不同的数据库或服务之间执行的事务操作,这些操作需要保证一致性和完整性。分布式事务通常通过两阶段提交(2PC)、三阶段提交(3PC)或TCC(Try-Confirm/Cancel)等协议来实现。
二、Seata简介
Seata(Simple Extensible Autonomous Transaction Architecture)是阿里巴巴开源的一款分布式事务解决方案,提供了高效且易用的分布式事务服务。Seata支持AT、TCC、SAGA和XA等多种事务模式。
三、环境准备
- JDK 8或以上版本
- Maven 3或以上版本
- MySQL数据库
- Seata Server
四、创建Spring Boot项目
首先,我们使用Spring Initializr创建一个新的Spring Boot项目,并添加必要的依赖。
- 打开 Spring Initializr
- 输入项目信息:
- Group:
cn.juwatech
- Artifact:
spring-boot-seata
- Group:
- 添加依赖:
- Spring Web
- Spring Data JPA
- MySQL Driver
- Seata Spring Boot Starter
点击“Generate”按钮生成项目并下载,然后解压项目文件。
五、配置Seata
- 在
src/main/resources
目录下创建application.yml
配置文件,添加以下内容:
server:
port: 8080
spring:
application:
name: spring-boot-seata
datasource:
url: jdbc:mysql://localhost:3306/seata_example
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
seata:
enabled: true
application-id: spring-boot-seata
tx-service-group: my_test_tx_group
- 在
src/main/resources
目录下创建seata.conf
配置文件,添加以下内容:
transport {
type = "TCP"
server = "NIO"
heartbeat = "true"
serialization = "seata"
compressor = "none"
}
service {
vgroupMapping.my_test_tx_group = "default"
enableDegrade = false
disableGlobalTransaction = false
}
client {
rm {
asyncCommitBufferLimit = 10000
lock {
retryTimes = 30
retryInterval = 10
}
reportRetryCount = 5
tableMetaCheckEnable = false
}
tm {
commitRetryCount = 5
rollbackRetryCount = 5
}
undo {
dataValidation = true
logSerialization = "jackson"
onlyCareUpdateColumns = true
}
}
support {
spring {
datasource-autoproxy = false
}
}
六、编写业务逻辑
- 创建实体类
Order
和Account
:
package cn.juwatech.seata.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String userId;
private String productId;
private Integer count;
private Double money;
// getters and setters
}
package cn.juwatech.seata.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String userId;
private Double balance;
// getters and setters
}
- 创建
OrderRepository
和AccountRepository
:
package cn.juwatech.seata.repository;
import cn.juwatech.seata.entity.Order;
import org.springframework.data.jpa.repository.JpaRepository;
public interface OrderRepository extends JpaRepository<Order, Long> {
}
package cn.juwatech.seata.repository;
import cn.juwatech.seata.entity.Account;
import org.springframework.data.jpa.repository.JpaRepository;
public interface AccountRepository extends JpaRepository<Account, Long> {
Account findByUserId(String userId);
}
- 创建服务类
OrderService
和AccountService
:
package cn.juwatech.seata.service;
import cn.juwatech.seata.entity.Order;
import cn.juwatech.seata.repository.OrderRepository;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@GlobalTransactional(name = "my_test_tx_group", rollbackFor = Exception.class)
public void createOrder(Order order) {
orderRepository.save(order);
}
}
package cn.juwatech.seata.service;
import cn.juwatech.seata.entity.Account;
import cn.juwatech.seata.repository.AccountRepository;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AccountService {
@Autowired
private AccountRepository accountRepository;
@GlobalTransactional(name = "my_test_tx_group", rollbackFor = Exception.class)
public void decreaseBalance(String userId, double amount) {
Account account = accountRepository.findByUserId(userId);
if (account != null && account.getBalance() >= amount) {
account.setBalance(account.getBalance() - amount);
accountRepository.save(account);
} else {
throw new RuntimeException("Insufficient balance");
}
}
}
- 创建控制器
OrderController
:
package cn.juwatech.seata.controller;
import cn.juwatech.seata.entity.Order;
import cn.juwatech.seata.service.AccountService;
import cn.juwatech.seata.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/orders")
public class OrderController {
@Autowired
private OrderService orderService;
@Autowired
private AccountService accountService;
@PostMapping("/create")
public String createOrder(@RequestBody Order order) {
orderService.createOrder(order);
accountService.decreaseBalance(order.getUserId(), order.getMoney());
return "Order created successfully";
}
}
七、运行并验证
启动 Spring Boot 应用程序,并验证分布式事务功能。
- 运行
SpringBootSeataApplication
类,启动 Spring Boot 应用程序。 - 使用 Postman 或其他工具发送 POST 请求到
http://localhost:8080/orders/create
,请求体为:
{
"userId": "1",
"productId": "P1001",
"count": 2,
"money": 200.00
}
- 检查 MySQL 数据库,验证订单和账户信息是否正确更新。
通过以上步骤,我们成功地将 Spring Boot 应用与 Seata 集成,实现了分布式事务管理。Seata 提供了强大的分布式事务管理功能,使得在微服务架构中实现数据一致性变得更加容易和高效。