您的位置:首页 > 数据库 > Redis

基于Redis的资源锁

2016-12-14 20:56 393 查看
我们在交易系统中通常会用到锁定产品数量等功能。这里分享的即是其中的一种解决方案,使用Redis的script实现的分布式资源锁。代码如下:

//DLock.php
<?php

namespace libraries;
use enums\CachePrefixEnums;

/**
*
* @author fzq
* @comment 以Redis为基础实现的分布式资源锁 需要script的支持
* @date 2016-11-25
*/
class DLock
{
/**
* 强制初始化(强制覆盖原有值,自行处理善后)
*/
const INIT_FORCE = 1;

/**
* 非强制初始化 (有则返回剩余资源量,无则直接初始化)
*/
const INIT_FORCE_NONE = 2;

/**
* 默认资源数量
*/
const DEFAULT_INIT_RES_SIZE = 20;

/**
* 默认的锁定时间
*/
const DEFAULT_LOCK_SECONDS = 100;

const DEFAULT_APPENDIX = '_lockers';

private static $_instance = null;

private $_redis = null;
private $_di = null;

/**
* 强制初始化
*/
private $INIT_FORCE = 1;

/**
* 不强制初始化
*/
private $INIT_FORCE_NONE = 2;

/**
* 返回成功
*/
private $ERR_SUCCESS = 1;

/**
* 返回成功
*/
const ERR_SUCCESS = 1;

/**
* 参数错误
*/
private $ERR_PARAMETERS = -1;

/**
* 参数错误
*/
const ERR_PARAMETERS = -1;

/**
* 未初始化
*/
private $ERR_NONE_INIT = -2;
/**
* 未初始化
*/
const ERR_NONE_INIT = -2;

/**
* 初始化参数错误
*/
private $ERR_INIT_PARAMETERS = -3;
/**
* 初始化参数错误
*/
const ERR_INIT_PARAMETERS = -3;

/**
* 所需资源大于总资源量
*/
private $ERR_EXCEED = -4;

/**
* 所需资源大于总资源量
*/
const ERR_EXCEED = -4;

/**
* 资源不够加锁失败
*/
private $ERR_INSUFFICIENT = -5;
/**
* 资源不够加锁失败
*/
const ERR_INSUFFICIENT = -5;

/**
* 无申请者
*/
private $ERR_NO_APPLICANTS = -6;
/**
* 无申请者
*/
const ERR_NO_APPLICANTS = -6;

/**
* 消费失败 可能是已失去资源锁或是其他错误
*/
private $ERR_CONSUME = -7;
/**
* 消费失败 可能是已失去资源锁或是其他错误
*/
const ERR_CONSUME = -7;

/**
* 资源已耗尽或是未初始化
*/
private $ERR_NO_RESOURCE_LEFT = -8;
/**
* 资源已耗尽或是未初始化
*/
const ERR_NO_RESOURCE_LEFT = -8;

/**
* 剩余资源量
*/
private $LEFT_APPENDIX = '_left';

public function __construct( $redis )
{
$this->_redis = $redis;
}

public static function getInstance( $redis )
{
if( !self::$_instance )
{
self::$_instance = new DLock( $redis );
}

return self::$_instance;
}

/**
* status: done
* 初始化锁
* @param string $strLockName 资源锁名
* @param int $iResID 资源ID
* @param int $iBase 初始加锁时的资源
*
* return false:错误 int:未消费的资源数
*/
public function initLock( string $strLockName, int $iResID, int $iBase = self::DEFAULT_INIT_RES_SIZE, $iForce = self::INIT_FORCE_NONE )
{
if( $iResID <= 0 ||
$iBase <= 0 ||
!$strLockName ||
( $strLockName = trim( $strLockName ) ) == '' )
{
return self::ERR_PARAMETERS;
}

$strLock = CachePrefixEnums::LOCK_RES_PREFIX .  $strLockName;
// 		$strListApplicantKeys = CachePrefixEnums::LOCK_RES_LIST_KEYS_PREFIX . $strLockName . '_' . $iResID;//用于获取某个资源所有的持锁者ID集

$strScript = <<< EOS

local iCnt = redis.call( 'hGet', '$strLock', '$iResID$this->LEFT_APPENDIX' );

if( $iForce == $this->INIT_FORCE_NONE ) then
if iCnt then
return iCnt;
end
end

redis.call( 'hSet', '$strLock', $iResID, $iBase );
redis.call( 'hSet', '$strLock', '$iResID$this->LEFT_APPENDIX', $iBase );
return true;

EOS;
return $this->_redis->eval( $strScript );
}

/**
* @comment 取剩余的资源数量
* 剩余的资源数量 = 总资源量 - 消费的资源量
* @param string $strLockName
* @param int $iResID
*
* return false for error int for resource left
*/
public function getResLeftCnt( string $strLockName, int $iResID )
{
if( $iResID <= 0 ||
!$strLockName ||
( $strLockName = trim( $strLockName ) ) == '' )
{
return self::ERR_PARAMETERS;
}

$strLock = CachePrefixEnums::LOCK_RES_PREFIX .  $strLockName;

$strScript = <<<EOS
local strLeft = redis.call( 'hGet', '$strLock', '$iResID$this->LEFT_APPENDIX' );
if strLeft then
return strLeft;
end
return false;
EOS;

return $this->_redis->eval( $strScript );
}

/**
* 取初始资源总量
* @param $strLockName
* @param $iResID
*
* return false for error others for success
*/
public function getResInitCnt( string $strLockName, int $iResID )
{
if( $iResID <= 0 ||
!$strLockName ||
( $strLockName = trim( $strLockName ) ) == '' )
{
return self::ERR_PARAMETERS;
}

$strLock = CachePrefixEnums::LOCK_RES_PREFIX .  $strLockName;

$strScript = <<<EOS
local strCnt = redis.call( 'hGet', '$strLock', $iResID );
if strCnt then
return strCnt;
end
return $this->ERR_NONE_INIT;
EOS;

return $this->_redis->eval( $strScript );
}

/**
* 取资源使用情况
* 返回资源的初始值, 未消费资源量, 资源名, 资源ID
* 此接口主要用于前端显示规格之类的销售情况
*
* return array for success false for error
*/
public function getResStatus( string $strLockName, int $iResID )
{
if( $iResID <= 0 ||
!$strLockName ||
( $strLockName = trim( $strLockName ) ) == '' )
{
return self::ERR_PARAMETERS;
}

$strLock = CachePrefixEnums::LOCK_RES_PREFIX .  $strLockName;

$strScript = <<<EOS
return redis.call( 'hMGet', '$strLock', $iResID, '$iResID$this->LEFT_APPENDIX' );
EOS;

$arrRet = $this->_redis->eval( $strScript );
if( is_array( $arrRet ) && $arrRet[ 0 ] )
{
return array( 'name' => $strLockName, 'id' => $iResID, 'max' => $arrRet[0], 'left' => $arrRet[1] );
}

return self::ERR_NONE_INIT;
}

/**
* @param string $strLockName 资源名
* @param int $iResID 资源ID
* @param int $iApplicantID 资源的申请者ID
* @param int $iRequestNum 需要的资源数量
* @param int $holdSeconds
* @return boolean
*/
public function lockRes( string $strLockName, int $iResID, int $iApplicantID, int $iRequestNum, int $iHoldSeconds = self::DEFAULT_LOCK_SECONDS )
{
/*
* 算法:此资源锁使用共使用了两类数据即hash与string共三组
* h_s_lock_{$strLockName}:锁 所有的资源锁的最大资源数存放于存hash中采用 如spec numbers规格数 其中的每一组 $iResID:资源数 即是该类的一组资源
* h_s_lock_res_{$strLockName}_{$iResID}:这里存放的是持锁请求者的ID,其中的每一项即为 $iApplicantID:$
*/

if( $iResID <= 0 ||
$iApplicantID <= 0 ||
$iRequestNum <= 0 ||
$iHoldSeconds <= 0 ||
!$strLockName ||
( $strLockName = trim( $strLockName ) ) == '' )
{
return $this->ERR_PARAMETERS;//ERR_PARAMETERS = -1;
}

$strLockHash = CachePrefixEnums::LOCK_RES_PREFIX . $strLockName;//资源锁集
$strListApplicantKeys = CachePrefixEnums::LOCK_RES_LIST_KEYS_PREFIX . $strLockName . '_' . $iResID;//用于获取某个资源所有的持锁者ID集
$strApplicantKeyPrefix = CachePrefixEnums::LOCK_RES_ITEM_PREFIX . $strLockName . '_' . $iResID . '_';//资源持有者ID用于获取string

$strScript = <<< EOS

local strSum = redis.call( 'hGet', '$strLockHash', '$iResID$this->LEFT_APPENDIX' );
local iSum = 0;

if( strSum  ) then
iSum = tonumber( strSum );
else
return $this->ERR_NONE_INIT;
end

local arrKeys = redis.call( 'lrange', '$strListApplicantKeys', 0, -1 );

if( arrKeys and #arrKeys == 0 ) then

if( iSum <= 0 ) then
return $this->ERR_INSUFFICIENT;
elseif( $iRequestNum > iSum ) then
return $this->ERR_EXCEED;
end

redis.call( 'rpush', '$strListApplicantKeys', $iApplicantID );
local strLockItem = '$strApplicantKeyPrefix' .. $iApplicantID;
redis.call( 'set', strLockItem, $iRequestNum, 'EX', $iHoldSeconds );

return $this->ERR_SUCCESS;
else
local arrStrKeys = {};
local iKeySize = table.getn( arrKeys );
local bFoundApplicant = false;
local iLockRes = 0;

for i = 1, iKeySize do
if( tonumber( arrKeys[ i ] ) == $iApplicantID ) then
local strLockRes = redis.call( 'get', '$strApplicantKeyPrefix' .. $iApplicantID );
if( strLockRes and ( tonumber( strLockRes ) <= $iRequestNum )) then
local strLockItem = '$strApplicantKeyPrefix' .. $iApplicantID;
redis.call( 'set', strLockItem, $iRequestNum, 'EX', $iHoldSeconds );
return $this->ERR_SUCCESS;
end

if strLockRes then
iLockRes = tonumber( strLockRes );
end
bFoundApplicant = true;
end

table.insert( arrStrKeys, '$strApplicantKeyPrefix' .. arrKeys[ i ] );
end

local arrRes = redis.call( 'mget', unpack( arrStrKeys ) );
local iLocked = 0;
local iArrRes = table.getn( arrRes );
for i = 1, iArrRes do
if( arrRes[ i ] ) then
iLocked = iLocked + arrRes[ i ];
end
end

if( iLocked - iLockRes + $iRequestNum <= iSum ) then
if( not bFoundApplicant ) then
redis.call( 'rpush', '$strListApplicantKeys', $iApplicantID );
end
redis.call( 'set', '$strApplicantKeyPrefix' .. $iApplicantID, $iRequestNum, 'EX', $iHoldSeconds );
return $this->ERR_SUCCESS;
end

return $this->ERR_INSUFFICIENT;
end

EOS;

return $this->_redis->eval( $strScript );
}

/**
* status: to be done
* @param string $strLockName
* @param int $iID
*
* return true for success others for error
*/
public function unlockRes( string $strLockName, int $iResID, int $iApplicantID )
{//删除list中的key清除string中相关的key

if( $iResID <= 0 ||
$iApplicantID <= 0 ||
!$strLockName ||
( $strLockName = trim( $strLockName ) ) == '' )
{
return $this->ERR_PARAMETERS;//ERR_PARAMETERS = -1;
}

$strListApplicantKeys = CachePrefixEnums::LOCK_RES_LIST_KEYS_PREFIX . $strLockName . '_' . $iResID;
$strApplicantKey = CachePrefixEnums::LOCK_RES_ITEM_PREFIX . $strLockName . '_' . $iResID . '_' . $iApplicantID;

$strScript = <<<EOS
redis.call( 'lRem', '$strListApplicantKeys', 0, $iApplicantID );
redis.call( 'del', '$strApplicantKey' );
return true;
EOS;
return $this->_redis->eval( $strScript );
}

/**
* 删除资源锁
* @param string $strLockName
* @param int $iResID
* @return number
*/
public function delLock( string $strLockName, int $iResID )
{
if( $iResID <= 0 ||
!$strLockName ||
( $strLockName = trim( $strLockName ) ) == '' )
{
return $this->ERR_PARAMETERS;//ERR_PARAMETERS = -1;
}

$strLock = CachePrefixEnums::LOCK_RES_PREFIX .  $strLockName;
$strListApplicantKeys = CachePrefixEnums::LOCK_RES_LIST_KEYS_PREFIX . $strLockName . '_' . $iResID;
$strApplicantKeyPrefix = CachePrefixEnums::LOCK_RES_ITEM_PREFIX . $strLockName . '_' . $iResID . '_';
$strConsumeLog = CachePrefixEnums::LOCK_RES_LOCK_CONSUME_PREFIX . $iResID;//消费记录

$strScript = <<<EOS
local arrApps = redis.call( 'lrange', '$strListApplicantKeys', 0, -1 );
local strKey = '';
if( arrApps and #arrApps > 0 ) then
local iSize = #arrApps;
for i = 1, iSize do
strKey = '$strApplicantKeyPrefix' .. arrApps[ i ];
redis.call( 'del', strKey );
end
redis.call( 'del', '$strListApplicantKeys' );
end
redis.call( 'hDel', '$strLock', $iResID );
redis.call( 'hDel', '$strLock', '$iResID$this->LEFT_APPENDIX' );
redis.call( 'del', '$strConsumeLog' );
return true;
EOS;
return $this->_redis->eval( $strScript );
}

/**
* 消费资源 这里不会出现重复消费的情况
*
* @param string $strLockName
* @param int $iResID
* @param int $iApplicantID
*
* return true for success others for error
*/
public function consume( string $strLockName, int $iResID, int $iApplicantID )
{
if( $iResID <= 0 ||
$iApplicantID <= 0 ||
!$strLockName ||
( $strLockName = trim( $strLockName ) ) == '' )
{
return $this->ERR_PARAMETERS;//ERR_PARAMETERS = -1;
}

$strLock = CachePrefixEnums::LOCK_RES_PREFIX .  $strLockName;
$strListApplicantKeys = CachePrefixEnums::LOCK_RES_LIST_KEYS_PREFIX . $strLockName . '_' . $iResID;
$strApplicantKey = CachePrefixEnums::LOCK_RES_ITEM_PREFIX . $strLockName . '_' . $iResID . '_' . $iApplicantID;
$strConsumeLog = CachePrefixEnums::LOCK_RES_LOCK_CONSUME_PREFIX . $iResID;

$strScript = <<<EOS

local strAppNum = redis.call( 'get', '$strApplicantKey' );
if strAppNum then
local iAppNum = tonumber( strAppNum );
redis.call( 'hIncrBy', '$strLock', '$iResID$this->LEFT_APPENDIX', -1 * iAppNum );
redis.call( 'lRem', '$strListApplicantKeys', 0, $iApplicantID );
redis.call( 'del', '$strApplicantKey' );
redis.call( 'hSet', '$strConsumeLog', $iApplicantID, iAppNum );
return iAppNum;
end

return $this->ERR_CONSUME;
EOS;

return $this->_redis->eval( $strScript );
}

/**
* 取资源申请者及其申请的资源量
*
* @param string $strLockName
* @param int $iResID
* @return int for error object for success
*/
public function getApplicants( string $strLockName, int $iResID )
{
if( $iResID <= 0 ||
!$strLockName ||
( $strLockName = trim( $strLockName ) ) == '' )
{
return $this->ERR_PARAMETERS;//ERR_PARAMETERS = -1;
}

$strLock = CachePrefixEnums::LOCK_RES_PREFIX .  $strLockName;
$strListApplicantKeys = CachePrefixEnums::LOCK_RES_LIST_KEYS_PREFIX . $strLockName . '_' . $iResID;
$strApplicantKeyPrefix = CachePrefixEnums::LOCK_RES_ITEM_PREFIX . $strLockName . '_' . $iResID . '_';

$strScript = <<< EOS

local arrKeys = redis.call( 'lrange', '$strListApplicantKeys', 0, -1 );
if( not arrKeys or #arrKeys == 0 ) then
return $this->ERR_NO_APPLICANTS;
end

local arrStrKeys = {};
local iKeySize = table.getn( arrKeys );

for i = 1, iKeySize do
table.insert( arrStrKeys, '$strApplicantKeyPrefix' .. arrKeys[ i ] );
end
local arrRes = redis.call( 'mget', unpack( arrStrKeys ) );
local arrRets = {};
for i = iKeySize, 1, -1 do
if( arrRes[i] ) then
arrRets[ arrKeys[i] ] = arrRes[i];
else
redis.call( 'lRem', '$strListApplicantKeys', 0, arrKeys[i] );
redis.call( 'del', '$strApplicantKeyPrefix .. arrKeys[i]' );
end
end

if( next( arrRets )  ) then
return cjson.encode( arrRets );
else
return $this->ERR_NO_APPLICANTS;
end
EOS;

$ret = $this->_redis->eval( $strScript );
if( $ret && is_string( $ret ))
{
return json_decode( $ret );
}

return $ret;
}

/**
* 剩余的未加锁的资源与剩余的资源总量
* @param $strLockName
* @param $iResID
*
* array for success int for error e.g.
* object( public 'locked' => 11, public 'left' => '17' )
*/
public function getNoneLockedRes( string $strLockName, int $iResID )
{
if( $iResID <= 0 ||
!$strLockName ||
( $strLockName = trim( $strLockName ) ) == '' )
{
return $this->ERR_PARAMETERS;//ERR_PARAMETERS = -1;
}

$strLock = CachePrefixEnums::LOCK_RES_PREFIX .  $strLockName;
$strListApplicantKeys = CachePrefixEnums::LOCK_RES_LIST_KEYS_PREFIX . $strLockName . '_' . $iResID;
$strApplicantKeyPrefix = CachePrefixEnums::LOCK_RES_ITEM_PREFIX . $strLockName . '_' . $iResID . '_';

$strScript = <<< EOS
local strLeft = redis.call( 'hGet', '$strLock', '$iResID$this->LEFT_APPENDIX' );
if not strLeft then
return $this->ERR_NONE_INIT;
end

local arrRets = {};

local arrKeys = redis.call( 'lrange', '$strListApplicantKeys', 0, -1 );
if( not arrKeys or #arrKeys == 0 ) then

arrRets['locked'] = 0;
arrRets['left'] = strLeft;

return cjson.encode( arrRets );
end

local arrStrKeys = {};
local iKeySize = table.getn( arrKeys );

for i = 1, iKeySize do
table.insert( arrStrKeys, '$strApplicantKeyPrefix' .. arrKeys[ i ] );
end

local arrRes = redis.call( 'mget', unpack( arrStrKeys ) );
local iSum = 0;
for i = iKeySize, 1, -1 do
if( arrRes[i] ) then
iSum = iSum + tonumber( arrRes[i] );
else
redis.call( 'lRem', '$strListApplicantKeys', 0, arrKeys[i] );
redis.call( 'del', '$strApplicantKeyPrefix .. arrKeys[i]' );
end
end

arrRets[ 'locked' ] = iSum;
arrRets[ 'left' ] = tonumber( strLeft );

return cjson.encode( arrRets );
EOS;

$ret = $this->_redis->eval( $strScript );
if( $ret && is_string( $ret ))
{
return json_decode( $ret );
}

return $ret;
}

/**
* 退款后需要回滚Res
* @param string $strLockName
* @param int $iResID
* @param int $iApplicantID
* @param int $iCnt 0:退回所有已消费的, >0 退回指定数目已消费的
*
* return -1:参数错误 false:错误 true for success
*/
public function incrRes( string $strLockName, int $iResID, int $iApplicantID, int $iCnt = 0 )
{
if( $iResID <= 0 ||
$iCnt < 0 ||
$iApplicantID <= 0 ||
!$strLockName ||
( $strLockName = trim( $strLockName ) ) == '' )
{
return $this->ERR_PARAMETERS;//ERR_PARAMETERS = -1;
}

$strConsumeLog = CachePrefixEnums::LOCK_RES_LOCK_CONSUME_PREFIX . $iResID;
$strLock = CachePrefixEnums::LOCK_RES_PREFIX .  $strLockName;

/*
* 为了性能做了一些妥协: script中本应该使用两个变量转而使用了一个变量iConsumedNum来代替两个变量的功能特此说明
*/
$strScript = <<<EOS
local strConsumedNum = redis.call( 'hGet', '$strConsumeLog', $iApplicantID );

if strConsumedNum then
local iConsumedNum = tonumber( strConsumedNum );
if $iCnt > iConsumedNum then
return false;
end

if 0 ~= $iCnt then
iConsumedNum = $iCnt;
end

redis.call( 'hIncrBy', '$strConsumeLog', $iApplicantID, -1 * iConsumedNum );
redis.call( 'hIncrBy', '$strLock', '$iResID$this->LEFT_APPENDIX', iConsumedNum );

return true;
end

return false;
EOS;

$iRet = $this->_redis->eval( $strScript );

if( $iRet )
{
return true;
}

return false;
}

}

//CachePrefixEnums.php
<?php
namespace enums;

/**
*
* @author fzq
*
*/
class CachePrefixEnums
{

//********************************************************************************
//DLock.php
/**
* 某个资源持有者list前缀
*/
const LOCK_RES_LIST_KEYS_PREFIX = 'l_s_res_lock_keys_';

/**
* 持锁者前缀
*/
const LOCK_RES_ITEM_PREFIX = 'str_s_res_lock_item_';

/**
* 资源锁 资源数量hash前缀 主要用来存放资源的个数及剩余数量等
*/
const LOCK_RES_PREFIX = 'h_s_res_lock_';

/**
* 消费记录前缀
*/
const LOCK_RES_LOCK_CONSUME_PREFIX = 'h_s_res_lock_consume_log_';

//********************************************************************************
//RedLock
/**
* 扩展锁
*/
const LOCK_EX_PREFIX = 'str_s_redlock_exlock_';

/**
* 普通锁
*/
const LOCK_PREFIX = 'str_s_redlock_lock_';

}


贴上使用方法:
DLock::getInstance( $this->di );

$strLockName = 'fortest_bruce' . time();
$iResID = 1;
$iBase = 11;

$this->_ut->setTitle( 'DLock测试' );
//test for initlock
$this->_ut->run( ( $iRet = $dLock->initLock( $strLockName, $iResID, $iBase, DLock::INIT_FORCE_NONE ) ) >= 1, true,
"initLock( $strLockName, $iResID, $iBase, DLock::INIT_FORCE_NONE ) 返回值: $iRet", '测试initLock' );

$this->_ut->run( ( $iRet = $dLock->initLock( $strLockName, $iResID, $iBase, DLock::INIT_FORCE_NONE ) ) >= 1, true,
"initLock( $strLockName, $iResID, $iBase, DLock::INIT_FORCE_NONE ) 返回值: $iRet", '测试initLock' );

$iBase = 22;
$this->_ut->run( ( $iRet = $dLock->initLock( $strLockName, $iResID, $iBase, DLock::INIT_FORCE_NONE ) ) >= 1, true,
"initLock( $strLockName, $iResID, $iBase, DLock::INIT_FORCE_NONE ) 返回值: $iRet", '测试initLock' );

$this->_ut->run( ( $iRet = $dLock->initLock( $strLockName, $iResID, $iBase, DLock::INIT_FORCE ) ) >= 1, true,
"initLock( $strLockName, $iResID, $iBase, DLock::INIT_FORCE ) 返回值: $iRet", '测试initLock' );

$this->_ut->run( ( $iRet = $dLock->initLock( $strLockName, $iResID, $iBase, DLock::INIT_FORCE_NONE ) ) >= 1, true,
"initLock( $strLockName, $iResID, $iBase, DLock::INIT_FORCE_NONE ) 返回值: $iRet", '测试initLock' );

$this->_ut->run( ( $iRet = $dLock->initLock( $strLockName, $iResID, $iBase, DLock::INIT_FORCE ) ) >= 1, true,
"initLock( $strLockName, $iResID, $iBase, DLock::INIT_FORCE_NONE ) 返回值: $iRet", '测试initLock' );

//*********************************************************************************************************
//test for getApplicants
$this->_ut->run( ( DLock::ERR_NO_APPLICANTS == $dLock->getApplicants( $strLockName, $iResID )), true,
"getApplicants( $strLockName, $iResID )", '测试getApplicants' );

//test for getNoneLockedRes
$this->_ut->run( (($objRet = $dLock->getNoneLockedRes($strLockName, $iResID)) && is_object( $objRet ) &&
( $objRet->locked == 0 ) && ( $objRet->left == $iBase )),
true,
"getNoneLockedRes($strLockName, $iResID)" . var_export( $objRet, true ),
'测试getNoneLockedRes' );

//test for getResInitCnt
$this->_ut->run( ( $iRet = $dLock->getResInitCnt($strLockName, $iResID)) == $iBase, true,
"getResInitCnt($strLockName, $iResID) 返回值: $iRet", '测试getResInitCnt');

//test for getResStatus
$this->_ut->run( ( $objRet = $dLock->getResStatus($strLockName, $iResID)) && $objRet['name'] == $strLockName &&
$objRet['max'] == $iBase && $objRet['left'] == $iBase,
true, "getResStatus($strLockName, $iResID) 返回值:" . var_export( $objRet, true ), '测试getResStatus');

//************************************************************************************************************************

$iApplicantID = 2;
$iRequireNum = 3;
//test for lockres
$this->_ut->run( ($iRet = $dLock->lockRes($strLockName, $iResID, $iApplicantID, $iRequireNum )),
1,
"lockRes($strLockName, $iResID, $iApplicantID, $iRequireNum ) 返回值: $iRet",
'测试lockRes' );

$this->_ut->run( ($iRet = $dLock->lockRes($strLockName, $iResID, $iApplicantID, $iRequireNum )),
1,
"lockRes($strLockName, $iResID, $iApplicantID, $iRequireNum ) 返回值:" . $iRet,
'测试lockRes' );

//test for consume
$this->_ut->run( ($iRet =  $dLock->consume( $strLockName, $iResID, $iApplicantID )), $iRequireNum,
"consume($strLockName, $iResID, $iApplicantID ) 返回值: $iRet",
'测试consume'
);

$this->_ut->run( ( $objRet = $dLock->getResStatus($strLockName, $iResID)) && $objRet['name'] == $strLockName &&
$objRet['max'] == $iBase && $objRet['left'] == $iBase - $iRet,
true, "getResStatus($strLockName, $iResID) 返回值:" . var_export( $objRet, true ), '测试getResStatus');

$this->_ut->run( ($iRet =  $dLock->consume( $strLockName, $iResID, $iApplicantID )), DLock::ERR_CONSUME,
"consume($strLockName, $iResID, $iApplicantID ) 返回值: $iRet",
'测试consume'
);

$this->_ut->run( $iRet = $dLock->incrRes( $strLockName, $iResID, $iApplicantID, 1 ),
1,
"incrRes( $strLockName, $iResID, $iApplicantID, 1 ) 返回值: " . $iRet,
'测试incrRes( $strLockName, $iResID, $iApplicantID, 1 )' );

$this->_ut->run( $iRet = $dLock->incrRes( $strLockName, $iResID, $iApplicantID, 3 ),
false,
"incrRes( $strLockName, $iResID, $iApplicantID, 1 ) 返回值: " . $iRet,
'测试incrRes( $strLockName, $iResID, $iApplicantID, 1 )' );

$this->_ut->run( $iRet = $dLock->incrRes( $strLockName, $iResID, $iApplicantID, 1 ),
true,
"incrRes( $strLockName, $iResID, $iApplicantID, 1 ) 返回值: " . $iRet,
'测试incrRes( $strLockName, $iResID, $iApplicantID, 1 )' );

$this->_ut->run( ($iRet = $dLock->lockRes($strLockName, $iResID, $iApplicantID, $iRequireNum )),
1,
"lockRes($strLockName, $iResID, $iApplicantID, $iRequireNum ) 返回值: $iRet",
'测试lockRes' );

$this->_ut->run( ($iRet = $dLock->lockRes($strLockName, $iResID, $iApplicantID, $iRequireNum )),
1,
"lockRes($strLockName, $iResID, $iApplicantID, $iRequireNum ) 返回值: $iRet",
'测试lockRes' );

$this->_ut->run( ($objRet = $dLock->getApplicants($strLockName, $iResID)) && is_object( $objRet ), true,
"consume($strLockName, $iResID, $iApplicantID ) 返回值: $iRet",
'测试consume'
);

//test for getNoneLockedRes
$this->_ut->run( (($objRet = $dLock->getNoneLockedRes($strLockName, $iResID)) && is_object( $objRet ) &&
( $objRet->locked == $iRequireNum ) && ( $objRet->left == 21 )),
true,
"getNoneLockedRes($strLockName, $iResID)" . var_export( $objRet, true ),
'测试getNoneLockedRes' );

//test for getResInitCnt
$this->_ut->run( ( $iRet = $dLock->getResInitCnt($strLockName, $iResID)) == $iBase, true,
"getResInitCnt($strLockName, $iResID) 返回值: $iRet", '测试getResInitCnt');

//test for getResStatus
$this->_ut->run( ( $objRet = $dLock->getResStatus($strLockName, $iResID)) && $objRet['name'] == $strLockName &&
$objRet['max'] == $iBase && $objRet['left'] == 21,
true, "getResStatus($strLockName, $iResID) 返回值:" . var_export( $objRet, true ), '测试getResStatus');

$this->_ut->run( ($iRet =  $dLock->consume( $strLockName, $iResID, $iApplicantID )), $iRequireNum,
"consume($strLockName, $iResID, $iApplicantID ) 返回值: $iRet",
'测试consume'
);

$this->_ut->run( ($iRet =  $dLock->delLock($strLockName, $iResID)), DLock::ERR_SUCCESS,
"delLock($strLockName, $iResID) 返回值: $iRet",
'测试delLock'
);

echo $this->_ut->report();
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息