【laravel+redis】分布式锁的实现

这篇博客探讨了laravel如何借助redis实现分布式锁。文章指出,laravel的原子锁依赖于redis作为缓存驱动,并且在集群环境中不保证故障转移时的数据安全性。作者通过分析laravel的源码,展示了从Cache门面到RedisStore的lock方法,再到RedisLock的实现过程,重点提及了redis的set指令结合ex和nx参数实现原子性加锁。同时,锁的唯一标识由Str::random()生成,防止误删其他锁。

laravel官方支持“原子锁”,并且说“要使用这个功能,应用必须使用 memcacheddynamodbredisdatabase 或 array 缓存驱动作为应用默认的缓存驱动,此外,所有服务器必须和同一台中央缓存服务器进行通信”。前半句不多解释,后半句也强调了laravel的原子锁不负责在集群架构中保障故障转移期间的数据安全性。

我贴一下laravel的源码看一下它是怎样用redis实现的分布式锁,我本机是laravel8,redis6,laravel6、7也都可以用,代码没什么变化。

1.laravel中实现一个锁的demo

use Illuminate\Contracts\Cache\LockTimeoutException;//抛出异常类
    
$lock = Cache::lock('foo', 10);//持有锁10s
    
try {
    $lock->block(5);
    
    // 最长等待 5s 后获取锁...
} catch (LockTimeoutException $e) {
    // 无法获取锁...
} finally {
    optional($lock)->release();
}
    
Cache::lock('foo', 10)->block(5, function () {
    // 最长等待 5s 后获取锁...
});

发现它用了Cache门面的lock方法,去\vendor\laravel\framework\src\Illuminate\Support\Facades\Cache.php看一下,

/**
 * @method static \Illuminate\Cache\TaggedCache tags(array|mixed $names)
 * @method static \Illuminate\Contracts\Cache\Lock lock(string $name, int $seconds = 0, mixed $owner = null)
...

lock是由\Illuminate\Contracts\Cache\Lock定义的,这是个工厂模式,接口定义

public function block($seconds, $callback = null);

由于cache是由redis实现,所以这个锁也是redis实现的

找到redis缓存执行器\vendor\laravel\framework\src\Illuminate\Cache\RedisStore.php,找到lock方法,发现它实例化了redislock对象

/**
     * Get a lock instance.
     *
     * @param  string  $name
     * @param  int  $seconds
     * @param  string|null  $owner
     * @return \Illuminate\Contracts\Cache\Lock
     */
    public function lock($name, $seconds = 0, $owner = null)
    {
        return new RedisLock($this->connection(), $this->prefix.$name, $seconds, $owner);
    }

发现redislock的__construct继承了父类的__construct

acquire()加锁方法用了redis set非常有特色的扩展字段 ex nx,这是redis原生的,nx扩展指令的意思是当set的字段为空时set,ex是为其设置过期时间,这是一个原子性的操作,成功返回1,失败返回0,不用写lua, 详情可以查官网。

class RedisLock extends Lock
{
    /**
     * The Redis factory implementation.
     *
     * @var \Illuminate\Redis\Connections\Connection
     */
    protected $redis;

    /**
     * Create a new lock instance.
     *
     * @param  \Illuminate\Redis\Connections\Connection  $redis
     * @param  string  $name
     * @param  int  $seconds
     * @param  string|null  $owner
     * @return void
     */
    public function __construct($redis, $name, $seconds, $owner = null)
    {
        parent::__construct($name, $seconds, $owner);

        $this->redis = $redis;
    }

   /**
     * Attempt to acquire the lock.
     *
     * @return bool
     */
    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;
        }
    }
...

}

找到它的父类\vendor\laravel\framework\src\Illuminate\Cache\Lock.php,

 /**
     * Create a new lock instance.
     *
     * @param  string  $name
     * @param  int  $seconds
     * @param  string|null  $owner
     * @return void
     */
    public function __construct($name, $seconds, $owner = null)
    {
        if (is_null($owner)) {
            $owner = Str::random();
        }

        $this->name = $name;
        $this->owner = $owner;
        $this->seconds = $seconds;
    }

之前讲到分布式锁的时候说过,需要给锁设置一个唯一id,防止删了别人的锁,在这里是通过Str::random()随机生成字符串实现的。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值