php + redis + lua achieve Distributed Lock (reprint)

Here is what I use at work class, redis locked in two ways, in order to ensure atomicity unlocked so only lua + redis way

Defects: While deadlock is resolved, but the business execution time period or there is more than locking lock multi-client issues.
However, this class has met my current business needs

Better solutions can reference the following two articles:
https://redis.io/topics/distlock (algorithmic description of Redlock)
https://mp.weixin.qq.com/s/1bPLk_VZhZ0QYNZS8LkviA

Code:

RedisLock class
{
/ **
* @var identification current lock, for unlocking
* /
Private $ _lockFlag;

private $_redis;

public function __construct($host = '127.0.0.1', $port = '6379', $passwd = '')
{
$this->_redis = new Redis();
$this->_redis->connect($host, $port);
if ($passwd) {
$this->_redis->auth($passwd);
}
}

public function lock($key, $expire = 5)
{
$now= time();
$expireTime = $expire + $now;
if ($this->_redis->setnx($key, $expireTime)) {
$this->_lockFlag = $expireTime;
return true;
}

// get a lock on the expiration time
$ = $ currentLockTime the this -> _ redis-> GET (Key $);
IF (currentLockTime $ <$ now) {
/ * Solution
C0 overtime, also holds the lock, was added C1 / C2 / ... inside method of simultaneous requests into
C1 / C2 getset perform a process (process because atomic getset,
so the value of two unequal returned request must ensure that the C1 / C2 only acquire a lock) * /
$ $ = oldLockTime the this -> _ redis-> GetSet (Key $, $ expireTime);
IF (== $ $ currentLockTime oldLockTime) {
-;> _ lockFlag = $ $ expireTime the this
; return to true
}
}

return false;
}

public function lockByLua($key, $expire = 5)
{
$script = <<<EOF

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
EOF;

$this->_lockFlag = md5(microtime(true));
return $this->_eval($script, [$key, $this->_lockFlag, $expire]);
}

public function unlock($key)
{
$script = <<<EOF

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

EOF;

if ($this->_lockFlag) {
return $this->_eval($script, [$key, $this->_lockFlag]);
}
}

private function _eval($script, array $params, $keyNum = 1)
{
$hash = $this->_redis->script('load', $script);
return $this->_redis->evalSha($hash, $params, $keyNum);
}

}

$ RedisLock RedisLock = new ();

$key = 'lock';
if ($redisLock->lockByLua($key)) {
// to do...
$redisLock->unlock($key);
}

Guess you like

Origin www.cnblogs.com/myJuly/p/12640948.html