百度了几天自己总结了下写了一个类,里面有加锁、解锁和入队的操作,异步的减库存出队等的操作还没有写,写好了我再重新编辑
用apache的ab测试-n 500 -c 100测试了下没有出现超卖的情况
这段代码没有在项目里使用过,所以大家有觉得哪里有问题请指出,我们一起讨论完善这个代码
<?php
class RedisLock{
function __construct(){
$r = new \Redis();
$r->pconnect('127.0.0.1', '6379');
$this->redis = $r;
$this->timeout = 3;
$this->good_stock_queue = "good_stock_queue_";
$this->joined_queue = "joined_queue_";
$this->uid_queue = "uid_queue";
}
function get_set_by_name($name,$type=''){
$rs = $this->redis;
if($type == 'zset'){
return $rs->zrange($name,0,-1);
}
return $rs->get($name);
}
//初始化库存队列
function init_goodstock_queue($num,$gid){
$rs = $this->redis;
$rs->del($this->good_stock_queue.$gid);
for ($i=0; $i < $num; $i++) {
$rs->lpush($this->good_stock_queue.$gid,1);
}
$result = $rs->llen($this->good_stock_queue.$gid);
if($result){
return true;
}
return false;
}
//加锁操作
function add_lock($key,$timeout=null,$waitsec = 100000){
$rr = $this->redis;
$lock_name = $this->get_lock_name($key);
$timeout = $timeout ? $timeout : $this->timeout;
$time = time();
$expire_time = $time + $timeout;
$get_lock = $rr->setnx($lock_name,$expire_time);
if($get_lock){
return $expire_time;
}
while(1){
usleep($waitsec);
$old_expire_time = $rr->get($lock_name);
//如果锁的生存时间未过期,继续获取
if($old_expire_time >= time()){
continue;
}
$new_expire_time = time() + $timeout;
$last_expire_time = $rr->getset($lock_name,$new_expire_time);
//继续尝试获取,如果与上一个锁的score不一致,可能是其他请求获取到了锁,则继续
if($last_expire_time != $old_expire_time){
continue;
}
$get_lock = $new_expire_time;
break;
}
return $get_lock;
// dump($rr->get('ok'));
}
//释放锁
function release_lock($key,$score){
$rr = $this->redis;
$lock_name = $this->get_lock_name($key);
if($score < time()){
if($rr->del($lock_name)){
return true;
}
}
return false;
}
//获取锁名
function get_lock_name($key){
return "lock_".$key;
}
//入队
function enqueue($uid,$gid){
$rr = $this->redis;
//判断是否再已参与的队列中
$is_joined = $rr->get($this->joined_queue.$gid, $uid);
// dump($is_joined);
if(!$is_joined){
//判断库存队列
$stock_num = $rr->llen($this->good_stock_queue.$gid);
// dump($stock_num);
if($stock_num){
//入队
$time_score = time();
$uid_queue_len = $rr->zcard($this->uid_queue);
//这里为了测试假定只接受10个人参加秒杀
if($uid_queue_len < 10){
$se = $rr->zadd($this->uid_queue, $time_score,$uid);
}
// $rr->lpop($this->good_stock_queue.$gid);
return true;
}
}
return false;
}
//出队
function dequeue(){
}
}