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 ...
随机推荐
- CentOS 初体验三: Yum 安装、卸载软件
一:Yum 简介 Yum(全称为 Yellow dog Updater, Modified)是一个在Fedora和RedHat以及CentOS中的Shell前端软件包管理器.基于RPM包管理,能够从指 ...
- 10.Vue.js 样式绑定
Vue.js 样式绑定 Vue.js class class 与 style 是 HTML 元素的属性,用于设置元素的样式,我们可以用 v-bind 来设置样式属性. Vue.js v-bind 在处 ...
- JSP常见的状态码
1.404错误 -- 找不到访问的页面或资源 a.URL输入错误: b.未部署项目. 2.500错误 -- JSP页面代码有错误 3.302状态码+200状态码 -- 重定向 4.200状态码 -- ...
- 用graphviz可视化决策树
1.安装graphviz. graphviz本身是一个绘图工具软件,下载地址在:http://www.graphviz.org/.如果你是linux,可以用apt-get或者yum的方法安装.如果是w ...
- Python初探——sklearn库中数据预处理函数fit_transform()和transform()的区别
敲<Python机器学习及实践>上的code的时候,对于数据预处理中涉及到的fit_transform()函数和transform()函数之间的区别很模糊,查阅了很多资料,这里整理一下: ...
- 【HarmonyOS】【DevEco Studio】NOTE05:PageAbility生命周期的呈现
NOTE05:PageAbility生命周期的呈现 基本界面设置 创建Slice与对应xml BarAbilitySlice package com.example.myapplication.sli ...
- Redis哨兵日常维护
目录 一.日常操作 指定一个从做新主 添加一个从节点 添加一个Setinel节点 一.日常操作 指定一个从做新主 有时候需要将当前主节点机器下线,并指定一个高一些性能的从节点接替 将其它从节点的sla ...
- 前端浅谈-Js的组成
这里主要想详细的分析一下浏览器渲染过程,但东西比较多.所以分成多个部分. JS由三个部分组成,分别为ECMAScript.BOM.DOM. 其中BOM是浏览器层面的东西,而DOM是页面层面的东西.简单 ...
- Explain的详细使用
官方文档 https://dev.mysql.com/doc/refman/5.7/en/explain-output.html explain俩种类型 explain extended 会在 exp ...
- [BUUCTF]PWN10——[第五空间2019 决赛]PWN5
[第五空间2019 决赛]PWN5 题目网址:https://buuoj.cn/challenges#[第五空间2019%20决赛]PWN5 步骤: 例行检查,32位,开启了nx和canary(栈保护 ...