PHP分布式Redis存取数据,PHP 基于redis的分布式锁

{/**

* redis key 前缀*/

const KEY_PREFIX = 'PROCESS_REDIS_LOCK:';/**

* 默认超时时间(秒)*/

const DEFAULT_TIMEOUT = 5;/**

* 最大超时时间(秒)*/

const MAX_TIMEOUT_SETTING = 60;/**

* 随机数的最小值*/

const MIN_RAND_NUM = 0;/**

* 随机数的最大值*/

const RAND_MAX_NUM = 100000;/**

* 每次取锁间隔毫秒数*/

const GET_LOCK_SLEEP_MICRO_SECONDS = 0.1;/**

* @var mixed redis 实例*/

private $redisIns;/**

* @var string 锁名*/

private $lockName;/**

* @var int 超时时间,不可超过 self::MAX_TIMEOUT_SETTING*/

private $timeout;/*单元测试步骤

--------------------------------------------------------------------------------------

1.分别用一个chrome和一个ie,模拟并发请求:

1)http://xxx/test?queryName=task1&handlerTime=10&lockName=myLocktest&timeout=10

2)http://xxx/test?queryName=task2&handlerTime=10&lockName=myLocktest&timeout=5

2.如上

task1处理耗时10s,取锁超时时间10s

task2处理耗时10s,取锁超时时间5s

3.日志结果

2019-11-22 17:16:30 [task1]: 尝试取锁,超时时间设定10秒

2019-11-22 17:16:30 [task1]: 获取到锁,唯一标志:PROCESS_REDIS_LOCK:5dd7a76e1bafe37915

2019-11-22 17:16:32 [task2]: 尝试取锁,超时时间设定5秒

2019-11-22 17:16:35 [task1]: 释放锁

2019-11-22 17:16:35 [task2]: 获取到锁,唯一标志:PROCESS_REDIS_LOCK:5dd7a770d0c2b41355

2019-11-22 17:16:45 [task2]: 释放锁

--------------------------------------------------------------------------------------

单元测试代码

--------------------------------------------------------------------------------------

// 浏览器模拟并发请求

// 注意:这里测试的时候如果session以文件存储的话,要避免两个窗口共用一个会话id,因为同样的会话id在session_start()时会锁文件

// 这样会造成请求阻塞,模拟不了请求并发的情况,所以应该使用两个不同的浏览器(如一个chrome,一个firefox)(同样的浏览器共享cookie也会导致拿到同样的会话id)

public function testAction()

{

$params = $this->getRequest()->getParams();

$this->requestTask($params['queryName'], $params['handlerTime'], $params['lockName'], $params['timeout']);

}

// 模拟取锁、耗时操作、释放锁

public function requestTask($queryName, $handlerTime, $lockName, $timeout)

{

try {

// 获取redis实例

$processRedisLock = new ProcessRedisLock(redis(), $lockName, $timeout);

$this->echoAndSaveInfo($queryName, "尝试取锁,超时时间设定{$timeout}秒");

// 取锁

$id = $processRedisLock->lock();

// 如果到了超时时间还未取到会返回false,则直接抛异常

if($id === false){

throw new Exception('获取锁失败');

}

$this->echoAndSaveInfo($queryName, "获取到锁,唯一标志:".$id);

// 模拟耗时操作

sleep($handlerTime);

// 释放锁

$processRedisLock->unlock($id);

$this->echoAndSaveInfo($queryName, "释放锁");

} catch (Exception $e) {

$this->echoAndSaveInfo($queryName, $e->getMessage());

// do something

}

}

// 输出并且记录日志

public function echoAndSaveInfo($queryName, $content)

{

$info = date('Y-m-d H:i:s') . " [{$queryName}]: {$content}" . PHP_EOL;

echo $info;

file_put_contents('test.txt', $info . PHP_EOL, FILE_APPEND);

}

--------------------------------------------------------------------------------------*/

/**

* ProcessRedisLock constructor.

* @param $redisIns

* @param $lockName

* @param int $timeout

* @throws Exception*/

public function __construct($redisIns, $lockName, $timeout = self::DEFAULT_TIMEOUT)

{if (!$redisIns) {new Exception('The redis instance is empty');

}if(!$lockName){throw new Exception('Lock name invalid');

}//校验超时时间

$timeout = intval($timeout);if (!($timeout > 0 && $timeout <= self::MAX_TIMEOUT_SETTING)) {throw new Exception('The timeout interval is (0,' . self::MAX_TIMEOUT_SETTING . ']');

}$this->redisIns = $redisIns;$this->lockName = $lockName;$this->timeout = $timeout;

}/**

* 加锁

* @return bool

* @Date 2019/11/22*/

public functionlock()

{//redis key

$key = $this->getRedisKey();//唯一标志

$id = $this->getId();//超时时间

$endTime = time() + $this->timeout;//循环取锁

while (time() < $endTime) {//尝试加锁,若给定的 key 已经存在,则 SETNX 不做任何动作。

if ($this->redisIns->setnx($key, $id)) {//设置过期时间,防止程序异常退出没有解锁导致死锁

$this->redisIns->expire($key, $this->timeout);//返回唯一标志,用于解锁

return $id;

}usleep(self::GET_LOCK_SLEEP_MICRO_SECONDS);

}return false;

}/**

* 解锁

* @param string $id 唯一标志,加锁成功时返回

* @return bool

* @Date 2019/11/22*/

public function unlock($id)

{$key = self::getRedisKey();//如果锁的值与没有被修改

if ($this->redisIns->get($key) == $id) {//开始事务

$this->redisIns->multi();//释放该锁

$this->redisIns->del($key);//执行

$this->redisIns->exec();return true;

}else{return false;

}

}/**

* 获取redis key

* @return string

* @Date 2019/11/22*/

public functiongetRedisKey()

{return self::KEY_PREFIX . $this->lockName;

}/**

* 获取唯一标志位

* @Date 2019/11/22*/

public functiongetId()

{return uniqid(self::KEY_PREFIX) . mt_rand(self::MIN_RAND_NUM, self::RAND_MAX_NUM);

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值