65 lines
1.7 KiB
PHP
65 lines
1.7 KiB
PHP
|
<?php
|
||
|
declare (strict_types=1);
|
||
|
|
||
|
namespace app\service\util;
|
||
|
|
||
|
use app\exception\BusinessException;
|
||
|
use Predis\Client;
|
||
|
|
||
|
/**
|
||
|
* @author canny
|
||
|
* @date 2023/10/26 9:59
|
||
|
**/
|
||
|
class DistributedLockService
|
||
|
{
|
||
|
protected Client $redis;
|
||
|
protected string $lockKey;
|
||
|
protected string $lockValue;
|
||
|
protected $lockTimeout;
|
||
|
|
||
|
public function __construct($lockKey, $lockTimeout = 10)
|
||
|
{
|
||
|
$this->redis = RedisService::getRedisInstance();
|
||
|
|
||
|
$this->lockKey = $lockKey;
|
||
|
$this->lockValue = uniqid(); // 生成一个唯一的值作为锁的值
|
||
|
$this->lockTimeout = $lockTimeout;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @throws BusinessException
|
||
|
*/
|
||
|
public function acquireLock($callback)
|
||
|
{
|
||
|
$result = $this->redis->set($this->lockKey, $this->lockValue, 'ex', $this->lockTimeout, 'nx');
|
||
|
if (empty($result) || $result->getPayload() !== 'OK') {
|
||
|
throw new BusinessException("处理中,请稍后再试");
|
||
|
}
|
||
|
try {
|
||
|
return $callback();
|
||
|
}catch (\Throwable $throwable){
|
||
|
throw new BusinessException($throwable->getMessage(), $throwable->getCode());
|
||
|
} finally {
|
||
|
$this->releaseLock();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private function releaseLock()
|
||
|
{
|
||
|
// 释放锁,只有锁的值匹配时才会删除锁,以避免误释放
|
||
|
$script = "
|
||
|
if redis.call('get', KEYS[1]) == ARGV[1] then
|
||
|
return redis.call('del', KEYS[1])
|
||
|
else
|
||
|
return 0
|
||
|
end
|
||
|
";
|
||
|
$this->redis->eval($script, 1, $this->lockKey, $this->lockValue);
|
||
|
}
|
||
|
|
||
|
public function renewLock()
|
||
|
{
|
||
|
// 续期锁的过期时间
|
||
|
$this->redis->expire($this->lockKey, $this->lockTimeout);
|
||
|
}
|
||
|
}
|