| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- <?php
- namespace common\helpers;
- class LockHelper
- {
- /**
- * 加锁
- * @param $key
- * @param $identification
- * @param int $expire
- * @param int $wait_time
- * @return bool
- */
- public static function lock($key,$identification,$expire=30,$wait_time = 10){
- $redis = \Yii::$app->redis;
- $hasWait = 0;//已经等待的时间:毫秒
- while(true){
- $res = $redis->set($key,$identification,'NX','EX',$expire);
- if ($res){
- return true;
- break;
- }else{
- if($hasWait > $wait_time*1000){
- //等待超时
- return false;
- }
- //sleep(10);
- usleep(300 * 1000);//等待300ms
- $hasWait += 300;
- continue;
- }
- }
- return false;
- }
- /**
- * 解锁
- * @param $key
- * @param $identification
- */
- public static function uLock($key,$identification){
- $redis = \Yii::$app->redis;
- $script = '
- if redis.call("get",KEYS[1]) == ARGV[1] then
- return redis.call("del",KEYS[1])
- else
- return 0
- end';
- $redis->eval($script,1,$key,$identification);
- }
- /**
- * 批量解锁
- * @DateTime 2021-11-10 17:06:37
- * @copyright: Copyright (c) 2020 广东七件事集团
- * @param [type] $lock_arr[$key=>$identification]
- * @return void
- */
- public static function batchUlock($lock_arr){
- foreach($lock_arr as $key => $identification){
- self::uLock($key,$identification);
- }
- }
- /**
- * @desc:阻塞读锁
- * @Author: hua
- * @Date: 2021/12/3
- * @Time: 19:24
- * @Copyright: copyright (c) 2021 广东七件事集团
- * @param $key
- * @param $wait_time
- * @return bool
- */
- public static function readLock($key,$wait_time = 30){
- $redis = \Yii::$app->redis;
- $hasWait = 0;//已经等待的时间:毫秒
- while(true){
- $res = $redis->get($key);
- if (!$res){
- return true;
- break;
- }else{
- if($hasWait >= $wait_time*1000){
- //等待超时
- return false;
- }
- usleep(300 * 1000);//等待300ms
- $hasWait += 300;
- continue;
- }
- }
- return false;
- }
- /**
- * 获取频繁锁使用Key
- *
- * @param string $key
- * @return string
- */
- protected static function getFkey(string $key)
- {
- return sprintf('fff%s', $key);
- }
- /**
- * Redis频繁锁
- * 使用Lua脚本设置一个过期值
- * 解锁可以使用unFLock
- * 返回false时候为可以继续执行, 返回true需要返回停止程序返回繁忙信息
- * 使用方式
- *
- * if (LockHelper::fLock($key, $seconds)) return $this->error('忙碌');
- * ---业务逻辑---
- * static::unFLock($key);
- *
- * @param string $key
- * @param integer $seconds 正常操作5秒锁够用 如果业务逻辑比较慢可以增加锁的时间
- * @return boolean
- */
- public static function fLock(string $key, int $seconds = 5)
- {
- $redis = \Yii::$app->redis;
- $script = <<<SCRIPT
- local _val = 1
- local _key = KEYS[1]
- local _seconds = KEYS[2]
- if redis.call("get", _key) then
- return 1
- else
- redis.call("setex", _key, _seconds, _val)
- return 0
- end
- SCRIPT;
- return (bool)$redis->eval($script, 2, static::getFkey($key), $seconds);
- }
- /**
- * 解除Redis频繁锁
- *
- * @param string $key
- * @return boolean
- */
- public static function unFLock(string $key)
- {
- $redis = \Yii::$app->redis;
- $script = <<<SCRIPT
- local _key = KEYS[1]
- return redis.call("del", _key)
- SCRIPT;
- return (bool)$redis->eval($script, 1, static::getFkey($key));
- }
- /**
- * 直接使用回调函数进行操作
- * 使用方式
- * try {
- * LockHelper::callFLock($key, function() use (...) {
- * ---业务逻辑---
- * }, $seconds);
- * } catch (\Exception $e) {
- * return $this->error('忙碌');
- * }
- *
- * @param string $key
- * @param callable $func
- * @param integer $seconds
- * @return void
- * @throws \Exception
- */
- public static function callFLock(string $key, callable $func, int $seconds = 5)
- {
- if (static::fLock($key, $seconds)) throw new \Exception('请勿频繁操作');
- call_user_func($func);
- static::unFLock($key);
- }
- }
|