首页  >  文章  >  后端开发  >  PHP多线程模拟实现秒杀抢单活动(附代码)

PHP多线程模拟实现秒杀抢单活动(附代码)

php中世界最好的语言
php中世界最好的语言原创
2018-05-19 10:05:073226浏览

这次给大家带来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 class="brush:php;toolbar:false">';
  var_dump($val);
  echo '
'; } /**  * 写日志  * @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中文网其它相关文章!

推荐阅读:

redis计数器类使用步骤详解

php处理抢购类高并发请求实现详解

以上是PHP多线程模拟实现秒杀抢单活动(附代码)的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn