当前位置: 首页 > 后端技术 > Node.js

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

时间:2023-04-03 20:15:25 Node.js

1.前言在开发过程中,遇到互斥问题时,需要保证在分布式环境下,避免重复修改用户状态的操作,比如:用户订单状态,买票时,修改票的余额等。2、分布式锁的条件分布式锁需要满足以下条件锁需要有足够的可访问存储空间锁必须有唯一标识锁必须至少有两种状态同时要保证安全性特点:互斥访问,只有一种client总能拿到锁避免死锁:client最终能拿到锁,不会出现死锁,即使原本加锁的client出现问题无法解锁。FaultTolerance:容错,只要大部分redis节点工作正常,客户端就可以获取和释放锁。3、Redis单节点加锁的实现使用如下语句获取一个不存在的key。如果已经存在,则创建将无法确保键值唯一。添加过期时间,保证系统出错后及时解锁,避免死锁。SETkeyvalueNXPX30000但是这个方法在主库出错的时候会报错。Redis主从同步是异步的。当主库出错时,如果从库没有锁信息,会导致多个进程持有锁。3.Redlock算法(官方文档)在分布式版本的算法中,我们假设我们有N个Redis主节点,它们是完全独立的,我们不使用任何复制或其他隐含的分布式协调算法。我们已经描述了如何在单节点环境中安全地获取和释放锁。所以我们当然应该使用这个方法来获取和释放每个单个节点中的锁。在我们的例子中,我们将N设置为5,这是一个比较合理的值,所以我们需要在不同的计算机或虚拟机上运行5个master节点,以保证它们在大多数情况下不会同时宕机。客户端需要执行以下操作来获取锁:获取当前时间(以毫秒为单位)。轮流用相同的密钥和随机值在N个节点上请求锁。在这一步中,当client向各个master请求锁时,都会有一个超时时间,这个时间远小于总的锁释放时间。例如,如果锁自动释放时间为10秒,则每个节点的锁请求超时时间可以在5-50毫秒之间,这样可以避免客户端在宕机的主节点上被长时间阻塞,如果一个主节点不可用,我们应该尽快尝试下一个主节点。客户端在第二步计算获取锁所花费的时间。只有当客户端成功获取到大部分master节点上的锁(这里是3个),并且消耗的总时间没有超过释放锁的时间,才释放锁。它被认为是成功的。如果锁获取成功,锁自动释放时间现在是初始锁释放时间减去之前获取锁所花费的时间。如果获取锁失败,无论是因为没有超过一半的锁被成功获取(N/2+1)还是因为总消费时间超过了锁释放时间,client都会释放每个master节点上的锁,甚至那些other也认为没有成功获取锁。4、使用示例首先安装redis库和redlock库(官方库,git上命名为node-redlock)。在node项目中,可以直接使用npm或者yarn下载安装redis库。我用ioredis(直接装redis库也可以)npmi--saveioredisnpmi--saveredlock主要使用方法(官方文档)/**requestlock*/Redlock.prototype.lock(resource,ttl,?callback)//资源锁的名称//ttl锁的有效期//callback回调函数,当使用promise的写法时,可以填写这个参数/**Releasethelock*/Redlock.prototype.unlock(lock,?callback)/**延长锁有效时间*/Lock.prototype.extend(lock,ttl,?callback)//都支持回调、promise、yield代码示例constioredis=require('ioredis')constRedlock=require('redlock')constclient=newioredis(REDIS_SERVER)constredlock=newRedlock([client])co(function*(){while(true){letlock=nulltr??y{lock=yieldredlock.lock}('lock',1000)//this写方法获取锁失败,直接抛出错误}catch(error){lock=null}yieldsleep(30*1000)//处理逻辑lock.unlock()}})