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


Posted in PHP onFebruary 07, 2018

应集团要求给服务号做了个抢单秒杀的功能,需要对秒杀做个测试,想试试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' );

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

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

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

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

系统测试结果:

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

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

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

PHP 相关文章推荐
php echo()和print()、require()和include()函数区别说明
Mar 27 PHP
php中通过curl模拟登陆discuz论坛的实现代码
Feb 16 PHP
PHP CLI模式下的多进程应用分析
Jun 03 PHP
PHP json_encode中文乱码问题的解决办法
Sep 09 PHP
php的curl封装类用法实例
Nov 07 PHP
php检测图片主要颜色的方法
Jul 01 PHP
php防止CC攻击代码 php防止网页频繁刷新
Dec 21 PHP
php实现的操作excel类详解
Jan 15 PHP
smarty自定义函数用法示例
May 20 PHP
PHP文件操作实例总结
Sep 27 PHP
利用PHP绘图函数实现简单验证码功能的方法
Oct 18 PHP
PHPExcel中文帮助手册|PHPExcel使用方法(分享)
Jun 09 PHP
PHP设计模式之装饰器模式实例详解
Feb 07 #PHP
PHP使用星号替代用户名手机和邮箱的实现代码
Feb 07 #PHP
PHP unlink与rmdir删除目录及目录下所有文件实例代码
Feb 07 #PHP
php删除一个路径下的所有文件夹和文件的方法
Feb 07 #PHP
浅析PHP类的反射来实现依赖注入过程
Feb 06 #PHP
php打开本地exe程序,js打开本地exe应用程序,并传递相关参数方法
Feb 06 #PHP
PHP给源代码加密的几种方法汇总(推荐)
Feb 06 #PHP
You might like
强烈推荐:php.ini中文版(1)
2006/10/09 PHP
php在文件指定行中写入代码的方法
2012/05/23 PHP
解析如何修改phpmyadmin中的默认登陆超时时间
2013/06/25 PHP
php生成N个不重复的随机数实例
2013/11/12 PHP
php实现支持中文的文件下载功能示例
2017/08/30 PHP
让whoops帮我们告别ThinkPHP6的异常页面
2020/03/02 PHP
JQuery 无废话系列教程(一) jquery入门 [推荐]
2009/06/23 Javascript
JS中 用户登录系统的解决办法
2013/04/15 Javascript
javascript实现的DES加密示例
2013/10/30 Javascript
JS实现自定义简单网页软键盘效果代码
2015/11/05 Javascript
DataTables添加额外的查询参数和删除columns等无用参数实例
2017/07/04 Javascript
JavaScript中的FileReader图片预览上传功能实现代码
2017/07/24 Javascript
vue+Java后端进行调试时解决跨域问题的方式
2017/10/19 Javascript
详解在vue-cli中使用graphql即vue-apollo的用法
2018/09/08 Javascript
解决VUE中document.body.scrollTop为0的问题
2018/09/15 Javascript
详解vue中async-await的使用误区
2018/12/05 Javascript
使用nodejs分离html文件里的js和css详解
2019/04/12 NodeJs
TypeScript 运行时类型检查补充工具
2020/09/28 Javascript
[38:44]DOTA2上海特级锦标赛A组小组赛#2 Secret VS CDEC第二局
2016/02/25 DOTA
Python numpy实现数组合并实例(vstack,hstack)
2018/01/09 Python
15行Python代码带你轻松理解令牌桶算法
2018/03/21 Python
numpy.linspace 生成等差数组的方法
2018/07/02 Python
windows系统中Python多版本与jupyter notebook使用虚拟环境的过程
2019/05/15 Python
python定时任务 sched模块用法实例
2019/11/04 Python
Python zip函数打包元素实例解析
2019/12/11 Python
Python tkinter制作单机五子棋游戏
2020/09/14 Python
英国领先品牌手动工具和电动工具供应商:Tooled Up
2018/11/24 全球购物
恒华伟业笔试面试题
2015/02/26 面试题
软件设计的目标是什么
2016/12/04 面试题
医药大学生求职简历的自我评价
2013/10/17 职场文书
渡河少年教学反思
2014/02/12 职场文书
期中考试反思800字
2014/05/01 职场文书
书香家庭事迹材料
2014/05/09 职场文书
社区五一劳动节活动总结
2015/02/09 职场文书
2019学校运动会开幕词
2019/05/13 职场文书
vue修饰符.capture和.self的区别
2022/04/22 Vue.js