使用场景:在一些敏感操作上,比如:提现、兑换、购买
用户账户余额1000,黑客使用工具快速请求提现接口,导致账户余额判断失效,账号成负数
<?php
namespace ice;
/**
* Redis Lock锁
*/
class Lock
{
private static $_instance;
public $_redis;
private function __construct()
{
if (!extension_loaded('redis')) {
throw new \Exception('not support: redis');
}
$this->_redis = new \Redis();
$this->_redis->connect('127.0.0.1');
}
public static function getInstance()
{
if (self::$_instance instanceof self) {
return self::$_instance;
}
return self::$_instance = new self();
}
/**
* 获取锁
*
* @param String $key 锁标识
* @param Int $expire 锁过期时间
*
* @return Boolean
*/
public static function lock($key, $expire = 5)
{
$objRedisConn = self::getInstance();
//Redis Setnx(SET if Not eXists) 命令在指定的 key 不存在时,为 key 设置指定的值。
//设置成功,返回 1 。 设置失败,返回 0 。
$is_lock = $objRedisConn->_redis->setnx($key, time() + $expire);
// 不能获取锁
if (!$is_lock) {
// 判断锁是否过期
$lock_time = $objRedisConn->_redis->get($key);
// 锁已过期,删除锁,重新获取
if (time() > $lock_time) {
$objRedisConn->unlock($key);
$is_lock = $objRedisConn->_redis->setnx($key, time() + $expire);
}
}
return $is_lock ? false : true;
}
/**
* 释放锁
*
* @param String $key 锁标识
*
* @return Boolean
*/
public static function unlock($key)
{
return self::getInstance()->_redis->del($key);
}
}
使用方法:
1、Lock::lock($key); //上锁,如果已有锁返回真
2、Lock::unlock($key); //不需要锁的时候,手动解锁
$key = 'lock_buy_' . $user->id;
if (Lock::lock($key)) {
$this->error(__('请稍后再试'));
}
$order = Order::where(['status' => 0, 'id' => $id, 'user_id' => $user->id])->findOrEmpty();
if ($order->isEmpty()) {
Lock::unlock($key);
$this->error(__('参数错误'));
}
Lock::unlock($key);
$this->success(__('OK'));