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 相关文章推荐
理解PHP5中static和const关键字的区别
Mar 19 PHP
php 特殊字符处理函数
Sep 05 PHP
openflashchart 2.0 简单案例php版
May 21 PHP
PHP中3种生成XML文件方法的速度效率比较
Oct 06 PHP
php实现发送微信模板消息的方法
Mar 07 PHP
百度地图经纬度转换到腾讯地图/Google 对应的经纬度
Aug 28 PHP
部署PHP时的4个配置修改说明
Oct 19 PHP
深入理解PHP之OpCode原理详解
Jun 01 PHP
php生成图片缩略图功能示例
Feb 22 PHP
php获取目录中所有文件名及判断文件与目录的简单方法
Mar 04 PHP
浅析PHP数据导出知识点
Feb 17 PHP
php ajax confirm 删除实例详解
Mar 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 文件上传系统手记
2009/10/26 PHP
百度ping方法使用示例 自动ping百度
2014/01/26 PHP
thinkphp路由规则使用示例详解和伪静态功能实现(apache重写)
2014/02/24 PHP
PHP数组相关函数汇总
2015/03/24 PHP
php实现生成验证码实例分享
2016/04/10 PHP
css把超出的部分显示为省略号的方法兼容火狐
2008/07/23 Javascript
Jsonp 关键字详解及json和jsonp的区别,ajax和jsonp的区别
2015/12/30 Javascript
基于javascript实现右下角浮动广告效果
2016/01/08 Javascript
Vue系列:通过vue-router如何传递参数示例
2017/01/16 Javascript
js实现textarea限制输入字数
2017/02/13 Javascript
Angular2学习教程之TemplateRef和ViewContainerRef详解
2017/05/25 Javascript
angular 用拦截器统一处理http请求和响应的方法
2017/06/08 Javascript
vue中如何使用ztree
2018/02/06 Javascript
微信小程序实现提交input信息到后台的方法示例
2019/01/19 Javascript
setTimeout与setInterval的区别浅析
2019/03/23 Javascript
详解用js代码触发dom事件的实现方案
2020/06/10 Javascript
Vue实现简单的拖拽效果
2020/08/25 Javascript
python自然语言编码转换模块codecs介绍
2015/04/08 Python
Python中文竖排显示的方法
2015/07/28 Python
Python 装饰器实现DRY(不重复代码)原则
2018/03/05 Python
使用pandas实现csv/excel sheet互相转换的方法
2018/12/10 Python
Python使用post及get方式提交数据的实例
2019/01/24 Python
Python修改列表值问题解决方案
2020/03/06 Python
关于iframe跨域使用postMessage的实现
2019/10/29 HTML / CSS
世界上最好的帽子:Tilley
2016/11/27 全球购物
大学英语演讲稿范文
2014/04/24 职场文书
4s店销售经理岗位职责
2014/07/19 职场文书
化工实习心得体会
2014/09/09 职场文书
大学生学年个人总结
2015/02/15 职场文书
张思德观后感
2015/06/09 职场文书
边城读书笔记
2015/06/29 职场文书
教育教学读书笔记
2015/07/02 职场文书
Django使用channels + websocket打造在线聊天室
2021/05/20 Python
日本十大血腥动漫,那些被禁播的动漫盘点
2022/03/21 日漫
详解Nginx的超时keeplive_timeout配置步骤
2022/05/25 Servers
django项目、vue项目部署云服务器的详细过程
2022/07/23 Servers