当前位置: 首页 > news >正文

Renren框架DistributeLock排他锁实现详解

Renren框架DistributeLock排他锁实现详解

Renren框架的DistributeLock是基于Redis实现的分布式锁工具,其核心是通过原子操作保证同一时间只有一个客户端可以获取锁。以下从源码分析、实现原理到使用场景进行详细说明。

一、核心实现原理

DistributeLock主要基于Redis的以下特性:

  1. 原子操作:使用SET key value NX PX timeout命令实现原子性加锁
  2. 过期机制:设置锁的过期时间,防止死锁
  3. Lua脚本:通过Lua脚本保证解锁操作的原子性
核心源码分析
// 获取锁的核心方法
public boolean tryLock(String key, String requestId, long waitTime, long leaseTime) {long start = System.currentTimeMillis();try {// 循环尝试获取锁,直到超过等待时间while (true) {// 使用RedisTemplate执行SET命令Boolean success = redisTemplate.opsForValue().setIfAbsent(key, requestId, leaseTime, TimeUnit.MILLISECONDS);if (success != null && success) {return true; // 获取锁成功}// 计算剩余等待时间long elapsed = System.currentTimeMillis() - start;if (elapsed >= waitTime) {return false; // 等待超时}// 短暂休眠,避免频繁重试Thread.sleep(100);}} catch (InterruptedException e) {Thread.currentThread().interrupt();return false;}
}// 释放锁的核心方法(使用Lua脚本保证原子性)
public void unlock(String key, String requestId) {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +"   return redis.call('del', KEYS[1]) " +"else " +"   return 0 " +"end";redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),Collections.singletonList(key), requestId);
}

二、使用场景示例

1. 商品库存扣减
@Service
public class ProductService {@Autowiredprivate DistributeLock distributeLock;public void deductStock(String productId, int quantity) {String lockKey = "stock:" + productId;String requestId = UUID.randomUUID().toString();try {// 尝试获取锁,最多等待2秒,锁持有10秒后自动释放boolean locked = distributeLock.tryLock(lockKey, requestId, 2000, 10000);if (!locked) {throw new BusinessException("扣减库存失败,请稍后重试");}// 获取锁成功,执行库存扣减Product product = productDao.getById(productId);if (product.getStock() < quantity) {throw new BusinessException("库存不足");}product.setStock(product.getStock() - quantity);productDao.update(product);} finally {// 释放锁distributeLock.unlock(lockKey, requestId);}}
}
2. 订单创建防重
@Service
public class OrderService {@Autowiredprivate DistributeLock distributeLock;public String createOrder(OrderDTO orderDTO) {// 使用订单号或用户ID作为锁的KeyString lockKey = "order:" + orderDTO.getUserId();String requestId = UUID.randomUUID().toString();try {// 尝试获取锁,等待500毫秒boolean locked = distributeLock.tryLock(lockKey, requestId, 500, 5000);if (!locked) {throw new BusinessException("操作太频繁,请稍后再试");}// 检查订单是否已存在Order existingOrder = orderDao.findByUserIdAndStatus(orderDTO.getUserId(), OrderStatus.CREATED);if (existingOrder != null) {return existingOrder.getOrderNo();}// 创建新订单Order newOrder = new Order();// 设置订单属性...orderDao.save(newOrder);return newOrder.getOrderNo();} finally {distributeLock.unlock(lockKey, requestId);}}
}

三、参数配置说明

参数说明
key锁的唯一标识,建议使用业务相关前缀(如stock:product:123
requestId唯一标识请求,用于确保释放锁的安全性(必须与加锁时一致)
waitTime等待锁的最大时间(毫秒),超过则返回失败
leaseTime锁的自动释放时间(毫秒),防止死锁

四、高级特性

1. 可重入锁支持

Renren框架的DistributeLock默认不支持可重入,如需可重入功能,可自定义实现:

public class ReentrantDistributeLock {private final DistributeLock delegate;private final ThreadLocal<Map<String, Integer>> lockCount = ThreadLocal.withInitial(HashMap::new);public boolean tryLock(String key, String requestId, long waitTime, long leaseTime) {Map<String, Integer> countMap = lockCount.get();if (countMap.containsKey(key)) {countMap.put(key, countMap.get(key) + 1);return true; // 已持有锁,直接返回成功}boolean locked = delegate.tryLock(key, requestId, waitTime, leaseTime);if (locked) {countMap.put(key, 1);}return locked;}public void unlock(String key, String requestId) {Map<String, Integer> countMap = lockCount.get();Integer count = countMap.get(key);if (count == null) {return; // 未持有锁,直接返回}if (count > 1) {countMap.put(key, count - 1); // 减少重入次数} else {countMap.remove(key);delegate.unlock(key, requestId); // 真正释放锁}}
}
2. 看门狗机制(自动续期)
public boolean tryLockWithWatchdog(String key, String requestId, long waitTime) {long start = System.currentTimeMillis();try {while (true) {// 设置较短的初始租期Boolean success = redisTemplate.opsForValue().setIfAbsent(key, requestId, 30, TimeUnit.SECONDS);if (success != null && success) {// 启动看门狗线程,定期续期startWatchdog(key, requestId);return true;}if (System.currentTimeMillis() - start > waitTime) {return false;}Thread.sleep(100);}} catch (InterruptedException e) {Thread.currentThread().interrupt();return false;}
}private void startWatchdog(String key, String requestId) {ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();// 每10秒续期一次(租期30秒)executor.scheduleAtFixedRate(() -> {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +"   return redis.call('pexpire', KEYS[1], ARGV[2]) " +"else " +"   return 0 " +"end";redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),Collections.singletonList(key), requestId, 30000L // 续期30秒);}, 10, 10, TimeUnit.SECONDS);
}

五、常见问题与解决方案

1. 锁超时问题
  • 问题:业务执行时间超过leaseTime导致锁提前释放
  • 解决方案
    // 方案1:合理估算业务耗时,设置足够长的leaseTime
    long leaseTime = estimateBusinessTime() * 1.5; // 留出50%的缓冲时间// 方案2:使用看门狗机制自动续期
    distributeLock.tryLockWithWatchdog(lockKey, requestId, 5000);
    
2. 锁释放失败
  • 问题:unlock操作失败导致锁无法释放
  • 解决方案
    // 方案1:确保unlock在finally块中执行
    try {if (distributeLock.tryLock(lockKey, requestId, 1000, 30000)) {// 业务逻辑}
    } finally {distributeLock.unlock(lockKey, requestId);
    }// 方案2:使用带返回值的unlock方法,检查释放结果
    boolean unlocked = distributeLock.unlock(lockKey, requestId);
    if (!unlocked) {log.warn("锁释放失败: {}", lockKey);
    }
    
3. 锁竞争激烈
  • 问题:大量请求竞争同一把锁导致性能下降
  • 解决方案
    // 方案1:分段锁设计
    int segment = userId % 10; // 将用户分为10段
    String lockKey = "user:segment:" + segment;// 方案2:重试机制+指数退避
    int maxRetries = 3;
    for (int i = 0; i < maxRetries; i++) {if (distributeLock.tryLock(lockKey, requestId, 500, 5000)) {try {// 业务逻辑break;} finally {distributeLock.unlock(lockKey, requestId);}}// 指数退避:每次重试等待时间加倍Thread.sleep(100 * (i + 1));
    }
    

六、性能优化建议

  1. 减少锁持有时间

    // 错误示例:锁内包含IO操作
    distributeLock.tryLock(lockKey, ...);
    try {data = remoteService.getData(); // 远程调用processData(data);
    } finally {distributeLock.unlock(lockKey, ...);
    }// 正确示例:仅锁定关键代码
    data = remoteService.getData(); // 远程调用
    distributeLock.tryLock(lockKey, ...);
    try {processData(data); // 只锁定数据处理部分
    } finally {distributeLock.unlock(lockKey, ...);
    }
    
  2. 使用读写锁(如果适用)

    // 自定义读写锁实现
    public class ReadWriteDistributeLock {private final DistributeLock readLock;private final DistributeLock writeLock;public ReadWriteDistributeLock(String baseKey) {this.readLock = new DistributeLock(baseKey + ":read");this.writeLock = new DistributeLock(baseKey + ":write");}// 实现读写锁逻辑...
    }
    

七、总结

Renren框架的DistributeLock提供了简单易用的分布式锁实现,适用于大多数分布式场景。使用时需注意:

  1. 正确设置参数:合理设置waitTime和leaseTime
  2. 确保锁释放:在finally块中调用unlock
  3. 处理异常情况:考虑锁超时、释放失败等场景
  4. 优化锁粒度:避免锁范围过大导致性能问题

通过合理使用DistributeLock,可以有效解决分布式环境下的资源竞争问题,保障业务数据的一致性和正确性。

http://www.dtcms.com/a/273025.html

相关文章:

  • 企业内网系统:从传统开发到智能赋能的进化之路
  • 安达发|医疗器械行业APS自动排单:智能化生产管理的未来之路
  • useRef跨渲染周期存储
  • 数据结构 --- 队列
  • 10.Docker安装mysql
  • chatgpt是怎么诞生的,详解GPT1到GPT4的演化之路及相关背景知识
  • dexie 前端数据库封装
  • 使用快捷键迅速校准多个通道 | IPEmotion
  • 软件技术:柯里化
  • 《PyQt6-3D应用开发技术文档》
  • 仿豆包智能输入框实现
  • python基础25_某大网校(下)处理json数据以及保存题库
  • 安全访问云端内部应用:用frp的stcp功能解决SSH转发的痛点
  • Linux驱动开发(platform 设备驱动)
  • 老题新解|矩阵转置
  • AI驱动的业务系统智能化转型:从非结构化到结构化的智能转换
  • 【STM32 学习笔记】FLASH闪存
  • pytorch学习-12循环神经网络(基础篇)
  • 机器视觉之激光码检测系统
  • 【世纪龙科技】学测-汽车信息化综合实训考核平台(机电方向)
  • 数字孪生系统如何助力汽车零部件企业实现虚拟智控
  • RedisJSON 内存占用剖析与调优
  • Lua嵌入式爬虫实现步骤
  • 【Linux系统】冯诺依曼体系结构 | 初识操作系统
  • 生产者、消费者问题(C语言、POSIX)
  • 测试覆盖标准-条件覆盖-短路求值
  • 全新开源AI知识库系统!PandaWiki一键构建智能文档,支持AI问答、创作与搜索!
  • [特殊字符] 05_Jenkins 部署前端项目实现自动化部署
  • rv1106使用笔记
  • 【RL-VLM-F】算法框架图绘图学习笔记