Hyperf-redis锁
直接使用
<?php
declare(strict_types=1);
/**
*
* This is my open source code, please do not use it for commercial applications.
*
* For the full copyright and license information,
* please view the LICENSE file that was distributed with this source code
*
* @author lt<792532971@qq.com>
*/
namespace App\Kernel\Lock;
use Hyperf\Redis\Redis;
use Swoole\Coroutine;
class RedisLock
{
protected const REDIS_LOCK_PREFIX = 'redis:lock:';
/**
* redis key前缀
*/
private const REDIS_LOCK_KEY_PREFIX = 'redis:lock:';
/**
* @var array
*/
private $lockedNames = [];
/**
* @var Redis
*/
private $redis;
/**
* RedisLock constructor.
*/
public function __construct(Redis $redis)
{
$this->redis = $redis;
}
private function __clone()
{
}
/**
* 上锁
*
* @param string $name 锁名字
* @param int $expire 锁有效期
* @param int $retryTimes 重试次数
* @param float|int $sleep 重试休息微秒
*
* @return mixed
*/
public function lock(string $name, int $expire = 5, int $retryTimes = 10, float $sleep = 10000)
{
$lock = false;
$retryTimes = max($retryTimes, 1);
$key = self::REDIS_LOCK_KEY_PREFIX . $name;
while ($retryTimes-- > 0) {
$kVal = microtime(true) + $expire;
$lock = $this->getLock($key, $expire, $kVal); //上锁
if ($lock) {
$this->lockedNames[$key] = $kVal;
break;
}
if (\Hyperf\Utils\Coroutine::inCoroutine()) {
Coroutine::sleep((float) $sleep / 1000);
} else {
usleep($sleep);
}
}
return $lock;
}
/**
* 解锁
*
* @return mixed
*/
public function unlock(string $name)
{
$script = <<<'LUA'
local key = KEYS[1]
local value = ARGV[1]
if (redis.call('exists', key) == 1 and redis.call('get', key) == value)
then
return redis.call('del', key)
end
return 0
LUA;
$key = self::REDIS_LOCK_KEY_PREFIX . $name;
if (isset($this->lockedNames[$key])) {
$val = $this->lockedNames[$key];
return $this->execLuaScript($script, [$key, $val]);
}
return false;
}
/**
* 获取锁并执行.
*
* @param int $sleep
*
* @throws \Exception
* @return bool
*/
public function run(callable $func, string $name, int $expire = 5, int $retryTimes = 10, $sleep = 10000)
{
if ($this->lock($name, $expire, $retryTimes, $sleep)) {
try {
call_user_func($func);
} catch (\Exception $e) {
throw $e;
} finally {
$this->unlock($name);
}
return true;
}
return false;
}
/**
* 获取锁
*
* @param $key
* @param $expire
* @param $value
*
* @return mixed
*/
private function getLock($key, $expire, $value)
{
$script = <<<'LUA'
local key = KEYS[1]
local value = ARGV[1]
local ttl = ARGV[2]
if (redis.call('setnx', key, value) == 1) then
return redis.call('expire', key, ttl)
elseif (redis.call('ttl', key) == -1) then
return redis.call('expire', key, ttl)
end
return 0
LUA;
return $this->execLuaScript($script, [$key, $value, $expire]);
}
/**
* 执行lua脚本.
*
* @param string $script
* @param int $keyNum
*
* @return mixed
*/
private function execLuaScript($script, array $params, $keyNum = 1)
{
$hash = $this->redis->script('load', $script);
return $this->redis->evalSha($hash, $params, $keyNum);
}
}
已有 0 条评论