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 相关文章推荐
windows下开发并编译PHP扩展的方法
Mar 18 PHP
PHP 观察者模式的实现代码
May 10 PHP
一些php项目中比较通用的php自建函数的详解
Jun 06 PHP
php中的常用魔术方法总结
Aug 02 PHP
PHP循环结构实例讲解
Feb 10 PHP
PHP获取指定函数定义在哪个文件中以及其所在的行号实例
May 08 PHP
php使用date和strtotime函数输出指定日期的方法
Nov 14 PHP
thinkPHP数据查询常用方法总结【select,find,getField,query】
Mar 15 PHP
PHP使用preg_split和explode分割textarea存放内容的方法分析
Jul 03 PHP
PHP设计模式之模板模式定义与用法详解
Dec 20 PHP
php生成静态页面并实现预览功能
Jun 27 PHP
PHP图像处理 imagestring添加图片水印与文字水印操作示例
Feb 06 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 将bmp图片转为jpg等其他任意格式的图片
2009/06/29 PHP
PHP的cURL库功能简介 抓取网页、POST数据及其他
2011/04/07 PHP
php中将地址生成迅雷快车旋风链接的代码[测试通过]
2011/04/20 PHP
php数字运算验证码的实现代码
2015/07/30 PHP
解析WordPress中的post_class与get_post_class函数
2016/01/04 PHP
关于PHP中Session文件过多的问题及session文件保存位置
2016/03/17 PHP
CakePHP框架Model关联对象用法分析
2017/08/04 PHP
PHP5.5基于mysqli连接MySQL数据库和读取数据操作实例详解
2019/02/16 PHP
jQuery EasyUI API 中文文档 - TimeSpinner时间微调器
2011/10/23 Javascript
jQuery动画效果相关方法实例分析
2015/12/31 Javascript
最实用的jQuery分页插件
2016/10/09 Javascript
Jqprint实现页面打印
2017/01/06 Javascript
jquery Easyui Datagrid实现批量操作(编辑,删除,添加)
2017/02/20 Javascript
VUE实现一个分页组件的示例
2017/09/13 Javascript
前端html中jQuery实现对文本的搜索功能并把搜索相关内容显示出来
2017/11/14 jQuery
JS实现验证码倒计时的注册页面
2018/01/02 Javascript
vue中实现在外部调用methods的方法(推荐)
2018/02/08 Javascript
javascript如何使用函数random来实现课堂随机点名方法详解
2020/07/28 Javascript
详细解读tornado协程(coroutine)原理
2018/01/15 Python
Windows下的Python 3.6.1的下载与安装图文详解(适合32位和64位)
2018/02/21 Python
python使用turtle库绘制树
2018/06/25 Python
Python对数据进行插值和下采样的方法
2018/07/03 Python
Python中 map()函数的用法详解
2018/07/10 Python
python脚本之一键移动自定格式文件方法实例
2019/09/02 Python
基于Python中isfile函数和isdir函数使用详解
2019/11/29 Python
PyQt5 如何让界面和逻辑分离的方法
2020/03/24 Python
最简单的matplotlib安装教程(小白)
2020/07/28 Python
美国领先的奢侈手表在线零售商:WatchMaxx
2017/12/17 全球购物
打架检讨书100字
2014/01/19 职场文书
创意广告词
2014/03/17 职场文书
大四学生找工作的自荐信
2014/03/27 职场文书
暑假家长评语大全
2014/04/17 职场文书
体育运动口号
2014/06/09 职场文书
党的群众路线教育实践活动对照检查材料
2014/09/22 职场文书
2014年网络管理员工作总结
2014/12/01 职场文书
人生哲理妙语30条:淡写流年,笑过人生
2019/09/04 职场文书