学习时间: 4-5小时
学习目标: 掌握分布式事务管理,学会使用Seata实现分布式事务,理解数据一致性保证
详细学习清单
✅ 第一部分:分布式事务基础概念(60分钟)
1. 分布式事务的挑战
分布式事务问题分析
java
// DistributedTransactionChallenges.java
package com.example.demo.distributed;
import java.util.List;
import java.util.ArrayList;
public class DistributedTransactionChallenges {
public static class Challenge {
private String name;
private String description;
private String example;
private String solution;
public Challenge(String name, String description, String example, String solution) {
this.name = name;
this.description = description;
this.example = example;
this.service = solution;
}
// Getter方法
public String getName() { return name; }
public String getDescription() { return description; }
public String getExample() { return example; }
public String getSolution() { return solution; }
}
public static void main(String[] args) {
List<Challenge> challenges = new ArrayList<>();
// 网络分区问题
challenges.add(new Challenge(
"网络分区",
"分布式系统中网络不稳定导致节点间通信中断",
"订单服务无法连接库存服务,导致数据不一致",
"使用心跳检测、超时重试、熔断器模式"
));
// 时钟同步问题
challenges.add(new Challenge(
"时钟同步",
"不同节点时钟不同步,影响事务顺序判断",
"两个订单同时创建,时间戳相同但顺序错误",
"使用逻辑时钟、向量时钟、全局序列号"
));
// 数据一致性问题
challenges.add(new Challenge(
"数据一致性",
"多个服务数据状态不一致,违反业务规则",
"订单已创建但库存未扣减,或支付成功但订单未更新",
"使用分布式事务、最终一致性、补偿机制"
));
// 服务可用性问题
challenges.add(new Challenge(
"服务可用性",
"某个服务不可用影响整个业务流程",
"支付服务宕机导致订单无法完成",
"使用服务降级、异步处理、消息队列"
));
System.out.println("=== 分布式事务主要挑战 ===");
for (Challenge challenge : challenges) {
System.out.println("\n挑战名称: " + challenge.getName());
System.out.println("问题描述: " + challenge.getDescription());
System.out.println("具体示例: " + challenge.getExample());
System.out.println("解决方案: " + challenge.getSolution());
}
}
}
2. CAP理论与BASE理论
CAP理论分析
java
// CAPTheory.java
package com.example.demo.distributed;
import java.util.HashMap;
import java.util.Map;
public class CAPTheory {
public static class CAPProperty {
private String name;
private String description;
private String tradeoff;
private List<String> examples;
public CAPProperty(String name, String description, String tradeoff) {
this.name = name;
this.description = description;
this.tradeoff = tradeoff;
this.examples = new ArrayList<>();
}
public void addExample(String example) {
this.examples.add(example);
}
// Getter方法
public String getName() { return name; }
public String getDescription() { return description; }
public String getTradeoff() { return tradeoff; }
public List<String> getExamples() { return examples; }
}
public static void main(String[] args) {
Map<String, CAPProperty> capProperties = new HashMap<>();
// 一致性 (Consistency)
CAPProperty consistency = new CAPProperty(
"一致性 (Consistency)",
"所有节点在同一时间看到的数据是一致的",
"强一致性 vs 可用性"
);
consistency.addExample("银行转账:A账户扣款,B账户必须同时增加");
consistency.addExample("库存管理:所有节点看到的库存数量必须一致");
capProperties.put("C", consistency);
// 可用性 (Availability)
CAPProperty availability = new CAPProperty(
"可用性 (Availability)",
"每个请求都能收到响应,但不保证是最新数据",
"服务可用 vs 数据一致性"
);
availability.addExample("电商网站:即使数据不是最新的,也要能正常访问");
availability.addExample("缓存系统:优先返回缓存数据,保证响应速度");
capProperties.put("A", availability);
// 分区容错性 (Partition Tolerance)
CAPProperty partitionTolerance = new CAPProperty(
"分区容错性 (Partition Tolerance)",
"系统在网络分区情况下仍能继续工作",
"网络分区 vs 系统可用性"
);
partitionTolerance.addExample("微服务架构:服务间网络中断时仍能独立工作");
partitionTolerance.addExample("分布式存储:部分节点不可用时仍能提供服务");
capProperties.put("P", partitionTolerance);
System.out.println("=== CAP理论分析 ===");
for (Map.Entry<String, CAPProperty> entry : capProperties.entrySet()) {
CAPProperty property = entry.getValue();
System.out.println("\n" + entry.getKey() + " - " + property.getName());
System.out.println("描述: " + property.getDescription());
System.out.println("权衡: " + property.getTradeoff());
System.out.println("示例:");
for (String example : property.getExamples()) {
System.out.println(" - " + example);
}
}
System.out.println("\n=== CAP理论组合 ===");
System.out.println("CP (一致性+分区容错): 如分布式数据库");
System.out.println("AP (可用性+分区容错): 如NoSQL数据库");
System.out.println("CA (一致性+可用性): 单机系统,无法实现分区容错");
}
}
✅ 第二部分:Seata分布式事务框架(90分钟)
1. Seata架构与配置
Seata服务配置
xml
<!-- seata-server/pom.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>microservice-parent</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>seata-server</artifactId>
<dependencies>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-server</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
</dependencies>
</project>
Seata配置文件
conf
# seata-server/conf/registry.conf
registry {
type = "nacos"
nacos {
application = "seata-server"
serverAddr = "127.0.0.1:8848"
group = "SEATA_GROUP"
namespace = ""
username = "nacos"
password = "nacos"
}
}
config {
type = "nacos"
nacos {
serverAddr = "127.0.0.1:8848"
namespace = ""
group = "SEATA_GROUP"
username = "nacos"
password = "nacos"
dataId = "seataServer.properties"
}
}
Seata服务端配置
conf
# seata-server/conf/file.conf
service {
vgroupMapping.my_test_tx_group = "default"
default.grouplist = "127.0.0.1:8091"
enableDegrade = false
disableGlobalTransaction = false
}
client {
rm {
asyncCommitBufferLimit = 10000
lock {
retryInterval = 10
retryTimes = 30
retryPolicyBranchRollbackOnConflict = true
}
reportRetryCount = 5
tableMetaCheckEnable = false
reportSuccessEnable = false
}
tm {
commitRetryCount = 5
rollbackRetryCount = 5
}
undo {
dataValidation = true
logSerialization = "jackson"
onlyCareUpdateColumns = true
compress {
enable = true
type = zip
threshold = 64k
}
}
}
store {
mode = "db"
db {
url = "jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai"
dbType = "mysql"
driverClassName = "com.mysql.cj.jdbc.Driver"
username = "seata"
password = "seata123"
minConn = 5
maxConn = 100
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
maxWait = 5000
}
}
2. 业务服务集成Seata
订单服务配置
xml
<!-- order-service/pom.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>microservice-parent</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>order-service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
订单服务配置
yaml
# order-service/src/main/resources/application.yml
server:
port: 8081
spring:
application:
name: order-service
datasource:
url: jdbc:mysql://localhost:3306/order_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
cloud:
alibaba:
seata:
tx-service-group: my_test_tx_group
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
logging:
level:
io.seata: debug
com.example.order: debug
# Seata配置
seata:
enabled: true
application-id: ${spring.application.name}
tx-service-group: my_test_tx_group
enable-auto-data-source-proxy: true
service:
vgroup-mapping:
my_test_tx_group: default
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
namespace: ""
username: nacos
password: nacos
config:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: ""
group: SEATA_GROUP
username: nacos
password: nacos
data-id: seataServer.properties
3. 订单服务实现
订单实体类
java
// Order.java
package com.example.order.model;
import java.math.BigDecimal;
import java.time.LocalDateTime;
public class Order {
private Long id;
private String orderNumber;
private Long userId;
private Long productId;
private Integer quantity;
private BigDecimal unitPrice;
private BigDecimal totalAmount;
private String status; // PENDING, PAID, CANCELLED, COMPLETED
private LocalDateTime createTime;
private LocalDateTime updateTime;
private String remark;
// 构造函数
public Order() {}
public Order(String orderNumber, Long userId, Long productId, Integer quantity, BigDecimal unitPrice) {
this.orderNumber = orderNumber;
this.userId = userId;
this.productId = productId;
this.quantity = quantity;
this.unitPrice = unitPrice;
this.totalAmount = unitPrice.multiply(new BigDecimal(quantity));
this.status = "PENDING";
this.createTime = LocalDateTime.now();
this.updateTime = LocalDateTime.now();
}
// Getter和Setter方法
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getOrderNumber() { return orderNumber; }
public void setOrderNumber(String orderNumber) { this.orderNumber = orderNumber; }
public Long getUserId() { return userId; }
public void setUserId(Long userId) { this.userId = userId; }
public Long getProductId() { return productId; }
public void setProductId(Long productId) { this.productId = productId; }
public Integer getQuantity() { return quantity; }
public void setQuantity(Integer quantity) { this.quantity = quantity; }
public BigDecimal getUnitPrice() { return unitPrice; }
public void setUnitPrice(BigDecimal unitPrice) { this.unitPrice = unitPrice; }
public BigDecimal getTotalAmount() { return totalAmount; }
public void setTotalAmount(BigDecimal totalAmount) { this.totalAmount = totalAmount; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public LocalDateTime getCreateTime() { return createTime; }
public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime; }
public LocalDateTime getUpdateTime() { return updateTime; }
public void setUpdateTime(LocalDateTime updateTime) { this.updateTime = updateTime; }
public String getRemark() { return remark; }
public void setRemark(String remark) { this.remark = remark; }
}
订单服务接口
java
// OrderService.java
package com.example.order.service;
import com.example.order.model.Order;
import java.util.List;
public interface OrderService {
/**
* 创建订单
*/
Order createOrder(Order order);
/**
* 根据ID获取订单
*/
Order getOrderById(Long id);
/**
* 根据用户ID获取订单列表
*/
List<Order> getOrdersByUserId(Long userId);
/**
* 更新订单状态
*/
boolean updateOrderStatus(Long orderId, String status);
/**
* 删除订单
*/
boolean deleteOrder(Long id);
/**
* 获取所有订单
*/
List<Order> getAllOrders();
}
订单服务实现
java
// OrderServiceImpl.java
package com.example.order.service.impl;
import com.example.order.model.Order;
import com.example.order.mapper.OrderMapper;
import com.example.order.service.OrderService;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Override
@Transactional
public Order createOrder(Order order) {
// 生成订单号
if (order.getOrderNumber() == null) {
order.setOrderNumber(generateOrderNumber());
}
// 计算总金额
if (order.getTotalAmount() == null) {
order.setTotalAmount(order.getUnitPrice().multiply(new java.math.BigDecimal(order.getQuantity())));
}
// 保存订单
orderMapper.insert(order);
return order;
}
@Override
public Order getOrderById(Long id) {
return orderMapper.selectById(id);
}
@Override
public List<Order> getOrdersByUserId(Long userId) {
return orderMapper.selectByUserId(userId);
}
@Override
@Transactional
public boolean updateOrderStatus(Long orderId, String status) {
Order order = orderMapper.selectById(orderId);
if (order != null) {
order.setStatus(status);
order.setUpdateTime(java.time.LocalDateTime.now());
return orderMapper.updateById(order) > 0;
}
return false;
}
@Override
@Transactional
public boolean deleteOrder(Long id) {
return orderMapper.deleteById(id) > 0;
}
@Override
public List<Order> getAllOrders() {
return orderMapper.selectAll();
}
/**
* 生成订单号
*/
private String generateOrderNumber() {
return "ORD" + System.currentTimeMillis() + String.format("%04d", (int)(Math.random() * 10000));
}
}
订单Mapper接口
java
// OrderMapper.java
package com.example.order.mapper;
import com.example.order.model.Order;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface OrderMapper {
/**
* 插入订单
*/
int insert(Order order);
/**
* 根据ID查询订单
*/
Order selectById(@Param("id") Long id);
/**
* 根据用户ID查询订单
*/
List<Order> selectByUserId(@Param("userId") Long userId);
/**
* 更新订单
*/
int updateById(Order order);
/**
* 删除订单
*/
int deleteById(@Param("id") Long id);
/**
* 查询所有订单
*/
List<Order> selectAll();
}
✅ 第三部分:分布式事务业务逻辑(90分钟)
1. 订单创建事务
订单创建服务
java
// OrderCreationService.java
package com.example.order.service;
import com.example.order.model.Order;
import com.example.order.feign.ProductFeignClient;
import com.example.order.feign.InventoryFeignClient;
import com.example.order.feign.PaymentFeignClient;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
@Service
public class OrderCreationService {
@Autowired
private OrderService orderService;
@Autowired
private ProductFeignClient productFeignClient;
@Autowired
private InventoryFeignClient inventoryFeignClient;
@Autowired
private PaymentFeignClient paymentFeignClient;
/**
* 创建订单(分布式事务)
*/
@GlobalTransactional(name = "create-order-transaction", rollbackFor = Exception.class)
public Order createOrderWithTransaction(Order order) throws Exception {
try {
// 1. 验证商品信息
validateProduct(order.getProductId());
// 2. 检查库存
checkInventory(order.getProductId(), order.getQuantity());
// 3. 扣减库存
deductInventory(order.getProductId(), order.getQuantity());
// 4. 创建订单
Order createdOrder = orderService.createOrder(order);
// 5. 创建支付记录
createPaymentRecord(createdOrder);
// 6. 发送订单创建通知
sendOrderNotification(createdOrder);
return createdOrder;
} catch (Exception e) {
// 分布式事务会自动回滚
throw new RuntimeException("创建订单失败: " + e.getMessage(), e);
}
}
/**
* 验证商品信息
*/
private void validateProduct(Long productId) {
try {
ProductDTO product = productFeignClient.getProduct(productId);
if (product == null || !"ACTIVE".equals(product.getStatus())) {
throw new RuntimeException("商品不存在或已下架");
}
} catch (Exception e) {
throw new RuntimeException("验证商品信息失败: " + e.getMessage(), e);
}
}
/**
* 检查库存
*/
private void checkInventory(Long productId, Integer quantity) {
try {
InventoryDTO inventory = inventoryFeignClient.getInventory(productId);
if (inventory == null || inventory.getAvailableQuantity() < quantity) {
throw new RuntimeException("库存不足");
}
} catch (Exception e) {
throw new RuntimeException("检查库存失败: " + e.getMessage(), e);
}
}
/**
* 扣减库存
*/
private void deductInventory(Long productId, Integer quantity) {
try {
boolean success = inventoryFeignClient.deductInventory(productId, quantity);
if (!success) {
throw new RuntimeException("扣减库存失败");
}
} catch (Exception e) {
throw new RuntimeException("扣减库存失败: " + e.getMessage(), e);
}
}
/**
* 创建支付记录
*/
private void createPaymentRecord(Order order) {
try {
PaymentDTO payment = new PaymentDTO();
payment.setOrderId(order.getId());
payment.setOrderNumber(order.getOrderNumber());
payment.setAmount(order.getTotalAmount());
payment.setStatus("PENDING");
PaymentDTO createdPayment = paymentFeignClient.createPayment(payment);
if (createdPayment == null) {
throw new RuntimeException("创建支付记录失败");
}
} catch (Exception e) {
throw new RuntimeException("创建支付记录失败: " + e.getMessage(), e);
}
}
/**
* 发送订单创建通知
*/
private void sendOrderNotification(Order order) {
try {
NotificationDTO notification = new NotificationDTO();
notification.setUserId(order.getUserId());
notification.setType("ORDER_CREATED");
notification.setTitle("订单创建成功");
notification.setContent("您的订单 " + order.getOrderNumber() + " 已创建成功");
notificationFeignClient.sendNotification(notification);
} catch (Exception e) {
// 通知失败不影响主流程
log.warn("发送订单通知失败: " + e.getMessage());
}
}
}
2. Feign客户端接口
商品服务Feign客户端
java
// ProductFeignClient.java
package com.example.order.feign;
import com.example.order.dto.ProductDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(name = "product-service", fallback = ProductFeignFallback.class)
public interface ProductFeignClient {
@GetMapping("/api/products/{id}")
ProductDTO getProduct(@PathVariable("id") Long id);
}
库存服务Feign客户端
java
// InventoryFeignClient.java
package com.example.order.feign;
import com.example.order.dto.InventoryDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
@FeignClient(name = "inventory-service", fallback = InventoryFeignFallback.class)
public interface InventoryFeignClient {
@GetMapping("/api/inventory/{productId}")
InventoryDTO getInventory(@PathVariable("productId") Long productId);
@PostMapping("/api/inventory/{productId}/deduct")
boolean deductInventory(@PathVariable("productId") Long productId,
@RequestParam("quantity") Integer quantity);
}
支付服务Feign客户端
java
// PaymentFeignClient.java
package com.example.order.feign;
import com.example.order.dto.PaymentDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
@FeignClient(name = "payment-service", fallback = PaymentFeignFallback.class)
public interface PaymentFeignClient {
@PostMapping("/api/payments")
PaymentDTO createPayment(@RequestBody PaymentDTO payment);
@GetMapping("/api/payments/{id}")
PaymentDTO getPayment(@PathVariable("id") Long id);
}
3. DTO类定义
商品DTO
java
// ProductDTO.java
package com.example.order.dto;
import java.math.BigDecimal;
import java.time.LocalDateTime;
public class ProductDTO {
private Long id;
private String name;
private String description;
private BigDecimal price;
private String category;
private String status; // ACTIVE, INACTIVE, DELETED
private LocalDateTime createTime;
private LocalDateTime updateTime;
// 构造函数
public ProductDTO() {}
// Getter和Setter方法
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public BigDecimal getPrice() { return price; }
public void setPrice(BigDecimal price) { this.price = price; }
public String getCategory() { return category; }
public void setCategory(String category) { this.category = category; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public LocalDateTime getCreateTime() { return createTime; }
public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime; }
public LocalDateTime getUpdateTime() { return updateTime; }
public void setUpdateTime(LocalDateTime updateTime) { this.updateTime = updateTime; }
}
库存DTO
java
// InventoryDTO.java
package com.example.order.dto;
import java.time.LocalDateTime;
public class InventoryDTO {
private Long id;
private Long productId;
private Integer totalQuantity;
private Integer availableQuantity;
private Integer reservedQuantity;
private LocalDateTime createTime;
private LocalDateTime updateTime;
// 构造函数
public InventoryDTO() {}
// Getter和Setter方法
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public Long getProductId() { return productId; }
public void setProductId(Long productId) { this.productId = productId; }
public Integer getTotalQuantity() { return totalQuantity; }
public void setTotalQuantity(Integer totalQuantity) { this.totalQuantity = totalQuantity; }
public Integer getAvailableQuantity() { return availableQuantity; }
public void setAvailableQuantity(Integer availableQuantity) { this.availableQuantity = availableQuantity; }
public Integer getReservedQuantity() { return reservedQuantity; }
public void setReservedQuantity(Integer reservedQuantity) { this.reservedQuantity = reservedQuantity; }
public LocalDateTime getCreateTime() { return createTime; }
public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime; }
public LocalDateTime getUpdateTime() { return updateTime; }
public void setUpdateTime(LocalDateTime updateTime) { this.updateTime = updateTime; }
}
支付DTO
java
// PaymentDTO.java
package com.example.order.dto;
import java.math.BigDecimal;
import java.time.LocalDateTime;
public class PaymentDTO {
private Long id;
private Long orderId;
private String orderNumber;
private BigDecimal amount;
private String paymentMethod; // CREDIT_CARD, DEBIT_CARD, BANK_TRANSFER
private String status; // PENDING, SUCCESS, FAILED, CANCELLED
private String transactionId;
private LocalDateTime createTime;
private LocalDateTime updateTime;
// 构造函数
public PaymentDTO() {}
// Getter和Setter方法
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public Long getOrderId() { return orderId; }
public void setOrderId(Long orderId) { this.orderId = orderId; }
public String getOrderNumber() { return orderNumber; }
public void setOrderNumber(String orderNumber) { this.orderNumber = orderNumber; }
public BigDecimal getAmount() { return amount; }
public void setAmount(BigDecimal amount) { this.amount = amount; }
public String getPaymentMethod() { return paymentMethod; }
public void setPaymentMethod(String paymentMethod) { this.paymentMethod = paymentMethod; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public String getTransactionId() { return transactionId; }
public void setTransactionId(String transactionId) { this.transactionId = transactionId; }
public LocalDateTime getCreateTime() { return createTime; }
public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime; }
public LocalDateTime getUpdateTime() { return updateTime; }
public void setUpdateTime(LocalDateTime updateTime) { this.updateTime = updateTime; }
}
✅ 第四部分:事务监控与异常处理(60分钟)
1. 事务监控配置
Seata监控配置
java
// SeataMonitoringConfig.java
package com.example.order.config;
import io.seata.spring.annotation.GlobalTransactionScanner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SeataMonitoringConfig {
@Bean
public GlobalTransactionScanner globalTransactionScanner() {
return new GlobalTransactionScanner("order-service", "my_test_tx_group");
}
}
事务监控服务
java
// TransactionMonitoringService.java
package com.example.order.service;
import io.seata.core.context.RootContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Service
public class TransactionMonitoringService {
private static final Logger logger = LoggerFactory.getLogger(TransactionMonitoringService.class);
// 事务统计信息
private final Map<String, TransactionStats> transactionStats = new ConcurrentHashMap<>();
/**
* 记录事务开始
*/
public void recordTransactionStart(String businessKey) {
String xid = RootContext.getXID();
if (xid != null) {
TransactionStats stats = new TransactionStats();
stats.setXid(xid);
stats.setBusinessKey(businessKey);
stats.setStartTime(System.currentTimeMillis());
stats.setStatus("RUNNING");
transactionStats.put(xid, stats);
logger.info("事务开始 - XID: {}, 业务键: {}", xid, businessKey);
}
}
/**
* 记录事务提交
*/
public void recordTransactionCommit(String xid) {
TransactionStats stats = transactionStats.get(xid);
if (stats != null) {
stats.setEndTime(System.currentTimeMillis());
stats.setStatus("COMMITTED");
stats.setDuration(stats.getEndTime() - stats.getStartTime());
logger.info("事务提交 - XID: {}, 耗时: {}ms", xid, stats.getDuration());
}
}
/**
* 记录事务回滚
*/
public void recordTransactionRollback(String xid, String reason) {
TransactionStats stats = transactionStats.get(xid);
if (stats != null) {
stats.setEndTime(System.currentTimeMillis());
stats.setStatus("ROLLBACK");
stats.setDuration(stats.getEndTime() - stats.getStartTime());
stats.setRollbackReason(reason);
logger.warn("事务回滚 - XID: {}, 原因: {}, 耗时: {}ms", xid, reason, stats.getDuration());
}
}
/**
* 获取事务统计信息
*/
public Map<String, TransactionStats> getTransactionStats() {
return new ConcurrentHashMap<>(transactionStats);
}
/**
* 清理已完成的事务统计
*/
public void cleanupCompletedTransactions() {
transactionStats.entrySet().removeIf(entry -> {
TransactionStats stats = entry.getValue();
return "COMMITTED".equals(stats.getStatus()) || "ROLLBACK".equals(stats.getStatus());
});
}
/**
* 事务统计信息
*/
public static class TransactionStats {
private String xid;
private String businessKey;
private long startTime;
private long endTime;
private long duration;
private String status;
private String rollbackReason;
// Getter和Setter方法
public String getXid() { return xid; }
public void setXid(String xid) { this.xid = xid; }
public String getBusinessKey() { return businessKey; }
public void setBusinessKey(String businessKey) { this.businessKey = businessKey; }
public long getStartTime() { return startTime; }
public void setStartTime(long startTime) { this.startTime = startTime; }
public long getEndTime() { return endTime; }
public void setEndTime(long endTime) { this.endTime = endTime; }
public long getDuration() { return duration; }
public void setDuration(long duration) { this.duration = duration; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public String getRollbackReason() { return rollbackReason; }
public void setRollbackReason(String rollbackReason) { this.rollbackReason = rollbackReason; }
}
}
2. 异常处理与补偿机制
分布式事务异常处理
java
// DistributedTransactionExceptionHandler.java
package com.example.order.exception;
import com.example.order.service.TransactionMonitoringService;
import io.seata.core.context.RootContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice
public class DistributedTransactionExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(DistributedTransactionExceptionHandler.class);
@Autowired
private TransactionMonitoringService monitoringService;
/**
* 处理分布式事务异常
*/
@ExceptionHandler(Exception.class)
public Map<String, Object> handleException(Exception e) {
String xid = RootContext.getXID();
// 记录事务回滚
if (xid != null) {
monitoringService.recordTransactionRollback(xid, e.getMessage());
}
logger.error("分布式事务异常 - XID: {}, 错误: {}", xid, e.getMessage(), e);
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "操作失败,请稍后重试");
response.put("error", e.getMessage());
response.put("xid", xid);
return response;
}
/**
* 处理业务异常
*/
@ExceptionHandler(BusinessException.class)
public Map<String, Object> handleBusinessException(BusinessException e) {
String xid = RootContext.getXID();
logger.warn("业务异常 - XID: {}, 错误: {}", xid, e.getMessage());
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", e.getMessage());
response.put("errorCode", e.getErrorCode());
response.put("xid", xid);
return response;
}
}
业务异常类
java
// BusinessException.java
package com.example.order.exception;
public class BusinessException extends RuntimeException {
private String errorCode;
public BusinessException(String message) {
super(message);
}
public BusinessException(String errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
public BusinessException(String message, Throwable cause) {
super(message, cause);
}
public BusinessException(String errorCode, String message, Throwable cause) {
super(message, cause);
this.errorCode = errorCode;
}
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
}
🎯 今日学习总结
1. 掌握的核心技能
- ✅ 分布式事务基础概念
- ✅ CAP理论与BASE理论
- ✅ Seata框架配置与使用
- ✅ 分布式事务业务实现
- ✅ 事务监控与异常处理
2. 分布式事务核心概念
- 网络分区:节点间通信中断的解决方案
- 时钟同步:逻辑时钟和向量时钟的使用
- 数据一致性:强一致性与最终一致性
- 服务可用性:降级策略和异步处理
3. Seata框架特点
- AT模式:自动事务管理,对业务无侵入
- TCC模式:手动事务控制,性能更好
- Saga模式:长事务处理,支持补偿机制
- XA模式:强一致性事务,性能相对较低
4. 事务监控要点
- XID追踪:全局事务ID的生成和传播
- 性能统计:事务执行时间和成功率
- 异常分析:回滚原因和补偿策略
- 资源管理:连接池和锁资源的监控
5. 下一步学习方向
- 消息队列与异步处理
- 分布式锁实现
- 分布式缓存策略
- 服务降级与熔断
- 性能优化与调优
学习建议
- 理论理解:深入理解CAP理论和分布式事务的挑战
- 实践操作:搭建Seata环境,实现完整的分布式事务流程
- 异常处理:学会处理各种异常情况,实现补偿机制
- 性能优化:理解不同事务模式的性能特点,选择合适的模式
- 监控运维:学会使用监控工具分析事务性能