<?php class RedLock { private $retryDelay; private $retryCount; private $clockDriftFactor = 0.01; private $quorum; private $servers = array(); private $instances = array(); function __construct(array $servers = array(), $retryDelay = 200, $retryCount = 3) { $redis_conf = $servers&&$servers!=array()?$servers:C('MY_REDIS'); $ip = $redis_conf['ip']; $port = $redis_conf['port']; $auth = $redis_conf['auth']; $ip = $ip?$ip:'127.0.0.1'; $port = $port?$port:6379; $auth = $auth?$auth:false; $servers = [ [$ip, $port, 0.01,$auth], ]; $this->servers = $servers; $this->retryDelay = $retryDelay; $this->retryCount = $retryCount; $this->quorum = min(count($servers), (count($servers) / 2 + 1)); } public function lock($resource, $ttl) { $this->initInstances(); $token = uniqid(); $retry = $this->retryCount; do { $n = 0; $startTime = microtime(true) * 1000; foreach ($this->instances as $instance) { if ($this->lockInstance($instance, $resource, $token, $ttl)) { $n++; } } $drift = ($ttl * $this->clockDriftFactor) + 2; $validityTime = $ttl - (microtime(true) * 1000 - $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,$auth) = $server; $redis = new \Redis(); $redis->connect($host, $port, $timeout); if($auth){ $redis->auth($auth); } $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); } }
调用
$redLock = new RedLock(); $lock = $redLock->lock('key', 10000); if ($lock) { 抢购操作 }else{}
《“redis 锁 – 高并发抢购防止商品超卖与重复虚拟商品密钥”》 有 3 条评论
You have brought up a very superb details , regards for the post. Corinne Domingo Sylvester Marie-Jeanne Regen Baptist
This is a very good tip especially to those fresh to the blogosphere. Simple but very accurate information?Thanks for sharing this one. A must read post! Gretta Cammy Holt
Wonderful story, reckoned we could combine several unrelated information, nevertheless definitely really worth taking a appear, whoa did 1 discover about Mid East has got a lot more problerms as well Edi Mattheus Wenonah