laravel redis分布式锁原理

管理员

laravel redis分布式锁原理

Route::get('/test', function () {
    $lock = \Illuminate\Support\Facades\Cache::lock('test', 10, 'owner');
       try {
           // 未获取锁的消费者将阻塞在这里
           $lock->block(10);
           sleep(10);
           dump(123);
   
       } catch (\Illuminate\Contracts\Cache\LockTimeoutException $e) {
           dump('库存不足---------');
   
       } finally {
           optional($lock)->release();
       }
});

1. Illuminate\Support\Facades\Cache::lock(‘test’, 10, ‘owner’)

1.1 实例化RedisLock

Illuminate\Cache\RedisStore
public function lock($name, $seconds = 0, $owner = null)
{
    $lockName = $this->prefix.$name;

    $lockConnection = $this->lockConnection();

    if ($lockConnection instanceof PhpRedisConnection) {
        return new PhpRedisLock($lockConnection, $lockName, $seconds, $owner);
    }
    
    //实例化redisLock
    return new RedisLock($lockConnection, $lockName, $seconds, $owner);
}

1.2 赋值参数

Illuminate\Cache\RedisLock;
public function __construct($redis, $name, $seconds, $owner = null)
{
    parent::__construct($name, $seconds, $owner);

    $this->redis = $redis;
}
Illuminate\Cache\Lock;
public function __construct($name, $seconds, $owner = null)
{
    //锁拥有者,不传参数则生成
    if (is_null($owner)) {
        $owner = Str::random();
    }

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

2 $lock->block(2)

2.1 阻塞原理

Illuminate\Cache\Lock;
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;
}

如果加锁成功返回true,锁存在则返回false

Illuminate\Cache\RedisLock;
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;
    }
}

3 optional($lock)->release();
3.1释放锁

Illuminate\Cache\RedisLock;
public function release()
{
    return (bool) $this->redis->eval(LuaScripts::releaseLock(), 1, $this->name, $this->owner);
}

redis脚本,保持操作原子性

Illuminate\Cache\LuaScripts;
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;
}
0人点赞
Laravel
管理员

全部评论 0