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 相关文章推荐
一个用于网络的工具函数库
Oct 09 PHP
php切割页面div内容的实现代码分享
Jul 31 PHP
解析thinkphp基本配置 convention.php
Jun 18 PHP
php遍历CSV类实例
Apr 14 PHP
PHP基于yii框架实现生成ICO图标
Nov 13 PHP
smarty中改进truncate使其支持中文的方法
May 30 PHP
php.ini中date.timezone设置详解
Nov 20 PHP
php实现用户登陆简单实例
Apr 04 PHP
PHP基于递归实现的约瑟夫环算法示例
Aug 27 PHP
php5.x禁用eval的操作方法
Oct 19 PHP
PHP rmdir()函数的用法总结
Jul 02 PHP
tp5框架前台无限极导航菜单类实现方法分析
Mar 29 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
改造一台复古桌面收音机
2021/03/02 无线电
复杂检索数据并分页显示的处理方法
2006/10/09 PHP
PHP中soap的用法实例
2014/10/24 PHP
php输出金字塔的2种实现方法
2014/12/16 PHP
Laravel框架实现调用百度翻译API功能示例
2019/05/30 PHP
php学习笔记之字符串常见操作总结
2019/07/16 PHP
php弹出提示框的是实例写法
2019/09/26 PHP
PHP实现简单的协程任务调度demo示例
2020/02/01 PHP
jquery validation验证身份证号,护照,电话号码,email(实例代码)
2013/11/06 Javascript
javascript加载xml 并解析各节点的值(实现方法)
2016/10/12 Javascript
jQuery操作复选框(CheckBox)的取值赋值实现代码
2017/01/10 Javascript
JS html时钟制作代码分享
2017/03/03 Javascript
jQuery Validate 校验多个相同name的方法
2017/05/18 jQuery
浅谈箭头函数写法在ReactJs中的使用
2017/08/22 Javascript
自定义类似于jQuery UI Selectable 的Vue指令v-selectable
2017/08/23 jQuery
深入理解ES6中let和闭包
2018/02/22 Javascript
js判断非127开头的IP地址的实例代码
2020/01/05 Javascript
微信小程序scroll-view的滚动条设置实现
2020/03/02 Javascript
Python中的赋值、浅拷贝、深拷贝介绍
2015/03/09 Python
Python构建XML树结构的方法示例
2017/06/30 Python
python中的二维列表实例详解
2018/06/19 Python
对python遍历文件夹中的所有jpg文件的实例详解
2018/12/08 Python
Pytorch to(device)用法
2020/01/08 Python
Python matplotlib修改默认字体的操作
2020/03/05 Python
淘宝客服自我总结鉴定
2014/01/25 职场文书
检举信的格式及范文
2014/04/04 职场文书
税务干部群众路线教育实践活动自我剖析材料
2014/09/21 职场文书
2014年监理工作总结范文
2014/11/17 职场文书
离职感谢信怎么写
2015/01/22 职场文书
介绍信样本
2015/01/31 职场文书
英文导游词
2015/02/13 职场文书
公司酒会致辞
2015/07/30 职场文书
一文读懂go中semaphore(信号量)源码
2021/04/03 Golang
vue2的 router在使用过程中遇到的一些问题
2022/04/13 Vue.js
Redis实现订单过期删除的方法步骤
2022/06/05 Redis
微前端qiankun改造日渐庞大的项目教程
2022/06/21 Javascript