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初学者们头痛的十四个问题
Jan 15 PHP
PHP+Mysql+jQuery实现发布微博程序 jQuery篇
Oct 08 PHP
thinkphp区间查询、统计查询与SQL直接查询实例分析
Nov 24 PHP
PHP中浮点数计算比较及取整不准确的解决方法
Jan 09 PHP
PHP信号量基本用法实例详解
Feb 12 PHP
CI框架出现mysql数据库连接资源无法释放的解决方法
May 17 PHP
thinkphp3.x中session方法的用法分析
May 20 PHP
Yii2组件之多图上传插件FileInput的详细使用教程
Jun 20 PHP
Yii2针对指定url的生成及图片等的引入方法小结
Jul 18 PHP
php中文字符串截取多种方法汇总
Oct 06 PHP
PHP数组内存利用率低和弱类型详细解读
Aug 10 PHP
PHP命名空间(namespace)原理与用法详解
Dec 11 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
SONY SRF-40W电路分析
2021/03/02 无线电
php自动适应范围的分页代码
2008/08/05 PHP
PHPwind整合最土系统用户同步登录实现方法
2010/12/08 PHP
php魔术方法与魔术变量、内置方法与内置变量的深入分析
2013/06/03 PHP
php实现最简单的MVC框架实例教程
2014/09/08 PHP
php设置页面超时时间解决方法
2015/09/22 PHP
Symfony2中被遗弃的getRequest()方法分析
2016/03/17 PHP
thinkPHP5框架auth权限控制类与用法示例
2018/06/12 PHP
Laravel框架实现的上传图片到七牛功能详解
2019/09/06 PHP
禁止直接访问php文件代码分享
2020/05/05 PHP
下载网站打开页面后间隔多少时间才显示下载链接地址的代码
2010/04/25 Javascript
js实现交换运动效果的方法
2015/04/10 Javascript
使用jquery获取url以及jquery获取url参数的实现方法
2016/05/25 Javascript
AngularJS ng-blur 指令详解及简单实例
2016/07/30 Javascript
vue2.0数据双向绑定与表单bootstrap+vue组件
2017/02/27 Javascript
jQuery Plupload上传插件的使用
2017/04/19 jQuery
详解react-native WebView 返回处理(非回调方法可解决)
2018/02/27 Javascript
React从react-router路由上做登陆验证控制的方法
2018/05/10 Javascript
fetch 如何实现请求数据
2018/12/20 Javascript
iview的table组件自带的过滤器实现
2019/07/12 Javascript
vue swipeCell滑动单元格(仿微信)的实现示例
2020/09/14 Javascript
python3.3实现乘法表示例
2014/02/07 Python
python通过floor函数舍弃小数位的方法
2015/03/17 Python
列举Python中吸引人的一些特性
2015/04/09 Python
python直接访问私有属性的简单方法
2016/07/25 Python
Python实现简单遗传算法(SGA)
2018/01/29 Python
python读取csv文件并把文件放入一个list中的实例讲解
2018/04/27 Python
Python如何调用JS文件中的函数
2019/08/16 Python
关于pytorch中网络loss传播和参数更新的理解
2019/08/20 Python
HTML5 LocalStorage 本地存储刷新值还在
2017/03/10 HTML / CSS
英国文具、办公用品和科技商店:Ryman
2018/09/27 全球购物
经典优秀个人求职信分享
2013/12/12 职场文书
食品安全演讲稿
2014/09/01 职场文书
2015年图书馆个人工作总结
2015/05/26 职场文书
个人催款函范文
2015/06/24 职场文书
python 下载文件的几种方式分享
2021/04/07 Python