首先是鎖的抽象類,定義了繼承的類必須實現加鎖、釋放鎖、返回鎖擁有者的方法。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