Laravel Redis分布式锁实现源码分析
首先是锁的抽象类,定义了继承的类必须实现加锁、释放锁、返回锁拥有者的方法。
namespace Illuminate\Cache;
abstract class Lock implements LockContract
{
use InteractsWithTime;
// 锁的名称
protected $name;
// 锁的时长
protected $seconds;
// 当前操作锁的拥有者
protected $owner;
// 获取锁失败时,重新获取锁需要等待的毫秒数
protected $sleepMilliseconds = 250;
// 构造函数
public function __construct($name, $seconds, $owner = null)
{
if (is_null($owner)) {
$owner = Str::random();
}
$this->name = $name;
$this->owner = $owner;
$this->seconds = $seconds;
}
// 加锁
abstract public function acquire();
// 释放锁
abstract public function release();
// 获取锁中保存的拥有者信息
abstract protected function getCurrentOwner();
// 1. 尝试获取锁,并返回获取结果
// 2. 尝试获取锁,获取成功后执行一个回调函数,执行完成后自动释放锁
public function get($callback = null)
{
$result = $this->acquire();
if ($result && is_callable($callback)) {
try {
return $callback();
} finally {
$this->release();
}
}
return $result;
}
// 尝试在指定的时间内获取锁,超时则失败抛出异常
public function block($seconds, $callback = null)
{
$starting = $this->currentTime();
while (! $this->acquire()) {
usleep($this->sleepMilliseconds * 1000);
if ($this->currentTime() - $seconds >= $starting) {
throw new LockTimeoutException;
}
}
if (is_callable($callback)) {
try {
return $callback();
} finally {
$this->release();
}
}
return true;
}
// 返回当前操作锁的拥有者
public function owner()
{
return $this->owner;
}
// 判断当前操作的拥有者是否为锁中保存的拥有者
protected function isOwnedByCurrentProcess()
{
return $this->getCurrentOwner() === $this->owner;
}
// 设置重试获取锁需要等待的毫秒数
public function betweenBlockedAttemptsSleepFor($milliseconds)
{
$this->sleepMilliseconds = $milliseconds;
return $this;
}
}
Redis 锁实现类,增加了强制删除锁的方法。
class RedisLock extends Lock
{
// Redis对象
protected $redis;
// 构造函数
public function __construct($redis, $name, $seconds, $owner = null)
{
parent::__construct($name, $seconds, $owner);
$this->redis = $redis;
}
// 加锁逻辑代码
public function acquire()
{
if ($this->seconds > 0) {
return $this->redis->set($this->name, $this->owner, 'EX', $this->seconds, 'NX') == true;
} else {
return $this->redis->setnx($this->name, $this->owner) === 1;
}
}
// 使用 Lua 脚本释放锁逻辑代码
public function release()
{
return (bool) $this->redis->eval(LuaScripts::releaseLock(), 1, $this->name, $this->owner);
}
// 无视锁的拥有者强制删除锁
public function forceRelease()
{
$this->redis->del($this->name);
}
// 返回锁中保存的拥有者信息
protected function getCurrentOwner()
{
return $this->redis->get($this->name);
}
}
原子性释放锁的 Lua 脚本。
class LuaScripts
{
/**
* 使用 Lua 脚本原子性地释放锁.
*
* KEYS[1] - 锁的名称
* ARGV[1] - 锁的拥有者,只有是该锁的拥有者才允许释放
*
* @return string
*/
public static function releaseLock()
{
return <<<'LUA'
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
LUA;
}
}
总结:
- 可以通过get()方法直接获取锁并传入回调函数在成功时执行。
- 可以通过block()方法在指定时间内不断获取锁,知道成功或超时为止,成功时会执行传入的回调函数。
- Redis 通过 set() 命令设置一个值为“拥有者”的字符串来作为锁。
- set() 通过 NX 参数来实现排他锁(只在键不存在时,才对键进行设置)。
- set() 通过 EX 参数来控制锁的生存时间(防止程序意外终止发生死锁)。
- 不能使用 set()+expire() 来代替set(),防止网络延迟或其他故障导致死锁。
- Redis 通过 Lua 脚本来达到原子性删除锁。
- Lua 脚本中会判断字符串的内容是否与参数中的拥有者一致,一致才执行删除操作。防止当前锁被其他进程误删除,或者误删除了其他进程的锁。
Laravel Redis分布式锁实现源码分析的更多相关文章
- [转]分布式锁-RedisLockRegistry源码分析
前言 官网的英文介绍大概如下: Starting with version 4.0, the RedisLockRegistry is available. Certain components (f ...
- ZooKeeper 分布式锁 Curator 源码 04:分布式信号量和互斥锁
前言 分布式信号量,之前在 Redisson 中也介绍过,Redisson 的信号量是将计数维护在 Redis 中的,那现在来看一下 Curator 是如何基于 ZooKeeper 实现信号量的. 使 ...
- ReentrantLock 锁释放源码分析
ReentrantLock 锁释放源码分析: 调用的是unlock 的方法: public void unlock() { sync.release(1); } 接下来分析release() 方法: ...
- ZooKeeper 分布式锁 Curator 源码 02:可重入锁重复加锁和锁释放
ZooKeeper 分布式锁 Curator 源码 02:可重入锁重复加锁和锁释放 前言 加锁逻辑已经介绍完毕,那当一个线程重复加锁是如何处理的呢? 锁重入 在上一小节中,可以看到加锁的过程,再回头看 ...
- ZooKeeper 分布式锁 Curator 源码 03:可重入锁并发加锁
前言 在了解了加锁和锁重入之后,最需要了解的还是在分布式场景下或者多线程并发加锁是如何处理的? 并发加锁 先来看结果,在多线程对 /locks/lock_01 加锁时,是在后面又创建了新的临时节点. ...
- ZooKeeper 分布式锁 Curator 源码 01:可重入锁
前言 一般工作中常用的分布式锁,就是基于 Redis 和 ZooKeeper,前面已经介绍完了 Redisson 锁相关的源码,下面一起看看基于 ZooKeeper 的锁.也就是 Curator 这个 ...
- 又长又细,万字长文带你解读Redisson分布式锁的源码
前言 上一篇文章写了Redis分布式锁的原理和缺陷,觉得有些不过瘾,只是简单的介绍了下Redisson这个框架,具体的原理什么的还没说过呢.趁年前项目忙的差不多了,反正闲着也是闲着,不如把Rediss ...
- Redis学习——ae事件处理源码分析
0. 前言 Redis在封装事件的处理采用了Reactor模式,添加了定时事件的处理.Redis处理事件是单进程单线程的,而经典Reator模式对事件是串行处理的.即如果有一个事件阻塞过久的话会导致整 ...
- concurrent(三)互斥锁ReentrantLock & 源码分析
参考文档:Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock:http://www.cnblogs.com/skywang12345/p/3496101.html Reentr ...
随机推荐
- Java Maven项目搭建
创建空项目 New Project --> Empty Project --> ... 配置JDK Project Settings --> Project 选择JDK Module ...
- 【Linux】【Basis】文件
refer to: https://en.wikipedia.org/wiki/POSIX refer to: https://en.wikipedia.org/wiki/Unix_file_type ...
- 如何简单的理解LSTM——其实没有那么复杂(转载)
转载地址:https://www.jianshu.com/p/4b4701beba92 1.循环神经网络 人类针对每个问题的思考,一般不会是完全的从头开始思考.正如当你阅读这篇译文的时候,你会根据已经 ...
- 去除爬虫采集到的\xa0、\u3000等字符
\xa0表示不间断空白符,爬虫中遇到它的概率不可谓不小,而经常和它一同出现的还有\u3000.\u2800.\t等Unicode字符串.单从对\xa0.\t.\u3000等含空白字符的处理来说,有以下 ...
- 粒子群算法-PSO
粒子群优化算法 1. 背景知识 1995年美国社会心理学家Kennedy和电气工程师Eberhart共同提出粒子群优化算法(Particle Swarm Optimization, PSO).PSO算 ...
- apscheduler 设置python脚本定时任务
理论概念:https://zhuanlan.zhihu.com/p/95563033 BlockingScheduler与BackgroundScheduler区别 :https://www.jian ...
- 当PowerDesigner的工具栏不见时候该怎么调出来
当PowerDesigner的工具栏不见时候该怎么调出来,如下所示:选中[Palette]便可
- C++之去重
note 今天刷题,忘了去重复库函数,于是手写了一个. 前提: 必须保证数组是有序的. 源码 template <typename T> void unique_arr(T arr[], ...
- 聊一聊Yarp结合Nacos完成服务发现
背景 Yarp 这个反向代理出来后,相信还是有不少人在关注的. 在 Yarp 中,反向代理的配置默认也是基于配置文件的,也有不少大佬已经把这个配置做成了数据库配置+可视化界面. 仔细想了想,做成数据库 ...
- java源码——计算立体图形的表面积和体积
计算球,圆柱,圆锥的表面积和体积. 利用接口实现. 上代码. Contants.java 常量存储类 package com.fuxuemingzhu.solidgraphics.contants; ...