laravel官方支持“原子锁”,并且说“要使用这个功能,应用必须使用 memcached、dynamodb、redis、database 或 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()随机生成字符串实现的。

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

被折叠的 条评论
为什么被折叠?



