2020-11-05 17:40:51 +08:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace App\Support;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Class RedisLock
|
|
|
|
* @package App\Support
|
|
|
|
*/
|
|
|
|
class RedisLock
|
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 锁前缀标识
|
|
|
|
*/
|
|
|
|
const PREFIX = 'lock';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 获取Redis连接
|
|
|
|
*
|
|
|
|
* @return mixed|\Redis
|
|
|
|
*/
|
|
|
|
public static function getRedis()
|
|
|
|
{
|
|
|
|
return redis();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 获得锁,如果锁被占用,阻塞,直到获得锁或者超时。
|
|
|
|
* -- 1、如果 $timeout 参数为 0,则立即返回锁。
|
|
|
|
* -- 2、建议 timeout 设置为 0,避免 redis 因为阻塞导致性能下降。请根据实际需求进行设置。
|
|
|
|
*
|
|
|
|
* @param string $key 缓存KEY
|
|
|
|
* @param string $requestId 客户端请求唯一ID
|
2020-11-07 22:57:10 +08:00
|
|
|
* @param integer $lockSecond 锁定时间 单位(秒)
|
|
|
|
* @param integer $timeout 取锁超时时间。单位(秒)。等于0,如果当前锁被占用,则立即返回失败。如果大于0,则反复尝试获取锁直到达到该超时时间。
|
|
|
|
* @param integer|float $sleep 取锁间隔时间 单位(秒)。当锁为占用状态时。每隔多久尝试去取锁。默认 0.1 秒一次取锁。
|
2020-11-05 17:40:51 +08:00
|
|
|
* @return bool
|
|
|
|
* @throws \Exception
|
|
|
|
*/
|
2020-11-07 22:57:10 +08:00
|
|
|
public static function lock(string $key, string $requestId, $lockSecond = 20, $timeout = 0, $sleep = 0.1)
|
2020-11-05 17:40:51 +08:00
|
|
|
{
|
|
|
|
if (empty($key)) {
|
|
|
|
throw new \Exception('获取锁的KEY值没有设置');
|
|
|
|
}
|
|
|
|
|
|
|
|
$start = self::getMicroTime();
|
|
|
|
$redis = self::getRedis();
|
|
|
|
|
|
|
|
do {
|
|
|
|
$acquired = $redis->set(self::getLockKey($key), $requestId, 'NX', 'EX', $lockSecond);
|
|
|
|
if ($acquired) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($timeout === 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-11-07 22:57:10 +08:00
|
|
|
\Swoole\Coroutine\System::sleep($sleep);
|
2020-11-05 17:40:51 +08:00
|
|
|
} while (!is_numeric($timeout) || (self::getMicroTime()) < ($start + ($timeout * 1000000)));
|
|
|
|
|
|
|
|
return $acquired ? true : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 释放锁
|
|
|
|
*
|
|
|
|
* @param string $key 被加锁的KEY
|
|
|
|
* @param string $requestId 客户端请求唯一ID
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public static function release(string $key, string $requestId)
|
|
|
|
{
|
|
|
|
if (strlen($key) === 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$lua = <<<LAU
|
|
|
|
if redis.call("GET", KEYS[1]) == ARGV[1] then
|
|
|
|
return redis.call("DEL", KEYS[1])
|
|
|
|
else
|
|
|
|
return 0
|
|
|
|
end
|
|
|
|
LAU;
|
|
|
|
|
|
|
|
return self::getRedis()->eval($lua, 1, self::getLockKey($key), $requestId);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 获取锁 Key
|
|
|
|
*
|
|
|
|
* @param string $key 需要加锁的KEY
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public static function getLockKey(string $key)
|
|
|
|
{
|
|
|
|
return self::PREFIX . ':' . $key;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 获取当前微秒
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
protected static function getMicroTime()
|
|
|
|
{
|
|
|
|
return bcmul(microtime(true), 1000000);
|
|
|
|
}
|
|
|
|
}
|