<?php
namespace Service;
class Redlock extends Base
{
private $retryDelay;
private $retryCount;
private $clockDriftFactor = 0.01;
private $quorum;
private $servers = array();
private $instances = array();
public function init(array $servers, $retryDelay = 200, $retryCount = 3)
{
$this->servers = $servers;
$this->retryDelay = $retryDelay;
$this->retryCount = $retryCount;
$this->quorum = min(count($servers), (count($servers) / 2 + 1));
return $this;
}
private function getTime()
{
return microtime(true) * 1000;
}
public function lock($resource, $ttl)
{
$this->initInstances();
$token = uniqid();
$retry = $this->retryCount;
do {
$n = 0;
$startTime = $this->getTime();
foreach($this->instances as $instance){
if($this->lockInstance($instance, $resource, $token, $ttl)){
$n++;
}
}
$drift = ($ttl * $this->clockDriftFactor) + 2;
$validityTime = $ttl - ($this->getTime() - $startTime) - $drift;
if($n >= $this->quorum && $validityTime > 0){
return [
'validity' => $validityTime,
'resource' => $resource,
'token' => $token
];
}
else{
foreach($this->instances as $instance){
$this->unlockInstance($instance, $resource, $token);
}
}
$delay = mt_rand(floor($this->retryDelay / 2), $this->retryDelay);
usleep($delay * 1000);
$retry--;
}
while($retry > 0);
return false;
}
public function unlock(array $lock)
{
$this->initInstances();
$resource = $lock['resource'];
$token = $lock['token'];
foreach($this->instances as $instance){
$this->unlockInstance($instance, $resource, $token);
}
}
private function initInstances()
{
if(empty($this->instances)){
foreach($this->servers as $server){
list($host, $port, $timeout) = $server;
$redis = new \Redis();
$redis->connect($host, $port, $timeout);
$this->instances[] = $redis;
}
}
}
private function lockInstance($instance, $resource, $token, $ttl)
{
return $instance->set($resource, $token, ['NX', 'PX' => $ttl]);
}
private function unlockInstance($instance, $resource, $token)
{
$script = '
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
else
return 0
end
';
return $instance->eval($script, [$resource, $token], 1);
}
}
使用方法
/*
/usr/bin/php -d display_error /app/cli.php local Demo.redlock
*/
public function redlock()
{
$servers = [
['192.168.21.16', 6379, 0.01]
];
$redlock = $this->get(SERVICE_REDLOCK)->init($servers);
$lock = $redlock->lock('resource_name', 1000);
print_r($lock);
$redlock->unlock($lock);
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。