• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    PHP多线程模拟实现秒杀抢单

    应集团要求给服务号做了个抢单秒杀的功能,需要对秒杀做个测试,想试试PHP多线程,就模拟了下抢单功能。

    先说秒杀模块的思路:

    正常情况下的用户秒杀操作

    1、发起秒杀请求
    2、进入秒杀队列
    3、随机滞后 1 - 2 秒进行秒杀结果查询请求(算是变相分流吧)
    4、成功则生成订单
    5、返回结果

    以下是模拟秒杀的代码:

    ?php
    
    
    set_time_limit(0);
    
    /**
    * 线程的执行任务
    */
    class Threadrun extends Thread
    {
      public $url;
      public $data;
      public $params;
    
      public function __construct($url, $params=[])
      {
       $this->url = $url;
       $this->params = $params;
      }
    
      public function run()
      {
       if(($url = $this->url))
       {
         $params = [
          'goods_id'  => 1,
          'activity_id'  => 1,
          'user_id'   => isset($this->params['user_id']) ? $this->params['user_id'] : $this->getCurrentThreadId(),
         ];
         $startTime = microtime(true);
         $this->data = [
          'id'   => $params['user_id'],
          'result'  => model_http_curl_get( $url, $params ),
          'time'  => microtime(true)-$startTime,
          'now'   => microtime(true),
         ];
       }
      }
    }
    
    /**
    * 执行多线程
    */
    function model_thread_result_get($urls_array)
    {
      foreach ($urls_array as $key => $value)
      {
       $threadPool[$key] = new Threadrun($value["url"],['user_id'=>$value['user_id']]);
       $threadPool[$key]->start();
      }
      foreach ($threadPool as $thread_key => $thread_value)
      {
       while($threadPool[$thread_key]->isRunning())
       {
         usleep(10);
       }
       if($threadPool[$thread_key]->join())
       {
         $variable_data[$thread_key] = $threadPool[$thread_key]->data;
       }
      }
      return $variable_data;
    }
    
    /**
    * 发送 HTTP 请求
    */
    function model_http_curl_get($url,$data=[],$userAgent="")
    {
      $userAgent = $userAgent ? $userAgent : 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2)';
      $curl = curl_init();
      curl_setopt($curl, CURLOPT_URL, $url);
      curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
      curl_setopt($curl, CURLOPT_TIMEOUT, 5);
      curl_setopt($curl, CURLOPT_USERAGENT, $userAgent);
      curl_setopt($curl, CURLOPT_POST, true);
      if( !empty($data) ) {
       curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
      }
      $result = curl_exec($curl);
      curl_close($curl);
      return $result;
    }
    
    
    /**
     * 友好的打印变量
     * @param $val
     */
    function dump( $val )
    {
      echo 'pre>';
      var_dump($val);
      echo '/pre>';
    }
    
    /**
     * 写日志
     * @param $msg
     * @param string $logPath
     */
    function writeLog( $msg, $logPath='' ) {
      if( empty($logPath) ) {
       $logPath = date('Y_m_d').'.log';
      }
      if( !file_exists($logPath) ) {
       $fp = fopen( $logPath,'w' );
       fclose( $fp );
      }
      error_log( $msg.PHP_EOL, 3, $logPath);
    }
    
    /**
     * 生成日志信息
     * @param $result
     * @param $timeDiff
     * @return bool|string
     */
    function createLog( $result, $timeDiff ){
      if( empty($result) || !is_array($result) ) {
       return false;
      }
      $succeed = 0;
      $fail = 0;
      foreach( $result as $v ) {
       $times[] = $v['time'];
       $v['result'] === false ? $fail++ : $succeed++;
      }
      $totalTime = array_sum( $times );
      $maxTime = max( $times );
      $minTime = min( $times );
      $sum = count( $times );
      $avgTime = $totalTime/$sum;
      $segment = str_repeat('=',100);
      $flag = $segment . PHP_EOL;
      $flag .= '总共执行时间:' . $timeDiff . PHP_EOL ;
      $flag .= '最大执行时间:' . $maxTime . PHP_EOL;
      $flag .= '最小执行时间:' . $minTime . PHP_EOL;
      $flag .= '平均请求时间:' . $avgTime . PHP_EOL;
      $flag .= '请求数:' . $sum . PHP_EOL;
      $flag .= '请求成功数:' . $succeed . PHP_EOL;
      $flag .= '请求失败数:' . $fail . PHP_EOL;
      $flag .= $segment . PHP_EOL;
      return $flag;
    
    }
    
    
    /**
     * 发起秒杀请求
     */
    function insertList( $urls, $logPath='' )
    {
      $t = microtime(true);
      $result = model_thread_result_get($urls);
      $e = microtime(true);
      $timeDiff = $e-$t;
      echo "总执行时间:" . $timeDiff . PHP_EOL;
      foreach( $result as $v ) {
       $msg = '用户【' . $v['id'] . '】秒杀商品, 返回结果 ' . $v['result'] . ' 用时【' . $v['time'] . ' 秒】 当前时间【'.$v['now'].'】';
       writeLog( $msg,$logPath );
      }
      $logStr = createLog( $result, $timeDiff);
      writeLog( $logStr, $logPath );
      return $result;
    }
    
    
    //发起秒杀请求
    for ($i=0; $i  1000; $i++)
    {
      $urls_array[] = array("name" => "baidu", "url" => "http://***.***.com/seckill/shopping/listinsert");
    }
    
    $list = insertList( $urls_array, './inset.log' );
    
    //发起秒杀结果查询请求
    $urls_array = [];
    foreach( $list as $v ) {
      if( $v['result'] === false ) {
       continue;
      }
      $urls_array[] = array(
            "name"  => "baidu",
            "url"  => "http://***.***.com/seckill/shopping/query",
            'user_id' => $v['id'],
      );
    }
    insertList( $urls_array, './query.log' );
    
    

    测试代码机器性能(开发机):

    订单代码机器性能(测试机):

    系统测试结果:

    模拟 1000 并发的情况,单机每秒 300+ 订单,服务器毫无压力。
    反倒是测试机受不了了,CPU 飙升 100%。 Apache 偶尔崩溃。

    不知道是 PHP 多线程和 Windows 环境的支持不好,还是 PHP 多线程本身的问题,区区 1000 线程跑不动。多线程的地方还是比较需要 Python 和 C 出马。

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

    您可能感兴趣的文章:
    • 浅谈php使用curl模拟多线程发送请求
    • 实例讲解php实现多线程
    • php使用curl_init()和curl_multi_init()多线程的速度比较详解
    • PHP7多线程搭建教程
    • PHP中多线程的两个实现方法
    • php与python实现的线程池多线程爬虫功能示例
    • php多线程并发实现方法
    • php中foreach结合curl实现多线程的方法分析
    • 如何用PHP实现多线程编程
    上一篇:PHP设计模式之装饰器模式实例详解
    下一篇:php+redis消息队列实现抢购功能
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯

    时间:9:00-21:00 (节假日不休)

    地址:江苏信息产业基地11号楼四层

    《增值电信业务经营许可证》 苏B2-20120278

    PHP多线程模拟实现秒杀抢单 PHP,多,线程,模拟,实现,秒杀,