基于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();
相关文章推荐
- 分布式锁 分段锁 基于 memcached redis zookeeper (3种资源模式) 实现
- 基于数据库和ASP的网上教学资源管理系统的开发
- 论文--基于抽词及元搜索引擎技术的资源搜集系统
- 基于小型GIS技术的电力资源管理系统
- 一个基于socket的资源共享平台的实现(二)
- 基于资源的HTTP Cache的实现介绍
- 一个基于socket的资源共享平台的实现(三)
- 最简单的基于Tomcat的Web应用程序-没有Servlet,只访问静态资源(jsp,html,gif等)
- 基于角色-功能-资源的权限控制模型的设计与实现-引子
- 一个基于socket的资源共享平台的实现(一)
- 关于基于XML描述的GUI应用的一些资源
- 20多个基于CSS设计的免费站点模版下载资源
- 基于ACE框架的服务器端占CPU资源过多的故障排除
- 精品教程:创建基于 Ajax 的 IM 客户端(Web development | Ajax 资源中心)
- 20多个基于CSS设计的免费站点模版下载资源
- 向你的Java程序加入开放性资源、基于XML的即时消息
- 基于角色-功能-资源的权限控制模型的设计与实现-引子
- 基于角色-功能-资源的权限控制模型的设计与实现-引子
- 基于Web2.0的异构数字资源检索系统研究与开发
- 基于魔兽哈希算法的Ogre资源文件扩展的设计与实现