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中对xml读取的相关函数的介绍一
Jun 05 PHP
请离开include_once和require_once
Jul 18 PHP
PHP中spl_autoload_register()和__autoload()区别分析
May 10 PHP
ThinkPHP中的create方法与自动令牌验证实例教程
Aug 22 PHP
PHP实现UTF-8文件BOM自动检测与移除实例
Nov 05 PHP
php实现购物车功能(下)
Jan 05 PHP
PHP实现二维数组根据key进行排序的方法
Dec 30 PHP
PHP全功能无变形图片裁剪操作类与用法示例
Jan 10 PHP
PHP实现的登录页面信息提示功能示例
Jul 24 PHP
PHP中递归的实现实例详解
Nov 14 PHP
PHP JWT初识及其简单示例
Oct 10 PHP
Laravel项目中timeAgo字段语言转换的改善方法示例
Sep 16 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的优点与缺点
2013/04/11 PHP
PHP多线程模拟实现秒杀抢单
2018/02/07 PHP
Laravel 简单实现Ajax滚动加载示例
2019/10/22 PHP
从jQuery.camelCase()学习string.replace() 函数学习
2011/09/13 Javascript
JQuery的ON()方法支持的所有事件罗列
2015/02/28 Javascript
jQuery实现转动随机数抽奖效果的方法
2015/05/21 Javascript
常用的JQuery函数及功能小结
2016/03/24 Javascript
基于javascript实现全屏漂浮广告
2016/03/31 Javascript
原生JS封装Ajax插件(同域、jsonp跨域)
2016/05/03 Javascript
基于Node.js的WebSocket通信实现
2017/03/11 Javascript
vuejs响应用户事件(如点击事件)
2017/03/14 Javascript
vue.js使用代理和使用Nginx来解决跨域的问题
2018/02/03 Javascript
vue微信分享的实现(在当前页面分享其他页面)
2019/04/16 Javascript
ES6小技巧之代替lodash
2019/06/07 Javascript
如何自定义微信小程序tabbar上边框的颜色
2019/07/09 Javascript
vue+eslint+vscode配置教程
2019/08/09 Javascript
iSlider手机端图片滑动切换插件使用详解
2019/12/24 Javascript
javascript局部自定义鼠标右键菜单
2020/12/08 Javascript
python技能之数据导出excel的实例代码
2017/08/11 Python
python虚拟环境的安装配置图文教程
2017/10/20 Python
程序员写Python时的5个坏习惯,你有几条?
2018/11/26 Python
pandas基于时间序列的固定时间间隔求均值的方法
2019/07/04 Python
Python 处理文件的几种方式
2019/08/23 Python
Python自带的IDE在哪里
2020/07/01 Python
python递归函数用法详解
2020/10/26 Python
利用HTML5+CSS3实现3D转换效果实例详解
2017/05/02 HTML / CSS
历史学专业大学生找工作的自我评价
2013/10/16 职场文书
高校毕业生自我鉴定
2013/10/27 职场文书
新闻发布会主持词
2014/03/28 职场文书
组织鉴定材料
2014/06/02 职场文书
建筑管理专业求职信
2014/07/28 职场文书
战略合作意向书
2014/07/29 职场文书
初中生毕业评语
2014/12/29 职场文书
大学生个人学习总结
2015/02/15 职场文书
JavaScript与JQuery框架基础入门教程
2021/07/15 Javascript
Python中time标准库的使用教程
2022/04/13 Python