<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Log;
use Mockery\Exception;
use \Redis;
use App\Jobs\SeckillGoods;
class SeckillController extends Controller
{
protected $uid; // 用户id
protected $goodsId = 10086; // 秒抢商品
protected $goodsNum = 100; // 数量限制
protected $redis; // redis实例
protected $lockPrefix = 'lock:';
protected $lockExpireTime = 10;
protected $goodsCacheKeyTag = [
'goodsNumCacheKey' => 'goods_num',
'isSetGoods' => 'is_set_goods_status',
'hadGetGoodsMembers' => 'had_get_goods_members',
'limitUserNumKey' => 'limit_user_num'
];
public function __construct()
{
$redis = new Redis;
$redis->connect('127.0.0.1');
$redis->select(5);
$this->redis = $redis;
$this->uid = intval( request('uid'));
}
/**
* 加锁, 并发下的原子性操作
*/
public function limitAccessFrequency()
{
return $this->redis->set($this->lockPrefix . $this->uid , true , array('nx', 'ex' => $this->lockExpireTime));
}
/**
* 销毁锁
*/
public function __destruct()
{
$this->redis->del($this->lockPrefix . $this->uid);
}
/**
* 开始抢购
* @return array
*/
public function seckill()
{
$uid = $this->uid;
if (empty($uid)) {
return [
'status' => false,
'msg' => '参数错误'
];
}
// 加锁,限制用户访问速率
if( empty($this->limitAccessFrequency())){
return [
'status' => false,
'msg' => '请求频繁~~~~~~~~~~~'
];
}
// 进入队列抢购
return $this->interQueue($uid);
}
/**
* 进入抢购队列
* @param $uid
* @return array
*/
public function interQueue($uid)
{
$redis = $this->redis;
$goodsNumCacheKey = $this->goodsCacheKeyTag['goodsNumCacheKey'];
$hadGetGoodsMembers = $this->goodsCacheKeyTag['hadGetGoodsMembers'];
// 限制进入队列的人数
$limitStatus = $this->limitUser($uid, $this->goodsNum);
if (!$limitStatus['status']) {
return $limitStatus;
}
try {
// 开始redis事务: 抢购到的商品数量加1 ; 事务可能失败
$redis->watch($goodsNumCacheKey);
$res = $redis->multi()
->incr($goodsNumCacheKey)
->exec();
if ($res[0] <= $this->goodsNum) {
$redis->sAdd($hadGetGoodsMembers, $uid); // 抢购成功写入用户名单
} else {
return [
'status' => false,
'msg' => $uid . '来晚一步啦, 商品已经抢空3'
];
}
} catch (Exception $e) {
return [
'status' => false,
'msg' => $uid . '系统繁忙,请稍后再试'
];
}
// 异步写数据
$data = [
'goods_id' => $this->goodsId,
'uid' => $uid,
'goods_num' => 1,
'created_at' => intval(LARAVEL_START)
];
SeckillGoods::dispatch($data);
return ['status' => true, 'msg' => $uid . '抢购成功'];
}
/**
* 限制用户流
* @param $uid
* @param int $num
* @return array
*/
public function limitUser($uid, $goodsN = 100)
{
// 限定人数+50进入
$num = $goodsN + 50;
$limitUserNumKey = $this->goodsCacheKeyTag['limitUserNumKey'];
$hadGetGoodsMembers = $this->goodsCacheKeyTag['hadGetGoodsMembers'];
$goodsNumCacheKey = $this->goodsCacheKeyTag['goodsNumCacheKey'];
$redis = $this->redis;
// 每个用户最多抢1件商品
$hGGM = $redis->sIsMember($hadGetGoodsMembers, $uid);
if ($hGGM) {
return [
'status' => false,
'msg' => $uid . '每个人只能抢购1件商品'
];
}
// 超出限定人数说明已经抢购完
$res = $redis->incr($limitUserNumKey);
if ($res > $num) {
return [
'status' => false,
'msg' => $uid . '来晚一步啦, 商品已经抢空1'
];
}
// 商品已抢完
$goodsNum = $redis->get($goodsNumCacheKey);
if ($goodsNum >= $goodsN) {
return [
'status' => false,
'msg' => $uid . '来晚一步啦, 商品已经抢空2'
];
}
// TODO 查数据库验证用户身份
return ['status' => true];
}
}
laravel+redis实现的抢购(待优化)
猜你喜欢
转载自blog.csdn.net/mathphp/article/details/84336650
今日推荐
周排行