给出部分主代码
感谢HaKeem的提醒,程序发现一个bug,以前的代码会发生无法解锁的问题,放出修正后的代码
如果是redis2.2以上版本可以在lock逻辑中加上watch命令,锁定单个key
/**
* 加锁 【新的加锁算法,采用redis做锁,redis失效情况下,返回 变量redisErrReturn 设定的值】
* @param string $key 唯一标识
* @param int $expire
* @return
*/
public function lock($key, $expire = 5) {
try {
list($key, $lockIdKey) = $this->_getLockKey($key);
//抢锁,第一个线程抢到,并把过期时间写入锁
if (Common::getQueue()->setnx($key, Common::getTime() + $expire)) return true;
//没有抢到锁的线程,判断锁是否异常死锁,如果锁没有过期,返回false
if (Common::getQueue()->get($key) > Common::getTime()) return false;
//锁因异常死锁并过期的情况下,多个并发线程再次抢锁,getset命令到过期时间,如果未过期,表示锁已被其它线程抢得,返回false
if (Common::getQueue()->getset($key, Common::getTime() + $expire) > Common::getTime()) return false;
Common::getQueue()->set($lockIdKey, $this->_lockId);
return true;
} catch (RedisException $e) {
// 当捕捉到redis异常时,锁中断,返回变量redisErrReturn
$this->_lockStatus = false;
return $this->redisErrReturn;
}
}
/**
* 给锁进行续期,延长锁的生效周期
* @param string $key
* @param int $expire 延时锁失效的秒数
* @param int $triggerTime 触发续期倒计时间
* @return
*/
public function updateExpire($key, $expire = 3, $triggerTime = 2) {
if (!$this->_lockStatus) return true; //当redis失效,中断锁
list($key, $lockIdKey) = $this->_getLockKey($key);
$lockId = Common::getQueue()->get($lockIdKey);
if ($this->_lockId === null || $lockId != $this->_lockId) return false;
$time = Common::getQueue()->get($key);
if ($time - $triggerTime <= Common::getTime()) return Common::getQueue()->set($key, $time + $expire);
}
/**
* 解锁
* @param string $key
* @return
*/
public function unlock($key) {
if (!$this->_lockStatus) return true; //当redis失效,中断解锁
list($key, $lockIdKey) = $this->_getLockKey($key);
$lockId = Common::getQueue()->get($lockIdKey);
if ($this->_lockId === null || $lockId != $this->_lockId) return false;
Common::getQueue()->del($key);
Common::getQueue()->del($lockIdKey);
return true;
}
/**
* 清除死锁产生的无用key 默认清除24小时以前的无用key
* @param int $cleanTime 默认24小时
*/
public function cleanLockKey($cleanTime = 86400) {
if ($cleanTime < 3600 || !$keys = Common::getQueue()->keys('PwLock:*')) return false;
$i = 0;
foreach ($keys as $value) {
list($time, $lockId) = explode('|', Common::getQueue()->get($value));
if (Common::getTime() - $time > $cleanTime) {
if (Common::getQueue()->del($value)) $i++;
}
}
return $i;
}
private function _getLockKey($key) {
return array('PwLock:' . $key, 'PwLock:' . $key . ':lockId');
}