搜索
博主信息
博文 142
粉丝 5
评论 0
访问量 157004
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
武汉阿波罗**PHP多线程模拟实现秒杀抢单
php开发大牛
原创
1789人浏览过

应武汉阿波罗**集团总部要求给服务号做了个抢单秒杀的功能,需要对秒杀做个测试,想试试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 出马。


本博文版权归博主所有,转载请注明地址!如有侵权、违法,请联系admin@php.cn举报处理!
全部评论 文明上网理性发言,请遵守新闻评论服务协议
1条评论
刘静 2020-08-18 13:28:11
怎么样可以关注博主??
1楼
作者最新博文
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号

  • 登录PHP中文网,和优秀的人一起学习!
    全站2000+教程免费学