最近遇到一個功能,前端安卓人員點擊按鈕,請求后臺接口驻仅,后臺需要請求大量第三方接口返還給前端谅畅,所以導(dǎo)致返回不是很快,前端沒有做單次點擊限制噪服,導(dǎo)致用戶點擊多次觸發(fā)多次,這邊瞬間數(shù)據(jù)表錄入多次胜茧,所以需要后端對接收到的請求做出限制粘优,在此做出兩層限制,一層使用mysql呻顽,插入記錄時雹顺,記錄update_time,下次更改時先獲取這個時間,如果小于特定時間廊遍,提示稍后重試嬉愧,其二們使用redis的setnx屬性,下面是實現(xiàn)的類喉前,其中g(shù)etRedisConn整個方法自己實現(xiàn)没酣,
/**
* Class Lock_Service 單據(jù)鎖服務(wù)
*/
class lock
{
? ? /**
? ? * 單據(jù)鎖redis key模板
? ? */
? ? const REDIS_LOCK_KEY_TEMPLATE = 'order_lock_%s';
? ? /**
? ? * 單據(jù)鎖默認(rèn)超時時間(秒)
? ? */
? ? const REDIS_LOCK_DEFAULT_EXPIRE_TIME = 10;
? ? /**
? ? * 加單據(jù)鎖
? ? * @param int $intOrderId 單據(jù)ID
? ? * @param int $intExpireTime 鎖過期時間(秒)
? ? * @return bool|int 加鎖成功返回唯一鎖ID,加鎖失敗返回false
*/
? ? public static function addLock($intOrderId, $intExpireTime = self::REDIS_LOCK_DEFAULT_EXPIRE_TIME)
{
? ? ? ? //參數(shù)校驗
? ? ? ? if (empty($intOrderId) || $intExpireTime <= 0) {
? ? ? ? ? ? return false;
? ? ? ? }
? ? ? ? //獲取Redis連接
? ? ? ? $objRedisConn = self::getRedisConn();
? ? ? ? //生成唯一鎖ID卵迂,解鎖需持有此ID
? ? ? ? $intUniqueLockId =? self::generateUniqueLockId();
? ? ? ? //根據(jù)模板裕便,結(jié)合單據(jù)ID,生成唯一Redis key(一般來說见咒,單據(jù)ID在業(yè)務(wù)中系統(tǒng)中唯一的)
? ? ? ? $strKey = sprintf(self::REDIS_LOCK_KEY_TEMPLATE, $intOrderId);
? ? ? ? //加鎖(通過Redis setnx指令實現(xiàn)偿衰,從Redis 2.6.12開始,通過set指令可選參數(shù)也可以實現(xiàn)setnx改览,同時可原子化地設(shè)置超時時間)
? ? ? ? $bolRes = $objRedisConn->set($strKey, $intUniqueLockId, ['nx', 'ex'=>$intExpireTime]);
? ? ? ? //加鎖成功返回鎖ID下翎,加鎖失敗返回false
? ? ? ? return $bolRes ? $intUniqueLockId : $bolRes;
? ? }
? ? /**
? ? * 解單據(jù)鎖
? ? * @param int $intOrderId 單據(jù)ID
? ? * @param int $intLockId 鎖唯一ID
? ? * @return bool
*/
? ? public static function releaseLock($intOrderId, $intLockId)
{
? ? ? ? //參數(shù)校驗
? ? ? ? if (empty($intOrderId) || empty($intLockId)) {
? ? ? ? ? ? return false;
? ? ? ? }
? ? ? ? //獲取Redis連接
? ? ? ? $objRedisConn = self::getRedisConn();
? ? ? ? //生成Redis key
? ? ? ? $strKey = sprintf(self::REDIS_LOCK_KEY_TEMPLATE, $intOrderId);
? ? ? ? //監(jiān)聽Redis key防止在【比對lock id】與【解鎖事務(wù)執(zhí)行過程中】被修改或刪除,提交事務(wù)后會自動取消監(jiān)控宝当,其他情況需手動解除監(jiān)控
? ? ? ? $objRedisConn->watch($strKey);
? ? ? ? if ($intLockId == $objRedisConn->get($strKey)) {
? ? ? ? ? ? $objRedisConn->multi()->del($strKey)->exec();
return true;
? ? ? ? }
? ? ? ? $objRedisConn->unwatch();
return false;
? ? }
? ? /**
? ? * Redis配置:IP
*/
? ? const REDIS_CONFIG_HOST = '127.0.0.1';
? ? /**
? ? * Redis配置:端口
? ? */
? ? const REDIS_CONFIG_PORT = 6379;
? ? /**
? ? * 獲取Redis連接(簡易版本视事,可用單例實現(xiàn))
? ? * @param string $strIp IP
? ? * @param int $intPort 端口
? ? * @return object Redis連接
? ? */
? ? public static function getRedisConn($strIp = self::REDIS_CONFIG_HOST, $intPort = self::REDIS_CONFIG_PORT)
{
? ? ? ? $objRedis = redis::getRedis();
? ? ? ? return $objRedis;
? ? }
? ? /**
? ? * 用于生成唯一的鎖ID的redis key
*/
? ? const REDIS_LOCK_UNIQUE_ID_KEY = 'lock_unique_id';
? ? /**
? ? * 生成鎖唯一ID(通過Redis incr指令實現(xiàn)簡易版本,可結(jié)合日期今妄、時間戳郑口、取余、字符串填充盾鳞、隨機數(shù)等函數(shù)犬性,生成指定位數(shù)唯一ID)
? ? * @return mixed
*/
? ? public static function generateUniqueLockId()
{
? ? ? ? return self::getRedisConn()->incr(self::REDIS_LOCK_UNIQUE_ID_KEY);
? ? }
}
這個公共類在使用時,將上面類引入腾仅,然后調(diào)用?lock::addLock($unique);即可乒裆,其中$unique可以為系統(tǒng)中的唯一確定用戶身份的變量,如用戶uid拼接上字符串即可推励。