首先是锁的抽象类,定义了继承的类必须实现加锁、释放锁、返回锁拥有者的方法。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(); // 尝试获取锁,并执行一个回调函数 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 锁实现类,增长了强制删除锁的方法。dom
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; } }
总结:ui