Redis 秒杀系统实战代码
本来这个是想弄到博客的实验室里面的,但是测试后服务器顶不住测试,容易崩就放弃了
下面是具体的代码,大概主要的注释都有了,有疑问留言哈哈
/**
* 使用 Predis 这个库操作redis
* $request 和 $this->success()这是tp5框架和自己封装的json数据返回 自己定义吧
*/
/**
* 系统前缀
*/
protected $system_prefix='cc_test_';
/**
* 测试时长 5分钟
* @var int
*/
protected $second = 300;
/**
* Redis 秒杀测试初始化
* @param Request $request
*/
public function redisTestShopInit(Request $request)
{
/**
* 当前请求ip
*/
$current_ip = $_SERVER['REMOTE_ADDR'];
/**
* 先定义好每个测试员的key的前缀
*/
$prefix = $request->post('prefix');
if(strpos($prefix,'_')===false){
$prefix .= '_';
}
/**
* 定义商品数量
*/
$shop_num=$request->post('shop_num');
/**
* 测试商品名称
*/
$shop_name = $request->post('shop_name');
/**
* 秒杀时间长度:秒
*/
$second_len=$request->post('second_len');
if($second_len>300){//最大5分钟
$second_len=300;
}
$second_len=300;//这里我默认5分钟好了
$system_prefix =$this->system_prefix;
/**
* 测试者的前缀
* cc_test_192.168.1.106_
*/
$tester_prefix = $system_prefix . $current_ip . '_';//cc_test_192.168.1.106_
/**
* 这里好像跟我后面的有点出入, 还是留着吧 看看就行
*
* 1.把商品放入hash中 假设key为 xxxxxx_googd:1
* 2.先缓存待秒杀的商品id在 list中
* 3.设置商品库存在sorted set
*/
/**
* redis list 商品列表key
*/
$redis_shop_list_key=$tester_prefix.'shop_list';
/**
* 商品库存key score_set 暂时没用 后面需要库存再考虑
*/
$redis_shop_zset_key=$tester_prefix.'shop_inventory_score_set';
$client = new \Predis\Client();
if($client->exists($redis_shop_list_key)){
$this->success('商品已经初始化了,请勿重复','',4010);
}
//初始化商品
for ($i = 0; $i < $shop_num; $i++) {
$tmp_shop_key=$tester_prefix.'test_shop:'.$i;
$tmp_shop_hash_detail_key=$tmp_shop_key;
$tmp_shop_hash_detail_title=$shop_name.'-'.$i;
$tmp_shop_hash_detail_num=10;
$tmp_shop_hash_detail_content='我是 '.$shop_name.' 商品的详情'.$i.'-'.$i.'-'.$i.'-'.$i;
//把商品详情记录在 hash中 定义key,并把key放入 list中
$client->hset($tmp_shop_hash_detail_key, 'id', $i);
$client->hset($tmp_shop_hash_detail_key, 'title', $tmp_shop_hash_detail_title);
$client->hset($tmp_shop_hash_detail_key, 'content', $tmp_shop_hash_detail_content);
$client->hset($tmp_shop_hash_detail_key, 'num', $tmp_shop_hash_detail_num);//库存
$client->lpush($redis_shop_list_key, $tmp_shop_key);//把秒杀商品的key放进list中
//设置商品库存 (把库存存在scoreset中这中方法有待考察,觉得必要性不强)
// $client->zadd($redis_shop_zset_key, [$tmp_shop_hash_detail_key => 10]);
//把详情的也设置超时时长 提供给别人的测试需要设置超时时长,正式就不需要了
$client->expire($tmp_shop_hash_detail_key, $second_len);
}
//设置秒杀列表存在时长
$client->expire($redis_shop_list_key, $second_len);
$shop_lists= $client->lrange($redis_shop_list_key, 0, -1);
$data = array();
foreach ($shop_lists as $ind=>$shop_key) {
$data[$ind]['key']=$shop_key;
$data[$ind]['title']=$client->hget($shop_key,'title');
$data[$ind]['num']=$client->hget($shop_key,'num');
}
$this->success('success',$data,200);
}
/**
* 获取秒杀商品列表
*/
public function redisTestGetSeckillShopList(Request $request)
{
$current_ip =$_SERVER['REMOTE_ADDR'];
$system_prefix =$this->system_prefix;
$tester_prefix = $system_prefix . $current_ip . '_';//cc_test_192.168.1.106_
$redis_shop_list_key=$tester_prefix.'shop_list';
$client = new \Predis\Client();
$page = $request->post('page');
$page=$page<0?0:$page;
$start=($page-1)*10;
$end=$start+10;
$shop_lists= $client->lrange($redis_shop_list_key, $start, $end);
$data = array();
foreach ($shop_lists as $ind=>$shop_key) {
$data[$ind]['key']=$shop_key;
$data[$ind]['title']=$client->hget($shop_key,'title');
$data[$ind]['num']=$client->hget($shop_key,'num');
}
$this->success('success', $data,200);
}
/**
* 商品购买
* @param Request $request
*/
public function redisTestBuyShop(Request $request)
{
$shop_hash_key = $request->post('shop_key');
$buyer_ip = $_SERVER['REMOTE_ADDR'];
$second=$this->second;
/**
* 1.先判断商品是否过期,库存是否足够
* 2.用户购买了商品,商品库存-1
* 3.把商品key和用户放在一个list中,这个list就是购买成功人员的记录,后续需要存入数据库做持久化保存
*
*/
//score set 这个可以用作库存管理,后续再弄吧
$client = new \Predis\Client();
$tester_prefix = $this->system_prefix . $buyer_ip . '_';
$redis_shop_list_key=$tester_prefix.'shop_list';
// $client->multi();
if(!$client->exists($redis_shop_list_key)){
$this->success('秒杀活动过期','',4001);
}
if(!$client->exists($shop_hash_key)){
$this->success('商品过期','',4002);
}
$num=$client->hget($shop_hash_key,'num');
if($num<=0){
$client->hset($shop_hash_key, 'num', 0);
$this->success('库存不足','',4003);//库存没了
}
$client->hincrby($shop_hash_key, 'num', -1);
$buyer_list=$this->system_prefix.'ip:'.$buyer_ip.'_buyer_list';
$order_num = mt_rand(10000000, 99999999);
$buyer_hash_key=$this->system_prefix.'buyer_hash_buyer.ip:'.$buyer_ip.'_ordernum:'.$order_num;//为每个购买者生成一个订单号
$client->hset($buyer_hash_key, 'created_at', time());
$client->hset($buyer_hash_key, 'shop_key',$shop_hash_key);
$client->hset($buyer_hash_key, 'buy_num',1);
$client->hset($buyer_hash_key, 'buyer_ip',$buyer_ip);
$client->expire($buyer_hash_key, $second);
//记录购买者购买信息key在列表中
$client->lpush($buyer_list, $buyer_hash_key);
$client->expire($buyer_list, $second);
// $client->exec();
//秒杀结束后需要把这些购买成功的存入数据库中
$this->success('购买成功', $client->hgetall($buyer_hash_key));
}
/**
* 秒杀系统测试:
* -------------------------------------------------------------------------
* 第一次JMeter测试 : 200并发 测试10次,没问题,不会超出库存
* 第二次JMeter测试 : 500并发 测试10次,没问题,不会超出库存
* 第三次JMeter测试 : 1000并发 测试10次,失败 会超出库存
* 修改代码第四次JMeter测试 : 1000并发 测试10次,成功,不会超出库存
* 第四次JMeter测试 : 1000并发 测试50次,成功,不会超出库存
* 第五次JMeter测试 : 5000并发 测试50次, 成功 不会超出库存 但是有些请求直接崩了(这个先暂时不管了哈哈)
* 第六次JMeter测试 : 10000并发 测试10次, 成功 不会超出库存 服务器崩了
* --------------------------------------------------------------------------
*/