本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长

+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

2024-11(1)

PHP秒杀系统学习笔记 2/2

发布于2021-05-30 19:13     阅读(624)     评论(0)     点赞(27)     收藏(0)


一、基本需求分析

秒杀系统-----秒杀服务

秒杀服务核心实现

  • 满足基本需求,做到单服务极致性能
    基本需求:
    1、扣库存
    2、查库存,排队进度
    3、查订单详情,创建订单,支付订单
  • 请求链路流量优化,从客户端到服务端每层优化
  • 稳定性建设
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    扣库存方案
    多进程阻塞式服务
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

二、扣库存分布式实现方案

在这里插入图片描述
步骤实现
在这里插入图片描述

三、扣库存代码演示

base.php
在这里插入图片描述
api.php
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

四、商品信息页及抢购进度查询实现

1、基本需求,创建,支付订单

在这里插入图片描述

2、读商品信息

在这里插入图片描述

3、排队进度查看

在这里插入图片描述

五、高性能的查库存服务实现

1、高性能可读写库存

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、基本需求实现

在这里插入图片描述

六、链路如何实现漏斗型流量

在这里插入图片描述
读商品信息页优化
在这里插入图片描述
读库存优化
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
有效压榨CPU,减少I/O操作,减少接口的调用,通过本地内存,减少cpu上下文的切换,多进程,多线程的切换。
在这里插入图片描述

七、附·代码

1、base.php

<?php

class Base
{
    static $redisObj;

    static function conRedis($config = array())
    {
        if (self::$redisObj) return self::$redisObj;
        self::$redisObj = new \Redis();
        self::$redisObj->connect("127.0.0.1", 6379);
        return self::$redisObj;
    }

    static function output($data = array(), $errNo = 0, $errMsg = 'ok')
    {
        $res['errno'] = $errNo;
        $res['errmsg'] = $errMsg;
        $res['data'] = $data;
        echo json_encode($res);exit();
    }
}

?>

2、api.php

<?php
include('base.php');
class Api extends Base
{
    //共享信息,存储在redis中,以hash表的形式存储,%s变量代表的是商品id
    static $userId;
    static $productId;

    static $REDIS_REMOTE_HT_KEY         = "product_%s";     //共享信息key
    static $REDIS_REMOTE_TOTAL_COUNT    = "total_count";    //商品总库存
    static $REDIS_REMOTE_USE_COUNT      = "used_count";     //已售库存
    static $REDIS_REMOTE_QUEUE          = "c_order_queue";  //创建订单队列

    static $APCU_LOCAL_STOCK    = "apcu_stock_%s";       //总共剩余库存

    static $APCU_LOCAL_USE      = "apcu_stock_use_%s";   //本地已售多少
    static $APCU_LOCAL_COUNT    = "apcu_total_count_%s"; //本地分库存分摊总数

    public function __construct($productId, $userId)
    {
        self::$REDIS_REMOTE_HT_KEY  = sprintf(self::$REDIS_REMOTE_HT_KEY, $productId);
        self::$APCU_LOCAL_STOCK     = sprintf(self::$APCU_LOCAL_STOCK, $productId);
        self::$APCU_LOCAL_USE       = sprintf(self::$APCU_LOCAL_USE, $productId);
        self::$APCU_LOCAL_COUNT     = sprintf(self::$APCU_LOCAL_COUNT, $productId);
        self::$APCU_LOCAL_COUNT     = sprintf(self::$APCU_LOCAL_COUNT, $productId);
        self::$userId               = $userId;
        self::$productId            = $productId;
    }
    static  function clear(){
	apcu_delete(self::$APCU_LOCAL_STOCK);
	apcu_delete(self::$APCU_LOCAL_USE);
	apcu_delete(self::$APCU_LOCAL_COUNT);
		
	}
    /*查剩余库存*/
    static function getStock()
    {
	$stockNum = apcu_fetch(self::$APCU_LOCAL_STOCK);
        if ($stockNum === false) {
            $stockNum = self::initStock();
        }
        self::output(['stock_num' => $stockNum]);
    }

    /*抢购-减库存*/
    static function buy()
    {
        $localStockNum = apcu_fetch(self::$APCU_LOCAL_COUNT);
        if ($localStockNum === false) {
            $localStockNum = self::init();
        }

        $localUse = apcu_inc(self::$APCU_LOCAL_USE);//本已卖 + 1
        if ($localUse > $localStockNum) {//抢购失败 大部分流量在此被拦截
		echo 1;
            self::output([], -1, '该商品已售完');
        }

        //同步已售库存 + 1;
        if (!self::incUseCount()) {//改失败,返回商品已售完
            self::output([], -1, '该商品已售完');
        }

        //写入创建订单队列
        self::conRedis()->lPush(self::$REDIS_REMOTE_QUEUE, json_encode(['user_id' => self::$userId, 'product_id' => self::$productId]));
        //返回抢购成功
        self::output([], 0, '抢购成功,请从订单中心查看订单');
    }

    /*创建订单*/
    /*查询订单*/
    /*总剩余库存同步本地,定时执行就可以*/
    static function sync()
    {
	$data = self::conRedis()->hMGet(self::$REDIS_REMOTE_HT_KEY, [self::$REDIS_REMOTE_TOTAL_COUNT, self::$REDIS_REMOTE_USE_COUNT]);
        $num = $data['total_count'] - $data["used_count"];
        apcu_add(self::$APCU_LOCAL_STOCK, $num);
        self::output([], 0, '同步库存成功');
    }
    /*私有方法*/
    //库存同步
    private static function incUseCount()
    {
        //同步远端库存时,需要经过lua脚本,保证不会出现超卖现象
        $script = <<<eof
            local key = KEYS[1]
            local field1 = KEYS[2]
            local field2 = KEYS[3]
            local field1_val = redis.call('hget', key, field1)
            local field2_val = redis.call('hget', key, field2)
            if(field1_val>field2_val) then
                return redis.call('HINCRBY', key, field2,1)
            end
            return 0
eof;
        return self::conRedis()->eval($script,[self::$REDIS_REMOTE_HT_KEY,  self::$REDIS_REMOTE_TOTAL_COUNT, self::$REDIS_REMOTE_USE_COUNT] , 3);
    }
    /*初始化本地数据*/
    private static function init()
    {
        apcu_add(self::$APCU_LOCAL_COUNT, 150);
        apcu_add(self::$APCU_LOCAL_USE, 0);
    }
    static  function initStock(){
        $data = self::conRedis()->hMGet(self::$REDIS_REMOTE_HT_KEY, [self::$REDIS_REMOTE_TOTAL_COUNT, self::$REDIS_REMOTE_USE_COUNT]);
        $num = $data['total_count']- $data["used_count"];
        apcu_add(self::$APCU_LOCAL_STOCK, $num);
        return $num;
    }

}

try{
$act = $_GET['act'];
$product_id = $_GET['product_id'];
$user_id = $_GET['user_id'];

$obj = new Api($product_id, $user_id);
if (method_exists($obj, $act)) {
    $obj::$act();
    die;
}
echo 'method_error!';
} catch (\Exception $e) {
    echo 'exception_error!';
    var_dump($e);
}



所属网站分类: 技术文章 > 博客

作者:再拍我就焖面

链接:http://www.phpheidong.com/blog/article/86738/b59f33e79f2a7a6e6942/

来源:php黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

27 0
收藏该文
已收藏

评论内容:(最多支持255个字符)