<?php
use Illuminate\Support\Facades\Redis;
class RedisLock
{
const NOT_EXISTS = 'NX';
const MILLISECOND_EXPIRE_TIME = 'PX';
// key 持有时间(millisecond)
const EXPIRE_TIME = 500;
// 等待超时时间
const TIME_OUT = 1000;
private static $token;
// 解锁 LUA 脚本
const LUA_SCRIPT_RELEASE_LOCK = '
if (ARGV[1] == redis.call("GET", KEYS[1])) then
return redis.call("DEL", KEYS[1]);
end;
return 0;
';
/**
* @param $key
* @param int $expire
*
* @return bool
* @throws \Exception
*/
public static function lock($key, $expire = self::EXPIRE_TIME)
{
$start = self::msectime();
self::$token = self::createToken();
// 获取锁失败,再一定时间内循环尝试获取
while (1) {
$result = Redis::set($key, self::$token, self::MILLISECOND_EXPIRE_TIME, $expire, self::NOT_EXISTS);
if ('OK' === (string)$result) {
return true;
}
$diff = self::msectime() - $start;
if ($diff >= self::TIME_OUT) {
return false;
}
usleep(100);
}
}
/**
* @param $key
*
* @return bool
*/
public static function unlock($key)
{
return (bool)Redis::eval(self::LUA_SCRIPT_RELEASE_LOCK, 1, $key, self::$token);
}
/**
* @return string
* @throws \Exception
*/
public static function createToken()
{
if (PHP_VERSION_ID >= 70000 && function_exists('random_bytes')) {
return getmypid() . '_' . bin2hex(random_bytes(16));
}
return getmypid() . '_' . uniqid() . rand(10000, 99999);
}
/**
* 返回当前的毫秒时间戳
*
* @return float
*/
public static function msectime() {
list($msec, $sec) = explode(' ', microtime());
$msectime = (float)sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);
return $msectime;
}
}
使用:
<?php
$key = 'redis_lock:'.$response['order_no'];
$lock = RedisLock::lock($key);
if(! $lock) {
Log::error("order lock: ".$key);
return;
}
try {
sleep(10);
} catch (\Throwable $throwable) {
Log::error(CommonService::getExceptionMsg($throwable));
} finally {
// 处理完任务释放锁
RedisLock::unlock($key);
}
网友评论