分布式系统同步与异步处理机制详解
分布式场景下的同步与异步处理机制
在分布式系统中,同步与异步的处理方式直接影响系统的可用性、一致性和性能。本文将深入探讨分布式锁和分布式事务两大核心机制。
一、分布式锁
分布式锁用于协调多个节点对共享资源的访问,防止并发冲突。以下是两种主流实现方式:
1. Redis的RedLock算法
RedLock是Redis官方提出的分布式锁算法,基于多个独立Redis节点实现高可用锁服务。
实现原理:
public class RedLockDemo {
private RedissonClient redisson;
public void doWithLock() {
RLock lock1 = redisson.getLock("lock1");
RLock lock2 = redisson.getLock("lock2");
RLock lock3 = redisson.getLock("lock3");
// 构建联锁
RLock redLock = redisson.getMultiLock(lock1, lock2, lock3);
try {
// 尝试加锁,等待时间100秒,锁有效期10秒
boolean locked = redLock.tryLock(100, 10, TimeUnit.SECONDS);
if (locked) {
// 执行业务逻辑
process();
}
} finally {
redLock.unlock();
}
}
}
RedLock关键点:
- 需要至少3个独立的Redis主节点(非集群模式)
- 客户端依次向所有节点申请锁
- 当获得多数节点(N/2+1)的锁时才算成功
- 锁的有效期应远小于业务处理时间
实践建议:
- 设置合理的锁超时时间,避免死锁
- 实现锁续约机制(watch dog)防止业务未完成锁已过期
- 生产环境建议使用Redisson等成熟客户端
2. ZooKeeper临时节点实现
ZooKeeper通过临时顺序节点实现分布式锁,天然具备故障恢复能力。
Java实现示例:
public class ZkDistributedLock {
private final InterProcessMutex lock;
public ZkDistributedLock(ZooKeeper zk, String lockPath) {
this.lock = new InterProcessMutex(zk, lockPath);
}
public void doWithLock() throws Exception {
if (lock.acquire(30, TimeUnit.SECONDS)) {
try {
// 临界区代码
process();
} finally {
lock.release();
}
}
}
}
实践建议:
- 使用Curator框架而非原生ZK API
- 合理设置sessionTimeout防止网络抖动导致锁失效
- 监控ZK集群健康状态
二、分布式事务
1. 异步提交(Seata AT模式)
Seata的AT(Auto Transaction)模式通过全局锁+本地事务实现分布式事务。
执行流程:
- TM(事务管理器)开启全局事务
- RM(资源管理器)注册分支事务
- RM执行本地事务并生成undo log
- TC(事务协调器)决定全局提交或回滚
@GlobalTransactional
public void purchase() {
// 扣减库存(服务A)
inventoryService.reduce();
// 创建订单(服务B)
orderService.create();
}
实践建议:
- 避免长事务,单个事务内操作不宜过多
- 对热点数据做好并发控制
- 监控事务成功率,设置合理超时时间
2. 消息最终一致性(RocketMQ事务消息)
Java实现示例:
public class TransactionProducer {
public static void main(String[] args) throws Exception {
TransactionMQProducer producer = new TransactionMQProducer("group");
producer.setTransactionListener(new TransactionListener() {
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
// 执行本地事务
try {
doBusiness();
return LocalTransactionState.COMMIT_MESSAGE;
} catch (Exception e) {
return LocalTransactionState.ROLLBACK_MESSAGE;
}
}
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
// 检查本地事务状态
return checkBusinessStatus() ?
LocalTransactionState.COMMIT_MESSAGE :
LocalTransactionState.ROLLBACK_MESSAGE;
}
});
Message msg = new Message("topic", "tag", "key", "body".getBytes());
producer.sendMessageInTransaction(msg, null);
}
}
实践建议:
- 实现消息消费的幂等性处理
- 设置合理的消息重试次数和死信队列
- 监控消息积压情况
三、方案选型对比
方案 | 一致性 | 性能 | 复杂度 | 适用场景 |
---|---|---|---|---|
Redis RedLock | 最终 | 高 | 中 | 短期锁,高并发场景 |
ZooKeeper | 强 | 中 | 高 | 长期锁,强一致性要求 |
Seata AT | 最终 | 中 | 中 | 跨服务事务,短事务 |
RocketMQ事务消息 | 最终 | 高 | 低 | 异步解耦,长流程业务 |
总结建议:
- 对一致性要求高的场景优先选择ZooKeeper
- 高并发场景下Redis性能更优但需处理锁续约
- 分布式事务中短事务用Seata,异步场景用消息队列
- 无论哪种方案都要考虑网络分区和脑裂问题