<?php
/**
* 基于redis的分布式锁
*
* 参考开源代码:
* http://nleach.com/post/31299575840/redis-mutex-in-php
*
* https://gist.github.com/nickyleach/3694555
*/
pc_base::load_sys_class('cache_redis', '', 0);
class dist_key_redis {
//锁的超时时间
const TIMEOUT = 20;
const SLEEP = 100000;
/**
* Stores the expire time of the currently held lock
* 当前锁的过期时间
* @var int
*/
protected static $expire;
public static function getRedis()
{
return new cache_redis();
}
/**
* Gets a lock or waits for it to become available
* 获得锁,如果锁被占用,阻塞,直到获得锁或者超时
*
* 如果$timeout参数为0,则立即返回锁。
*
* @param string $key
* @param int $timeout Time to wait for the key (seconds)
* @return boolean 成功,true;失败,false
*/
public static function lock($key, $timeout = null){
if(!$key)
{
return false;
}
$start = time();
$redis = self::getRedis();
do{
self::$expire = self::timeout();
if($acquired = ($redis->setnx("Lock:{$key}", self::$expire)))
{
break;
}
if($acquired = (self::recover($key)))
{
break;
}
if($timeout === 0)
{
//如果超时时间为0,即为
break;
}
usleep(self::SLEEP);
} while(!is_numeric($timeout) || time() < $start + $timeout);
if(!$acquired)
{
//超时
return false;
}
return true;
}
/**
* Releases the lock
* 释放锁
* @param mixed $key Item to lock
* @throws LockException If the key is invalid
*/
public static function release($key){
if(!$key)
{
return false;
}
$redis = self::getRedis();
// Only release the lock if it hasn't expired
if(self::$expire > time())
{
$redis->del("Lock:{$key}");
}
}
/**
* Generates an expire time based on the current time
* @return int timeout
*/
protected static function timeout(){
return (int) (time() + self::TIMEOUT + 1);
}
/**
* Recover an abandoned lock
* @param mixed $key Item to lock
* @return bool Was the lock acquired?
*/
protected static function recover($key){
$redis = self::getRedis();
if(($lockTimeout = $redis->get("Lock:{$key}")) > time())
{
//锁还没有过期
return false;
}
$timeout = self::timeout();
$currentTimeout = $redis->getset("Lock:{$key}", $timeout);
if($currentTimeout != $lockTimeout)
{
return false;
}
self::$expire = $timeout;
return true;
}
}
?>