NodeJS 基于redis的分布式锁的实现(Redlock算法)

36 篇文章 0 订阅

1. 前言

开发时,碰到互斥问题,需要保证在分布式环境下,避免重复性操作修改用户状态,如:用户订单状态,购票时,修改票的余额等

2. 分布式锁的条件

分布式锁需要满足下列条件
锁需要有充足的可访问的存储空间
锁必须被唯一标识
锁至少要有两种状态
同时,要保证
安全特性:互斥访问,永远只有一个client能拿到锁
避免死锁:client最后可以拿到锁,不会出现死锁,即使原本上锁的client出现问题无法解锁
容错性:容错,只要大多数redis节点能够正常工作,客户端端都能获取和释放锁。

3.Redis单节点上锁的实现

用下列语句获取一个不存在的key,如果可以已存在则创建失败,确保key值唯一, 加上过期时间,确保系统错误后及时解锁,避免死锁

 SET key value NX PX 30000

但是这种方法在主库错误时,会发生错误,redis主从同步是异步,主库错误时,从库若还没有锁的信息,则会导致多个进程持有锁

4. Redis集群锁的实现-Redlock算法

在分布式版本的算法里我们假设我们有N个Redis master节点,这些节点都是完全独立的,我们不用任何复制或者其他隐含的分布式协调算法。我们已经描述了如何在单节点环境下安全地获取和释放锁。因此我们理所当然地应当用这个方法在每个单节点里来获取和释放锁。在我们的例子里面我们把N设成5,这个数字是一个相对比较合理的数值,因此我们需要在不同的计算机或者虚拟机上运行5个master节点来保证他们大多数情况下都不会同时宕机。一个客户端需要做如下操作来获取锁:

获取当前时间(单位是毫秒)。
轮流用相同的key和随机值在N个节点上请求锁,在这一步里,客户端在每个master上请求锁时,会有一个和总的锁释放时间相比小的多的超时时间。比如如果锁自动释放时间是10秒钟,那每个节点锁请求的超时时间可能是5-50毫秒的范围,这个可以防止一个客户端在某个宕掉的master节点上阻塞过长时间,如果一个master节点不可用了,我们应该尽快尝试下一个master节点。
客户端计算第二步中获取锁所花的时间,只有当客户端在大多数master节点上成功获取了锁(在这里是3个),而且总共消耗的时间不超过锁释放时间,这个锁就认为是获取成功了。
如果锁获取成功了,那现在锁自动释放时间就是最初的锁释放时间减去之前获取锁所消耗的时间。
如果锁获取失败了,不管是因为获取成功的锁不超过一半(N/2+1)还是因为总消耗时间超过了锁释放时间,客户端都会到每个master节点上释放锁,即便是那些他认为没有获取成功的锁。

4.使用示例

首先安装redis库与redlock库(官方库,在git上名叫node-redlock),在node项目中,直接使用npm或者yarn下载安装即可,redis库我使用的是ioredis(直接安装redis库也可)

  npm i --save ioredis
  npm i --save redlock

主要用到的方法(官方文档)

/** 请求锁 */
Redlock.prototype.lock(resource, ttl, ?callback) 
// resource锁的名称
// ttl锁的有效期
// callback 回调函数,当使用promise的写法的时候,可以填写这个参数
/** 释放锁 */
Lock.prototype.unlock(lock, ?callback)
/** 延长锁的有效时间 */
Redlock.prototype.extend(lock, ttl, ?callback)
// 都支持callback、promise、yield写法

代码示例

const ioredis = require('ioredis')
const Redlock = require('redlock')
const client = new ioredis(REDIS_SERVER)
const redlock = new Redlock([client])
co(function* () {
   while (true) {
       let lock = null
       try {
           lock = yield redlock.lock('lock', 1000) // 这种写法取不到锁时会直接抛出错误
       } catch (error) {
           lock = null
       }
       yield sleep(30 * 1000)
       // 处理逻辑
       lock.unlock()
   }
})

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值