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 ...
随机推荐
- redis 之 集群
#:下载源码包,并编译安装 [root@localhost src]# wget http://download.redis.io/releases/redis-4.0.14.tar.gz [root ...
- java中的++i是线程安全的吗?
java中的++i是线程安全的吗?为什么?怎么使它线程安全呢? 先说答案: 非线程安全 先说下为什么是非线程安全的? 从Java内存模型说起 Java内存模型规定了所有的便利都存储在主内存中,每个线程 ...
- Python multiprocessing 基础使用和小trick
最近进行数据预处理时(噪声插入),单进程严重影响实验周期,故学习了multiprocessing并发执行不同数据集的处理,加快执行效率.现于此进行一些简单记录以供日后参考. 1. 基础: From m ...
- HMAC在“挑战/响应”(Challenge/Response)身份认证的应用
HMAC的一个典型应用是用在"挑战/响应"(Challenge/Response)身份认证中. 认证流程 (1) 先由客户端向服务器发出一个验证请求. (2) 服务器接到此请求后生 ...
- LuoguP4263 [Code+#3]投票统计 题解
Content 有 \(t\) 组询问,每组询问给定一个长度为 \(n\) 的数列,请将出现次数最多的数按照从小到大的顺序输出,或者这些数在数列中出现的次数都相等. 数据范围:\(t\) 未知,\(n ...
- 前端实现list排序
需求 针对list中某个字段,实现list的升序和降序 效果图 代码 我是用在angular1.X中项目的,根据list中的sort字段进行排序. # sort.html <style> ...
- Linux使用docker安装Jenkins 并进行jar包发布
拉取镜像 docker pull jenkins/jenkins:lts 创建docker-compose.yml 文件 (主要习惯 也可以不用这种方式) Linux安装docker-compose ...
- SpringBoot 整合es(elasticsearch)使用elasticsearch-rest-high-level-client实现增删改
引入依赖 <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok< ...
- IDEA版本2020.1全局MAVEN配置
我们用IDEA打开项目有时候要每次配置maven地址,这样就很麻烦 我们可以直接设置全局的 这样所有新打开的项目都能使用
- cmake指定程序输出目录和库文件输出目录和拷贝文件
概述 本文样式环境: win10+cmake 3.18 本文将介绍使用CMAKE配置项目输出目录和 LIbrary项目的输出目录 本文将介绍 cmake的file函数的基础用法之拷贝文件 重点, 这些 ...