文档章节

Laravel Redis分布式锁实现源码分析

o
 osc_2jegjdnw
发布于 07/04 10:53
字数 733
阅读 52
收藏 0

「深度学习福利」大神带你进阶工程师,立即查看>>>

首先是锁的抽象类,定义了继承的类必须实现加锁、释放锁、返回锁拥有者的方法。

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();

    // 尝试获取锁,并执行一个回调函数
    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;
    }
}

总结:

  1. 可以通过get()方法直接获取锁并传入回调函数在成功时执行。
  2. 可以通过block()方法在指定时间内不断获取锁,知道成功或超时为止,成功时会执行传入的回调函数。
  3. Redis 通过 set() 命令设置一个值为“拥有者”的字符串来作为锁。
  4. set() 通过 NX 参数来实现排他锁(只在键不存在时,才对键进行设置)。
  5. set() 通过 EX 参数来控制锁的生存时间(防止程序意外终止发生死锁)。
  6. Redis 通过 Lua 脚本来达到原子性删除锁。
  7. Lua 脚本中会判断字符串的内容是否与参数中的拥有者一致,一致才执行删除操作。
o
粉丝 0
博文 66
码字总数 0
作品 0
私信 提问
加载中
请先登录后再评论。
Netty那点事(三)Channel与Pipeline

Channel是理解和使用Netty的核心。Channel的涉及内容较多,这里我使用由浅入深的介绍方法。在这篇文章中,我们主要介绍Channel部分中Pipeline实现机制。为了避免枯燥,借用一下《盗梦空间》的...

黄亿华
2013/11/24
2W
22
用vertx实现高吞吐量的站点计数器

工具:vertx,redis,mongodb,log4j 源代码地址:https://github.com/jianglibo/visitrank 先看架构图: 如果你不熟悉vertx,请先google一下。我这里将vertx当作一个容器,上面所有的圆圈要...

jianglibo
2014/04/03
4.4K
3
Flappy Bird(安卓版)逆向分析(一)

更改每过一关的增长分数 反编译的步骤就不介绍了,我们直接来看反编译得到的文件夹 方法1:在smali目录下,我们看到org/andengine/,可以知晓游戏是由andengine引擎开发的。打开/res/raw/at...

enimey
2014/03/04
6.2K
18
SQLServer实现split分割字符串到列

网上已有人实现sqlserver的split函数可将字符串分割成行,但是我们习惯了split返回数组或者列表,因此这里对其做一些改动,最终实现也许不尽如意,但是也能解决一些问题。 先贴上某大牛写的s...

cwalet
2014/05/21
9.7K
0
beego API开发以及自动化文档

beego API开发以及自动化文档 beego1.3版本已经在上个星期发布了,但是还是有很多人不了解如何来进行开发,也是在一步一步的测试中开发,期间QQ群里面很多人都问我如何开发,我的业余时间实在...

astaxie
2014/06/25
2.7W
22

没有更多内容

加载失败,请刷新页面

加载更多

汇总你在 Linux 上的命令使用情况

使用合适的命令,你可以快速了解 Linux 系统上使用的命令以及执行的频率。 汇总 Linux 系统上使用的命令只需一串相对简单的命令以及几条管道将它们绑定在一起。当你的历史记录缓冲区保留了最...

osc_bvincwvq
10分钟前
0
0
Hacker News 简讯 2020-08-15

最后更新时间: 2020-08-15 07:01 Welders set off Beirut blast while securing explosives - (maritime-executive.com) 焊工在固定炸药的同时引爆了贝鲁特爆炸 得分:383 | 评论:322 Factor......

FalconChen
今天
24
0
OSChina 周六乱弹 —— 老椅小猫秋乡梦 梦里石台堆小鱼

Osc乱弹歌单(2020)请戳(这里) 【今日歌曲】 @小小编辑 :《MOM》- 蜡笔小心 《MOM》- 蜡笔小心 手机党少年们想听歌,请使劲儿戳(这里) @狄工 :腾讯又在裁员了,35岁以上清退,抖音看到...

小小编辑
今天
111
3
构建高性能队列,你不得不知道的底层知识!

前言 本文收录于专辑:http://dwz.win/HjK,点击解锁更多数据结构与算法的知识。 你好,我是彤哥。 上一节,我们一起学习了如何将递归改写为非递归,其中,用到的数据结构主要是栈。 栈和队列...

彤哥读源码
今天
17
0
Anaconda下安装keras和tensorflow

Anaconda下安装keras和tensorflow 一、下载并安装Anaconda: Anaconda下载 安装步骤: 如果是多用户操作系统选择All Users,单用户选择Just Me 选择合适的安装路径 然后勾选这个,自动配置环境...

Atlantis-Brook
今天
15
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部