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_Flame(Version:Progress)的原代码
Oct 09 PHP
PHP用户指南-cookies部分
Oct 09 PHP
PHP图片上传类带图片显示
Nov 25 PHP
PHP中防止SQL注入实现代码
Feb 19 PHP
php设计模式 Delegation(委托模式)
Jun 26 PHP
php使用PDO操作MySQL数据库实例
Dec 30 PHP
PHP模拟asp中response类实现方法
Aug 08 PHP
ThinkPHP 模板substr的截取字符串函数详解
Jan 09 PHP
ThinkPHP框架表单验证操作方法
Jul 19 PHP
PHP基于IMAP收取邮件的方法示例
Aug 07 PHP
PHP crc32()函数讲解
Feb 14 PHP
ThinkPHP框架实现FTP图片上传功能示例
Apr 08 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中for循环语句的几种变型
2006/11/26 PHP
亲密接触PHP之PHP语法学习笔记1
2006/12/17 PHP
第三章 php操作符与控制结构代码
2011/12/30 PHP
PHP计算指定日期所在周的开始和结束日期的方法
2015/03/24 PHP
php app支付宝回调(异步通知)详解
2018/07/25 PHP
laravel-admin的图片删除实例
2019/09/30 PHP
JavaScript Accessor实现说明
2010/12/06 Javascript
jQuery UI Autocomplete 体验分享
2012/02/14 Javascript
jQuery异步上传文件插件ajaxFileUpload详细介绍
2015/05/19 Javascript
jQuery实现在列表的首行添加数据
2015/05/19 Javascript
jquery实现下拉菜单的手风琴效果
2017/07/23 jQuery
ActiveX控件的使用-js实现打印超市小票功能代码详解
2017/11/22 Javascript
Vue中的异步组件函数实现代码
2018/07/20 Javascript
Vue-router 中hash模式和history模式的区别
2018/07/24 Javascript
JavaScript常见继承模式实例小结
2019/01/11 Javascript
vue3 源码解读之 time slicing的使用方法
2019/10/31 Javascript
Django实现自定义404,500页面教程
2017/03/26 Python
详解python OpenCV学习笔记之直方图均衡化
2018/02/08 Python
python 显示数组全部元素的方法
2018/04/19 Python
python脚本调用iftop 统计业务应用流量的思路详解
2019/10/11 Python
在django项目中导出数据到excel文件并实现下载的功能
2020/03/13 Python
django中嵌套的try-except实例
2020/05/21 Python
Python  word实现读取及导出代码解析
2020/07/09 Python
Python 代码调试技巧示例代码
2020/08/11 Python
详解Selenium 元素定位和WebDriver常用方法
2020/12/04 Python
HTML5 canvas 基本语法
2009/08/26 HTML / CSS
可能这些是你想要的H5软键盘兼容方案(小结)
2019/04/23 HTML / CSS
优秀信贷员先进事迹
2014/01/31 职场文书
赔偿协议书范本
2014/04/15 职场文书
作文批改评语大全
2014/04/23 职场文书
小学学校评估方案
2014/06/08 职场文书
霸气队列口号
2014/06/18 职场文书
五五普法心得体会
2014/09/04 职场文书
幸福家庭事迹材料
2014/12/20 职场文书
Python字符串对齐方法使用(ljust()、rjust()和center())
2021/04/26 Python
React Fragment介绍与使用详解
2021/11/11 Javascript