diff --git a/README-zh.md b/README-zh.md index 879c1d9..d2c52ce 100644 --- a/README-zh.md +++ b/README-zh.md @@ -75,6 +75,48 @@ public function lock(ResponseInterface $response) ``` +- 读锁 +``` +/** + * @return \Psr\Http\Message\ResponseInterface + */ + public function lockA(ResponseInterface $response) + { + try { + $lock = new RedisLock($this->redis, 'lock', 4); + $res = $lock->readLock(4, function () { + return [456]; + }, 250000); + return $response->json(['res' => $res]); + // catch the exception + } catch (LockTimeoutException $exception) { + var_dump('lockA lock check timeout'); + return $response->json(['res' => false, 'message' => 'timeout']); + } + } +``` +- 写锁 +``` +/** + * @return \Psr\Http\Message\ResponseInterface + */ + public function lockA(ResponseInterface $response) + { + try { + $lock = new RedisLock($this->redis, 'lock', 4); + $res = $lock->writeLock(4, function () { + return [456]; + }, 250000); + return $response->json(['res' => $res]); + // catch the exception + } catch (LockTimeoutException $exception) { + var_dump('lockA lock check timeout'); + return $response->json(['res' => false, 'message' => 'timeout']); + } + } +``` + + ## 最后 #### 代码贡献 diff --git a/README.md b/README.md index 56de1a6..323cdb3 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,46 @@ for example: } ``` +- read lock +``` +/** + * @return \Psr\Http\Message\ResponseInterface + */ + public function lockA(ResponseInterface $response) + { + try { + $lock = new RedisLock($this->redis, 'lock', 4); + $res = $lock->readLock(4, function () { + return [456]; + }, 250000); + return $response->json(['res' => $res]); + // catch the exception + } catch (LockTimeoutException $exception) { + var_dump('lockA lock check timeout'); + return $response->json(['res' => false, 'message' => 'timeout']); + } + } +``` +- write lock +``` +/** + * @return \Psr\Http\Message\ResponseInterface + */ + public function lockA(ResponseInterface $response) + { + try { + $lock = new RedisLock($this->redis, 'lock', 4); + $res = $lock->writeLock(4, function () { + return [456]; + }, 250000); + return $response->json(['res' => $res]); + // catch the exception + } catch (LockTimeoutException $exception) { + var_dump('lockA lock check timeout'); + return $response->json(['res' => false, 'message' => 'timeout']); + } + } +``` ## Finally diff --git a/src/Lock.php b/src/Lock.php index 6d872ec..fcf2165 100644 --- a/src/Lock.php +++ b/src/Lock.php @@ -9,6 +9,9 @@ abstract class Lock implements LockContract { use InteractsWithTime; + const LOCK_MODE_SHARE = 1; + const LOCK_MODE_WRITE = 2; + /** * The name of the lock * @var string @@ -42,12 +45,34 @@ public function __construct($name, $seconds, $owner = null) */ abstract public function acquire(); + /** + * Attempt to acquire the share lock + * @return bool + */ + abstract protected function acquireShareLock(); + + /** + * Attempt to acquire the write lock + * @return bool + */ + abstract protected function acquireWriteLock(): bool; + /** * Release the lock * @return void */ abstract public function release(); + /** + * @return void + */ + abstract protected function releaseShareLock(); + + /** + * @return void + */ + abstract protected function releaseWriteLock(); + /** * Returns the owner value written into the driver for this lock * @return string @@ -104,6 +129,52 @@ public function block($seconds, $callback = null) return true; } + /** + * @throws LockTimeoutException + */ + public function readLock($seconds, $callback = null, $interval = 250000) + { + $starting = $this->currentTime(); + while ($this->acquireShareLock() == 0) { + usleep($interval); + if ($this->currentTime() - $seconds >= $starting) { + throw new LockTimeoutException(); + } + } + if (is_callable($callback)) { + try { + return $callback(); + } finally { + $this->releaseShareLock(); + } + } + + return true; + } + + /** + * @throws LockTimeoutException + */ + public function writeLock($seconds, $callback = null, $interval = 250000) + { + $starting = $this->currentTime(); + while ($this->acquireWriteLock() == 0) { + usleep($interval); + if ($this->currentTime() - $seconds >= $starting) { + throw new LockTimeoutException(); + } + } + if (is_callable($callback)) { + try { + return $callback(); + } finally { + $this->releaseWriteLock(); + } + } + + return true; + } + /** * Returns the current owner of the lock. * diff --git a/src/LockContract.php b/src/LockContract.php index 5bfacc9..adf01c0 100644 --- a/src/LockContract.php +++ b/src/LockContract.php @@ -18,6 +18,24 @@ public function get($callback = null); */ public function block($seconds, $callback = null); + /** + * Attempt to acquire share lock for given number of seconds, try every $interval microseconds + * @param $seconds + * @param null $callback + * @param int $interval + * @return mixed + */ + public function readLock($seconds, $callback = null, $interval = 250000); + + /** + * Attempt to acquire exclusive lock for given number of seconds, try every $interval microseconds + * @param $seconds + * @param null $callback + * @param int $interval + * @return mixed + */ + public function writeLock($seconds, $callback = null, $interval = 250000); + /** * Release the lock * @return mixed diff --git a/src/LockScripts.php b/src/LockScripts.php index cebbe67..01aeca3 100644 --- a/src/LockScripts.php +++ b/src/LockScripts.php @@ -17,4 +17,97 @@ public static function releaseLock() end LUA; } + + /** + * lock in share mode + * eval param key_name , share_mode, expire_time + * @return string + */ + public static function shareLock() + { + return <<name; + } + + /** + * @inheritDoc + */ + protected function acquireShareLock(): bool + { + $shareLockScript = LockScripts::shareLock(); + $expireTime = $this->seconds > 0 ? $this->seconds : 30; + + $result = $this->redis->eval($shareLockScript, [$this->getReadWriteLockKey(), self::LOCK_MODE_SHARE, $expireTime], 1); + return intval($result) === 1; + } + + /** + * @inheritDoc + * @return bool + */ + protected function acquireWriteLock(): bool + { + $lua = LockScripts::writeLock(); + $expireTime = $this->seconds > 0 ? $this->seconds : 30; + + $result = $this->redis->eval($lua, [$this->getReadWriteLockKey(), self::LOCK_MODE_WRITE, $this->owner, $expireTime], 1); + return intval($result) === 1; + } + /** * @inheritDoc */ @@ -44,6 +78,24 @@ public function release() } } + /** + * @inheritDoc + */ + protected function releaseShareLock() + { + $lua = LockScripts::releaseShareLock(); + $this->redis->eval($lua, [$this->getReadWriteLockKey(), self::LOCK_MODE_SHARE], 1); + } + + /** + * @inheritDoc + */ + protected function releaseWriteLock() + { + $lua = LockScripts::releaseWriteLock(); + $this->redis->eval($lua, [$this->getReadWriteLockKey(), self::LOCK_MODE_WRITE, $this->owner], 1); + } + /** * @inheritDoc */