zoukankan      html  css  js  c++  java
  • 使用Redis类库处理一般的抢购(秒杀)活动示例

    1、创建抢购活动Redis类库文件

    <?php
    /**
     * Created by PhpStorm.
     */
    
    namespace appaseservice;
    
    
    use mikkle	p_redisRedisHashInfoBase;
    use thinkException;
    
    class ScheduleDetail  extends RedisHashInfoBase
    {
        protected $table="gopar_schedule_detail";  //数据表的
        protected $pk = "id"; //数据表的主键
    
    
        public function _initialize()
        {
            //判断数据存在 并设置检查周期10分钟
            if (!$this->checkLock("dataExists") && !$this->checkTableDataExists()){
                throw  new  Exception("相关产品数据不存在");
            }else{
                //设置检查锁10分钟
                $this->setLock("dataExists",600);
            }
            //如果数据不存在 初始化读取数据
            if (!$this->checkExists()){
                $this->initTableData();
            }
        }
    
    
        public function getScheduleCenter()
        {
            return Schedule::instance( $this->getInfoFieldValue("schedule_id"));
        }
    
    
        public function __destruct()
        {
            //设置15天自动回收redis
            $this->setExpire((int)$this->getScheduleCenter()->getInfoFieldValue("end_time")+3600*24*15);
        }
    }

    2、在服务层或者控制器处理抢购逻辑

        public function index($data=["user_id"=>1,"ticket_detail_id"=>1,"buy_num"=>1]){
            try {
                //检测数据存在
                if (!$this->checkArrayValueEmpty($data,["user_id","ticket_detail_id","buy_num"])){
                    throw  new  Exception($this->error);
                }
    
                $user_id= $data["user_id"] ;              //用户Id
                $ticket_detail_id = $data["ticket_detail_id"] ;  //产品Id
                $buy_num = $data["buy_num"] ;   //购买数量
    
                $infoCenter= ScheduleDetail::instance( $ticket_detail_id );
                $scheduleDetailInfo =$infoCenter->getInfoList();
                //修改数据库后 需要运行initTableData()方法重新初始化 推荐写到Hook里
               // $infoCenter->initTableData();
                if ( $infoCenter->getInfoFieldValue( "hot_schedule")){
                    //热门抢购随机过滤随机过滤
                    if (!in_array(rand(100, 200) % 11, [1, 3, 5, 7, 9])) {
                        throw  new  Exception("抢票人数众多 ,你被挤出抢购队伍,还有余票,请重新再抢");
                    };
                }
                // 这里判断 购买数量和销售日期 不符合就  throw  new  Exception
                if (!true){
                    throw  new  Exception("这里写不符合原因");
                }
                if (((int)$infoCenter->getInfoFieldValue("{$user_id}_num")+$buy_num)>$scheduleDetailInfo["limit_num"] ){
                    throw  new  Exception("你超过最大购买数量");
                }
                if ($infoCenter->setInfoFieldIncre("pay_num",$buy_num) >$scheduleDetailInfo["limit_num"] ){
                    //
                    $infoCenter->setInfoFieldIncre("pay_num", -$buy_num);
                    throw  new  Exception("对不起,票已经卖光了!");
                }
                //这里写主逻辑 启用事务功能创建订单 
                //事务参见下节源码
                
               
                //升级已销售数量
                $infoCenter->updateTableData(["pay_num"]);
                
                //在这里推荐埋钩子   处理订单完成的后续事情
    
                 //返回结果
    
            } catch (Exception $e) {
                Log::error($e->getMessage());
                return ShowCode::jsonCodeWithoutData(1008, $e->getMessage());
            }
        }
    
    
    }

    3.定时队列判断订单是否处理完成 校准剩余库存

    <?php
    /**
     * Created by PhpStorm.
     */
    
    namespace mikkle	p_worker;
    
    use mikkle	p_masterException;
    use mikkle	p_masterLog;
    
    /**
     * title  定时队列类
     * Class TimingWorkerBase
     * @package mikkle	p_worker
     * 创建定时队列类并继承使用方法
     * class Test extends TimingWorkerBase
     * {
     * protected function runHandle($data)
     * {
     * Log::notice(  "测试".RandNumCenter::getTimeString()  );
     * }
     * }
     *
     * 添加方法定时队列方法
     *  appworkerTest::add(["name"=>"mikkle",],30);
     */
    
    abstract class TimingWorkerBase extends WorkerBase
    {
        protected $listName;
        protected $listData;
        protected $listNum;
        protected $lockName;
    
        public function _initialize($options = [])
        {
            $this->listData = "{$this->listName}_data";
            $this->listNum = "{$this->listName}_num";
        }
    
    
        /**
         *      * 快速定时任务
         *
         * 当命令行未运行 直接执行
         * description add
         * @param $data
         * @param $runTime
         * @param array $options
         * @param string $handleName
         * @return bool
         */
        static public function add($data, $runTime = 0, $handleName = "run", $options = [])
        {
            try {
                $data = json_encode($data);
                $instance = static::instance($options);
                switch (true) {
                    case (self::checkCommandRun()):
                        $time = $instance->getRunTime($runTime);
                        $num = $instance->redis()->incre($instance->listNum);
                        Log::notice("添加了 $num 号定时任务");
                        $instance->redis()->zAdd($instance->listName, [$time => $num]);
                        $instance->redis()->hSet($instance->listData, $num, $data);
                        Log::notice("Timing Command service start work!!");
                        $instance->runWorker($handleName);
                        break;
                    default:
                        Log::notice("Timing Command service No away!!");
                        $instance->runHandle($data);
                }
                return true;
            } catch (Exception $e) {
                Log::error($e->getMessage());
                return false;
            }
        }
    
        /**
         * 命令行执行的方法
         */
        static public function run()
        {
            try {
                $i = 0;
                $instance = static::instance();
                //读取并删除定时任务
                $workList = $instance->redis()->zRangByScore($instance->listName, 0, time());
                $instance->redis()->zDelete($instance->listName, $workList);
                //剩余任务数
                $re = $instance->redis()->zCard($instance->listName);
                if ( $workList ){
                    foreach ($workList as $num) {
                        try {
                            $redisData = $instance->redis()->hGet($instance->listData, $num);
                            if ($redisData) {
                                $data = json_decode($redisData, true);
                                $result = $instance->runHandle($data);
                                Log::notice("执行{$num}编号任务");
                                if ($instance->saveLog) {
                                    $instance->saveRunLog($result, $data);
                                }
                                $instance->redis()->hDel($instance->listData, $num);
                            }
                        } catch (Exception $e) {
                            Log::error($e->getMessage());
                            $instance->redis()->zAdd($instance->listData, [(time() + 300) => $num]);
                        }
                        $i++;
                        sleep(1);
                    }
                }
                if ( $re=== 0) {
                    $instance->clearWorker();
                }
                echo "执行了{$i}次任务,剩余未执行任务[{$re}]项" . PHP_EOL;
                Log::notice("执行了{$i}次任务,剩余未执行任务[{$re}]项");
            } catch (Exception $e) {
                //Log::error($e);
                Log::error($e->getMessage());
                echo($e->getMessage());
            }
        }
    
    
        protected function getRunTime($time = 0)
        {
            $now = time();
            switch (true) {
                case ($time == 0):
                    return $now;
                    break;
                case (is_int($time) && 30 * 3600 * 24 > $time):
                    return $now + $time;
                    break;
                case (is_int($time) && $now < $time):
                    return $time;
                    break;
                default:
                    return $now + (int)$time;
            }
        }
    }
  • 相关阅读:
    自定义Filter服务
    filter 以及 orderBy的使用
    ng-repeat-start ng-repeat-end 的使用
    ng-repeat 与ng-switch的简单应用
    ng-bind-html 的使用
    Oracle instr用法
    Oracle left查询案例
    Oracle case用法
    mysql 导出导入sql
    Gson解析复杂JSON对象
  • 原文地址:https://www.cnblogs.com/swmin/p/9950913.html
Copyright © 2011-2022 走看看