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
DW中链接mysql数据库时,建立字符集中文出现乱码的解决方法
Mar 27 PHP
php.ini中date.timezone设置分析
Jul 29 PHP
php利用iframe实现无刷新文件上传功能的代码
Sep 29 PHP
ThinkPHP后台首页index使用frameset时的注意事项分析
Aug 22 PHP
CI框架Session.php源码分析
Nov 03 PHP
注意!PHP 7中不要做的10件事
Sep 18 PHP
PHP类相关知识点实例总结
Sep 28 PHP
阿里云Win2016安装Apache和PHP环境图文教程
Mar 11 PHP
浅谈PHP中pack、unpack的详细用法
Mar 12 PHP
PHP消息队列实现及应用详解【队列处理订单系统和配送系统】
May 20 PHP
Laravel第三方包报class not found的解决方法
Oct 13 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
iis下php mail函数的sendmail配置方法(官方推荐)
2012/04/25 PHP
Javascript isArray 数组类型检测函数
2009/10/08 Javascript
javascript中的prototype属性使用说明(函数功能扩展)
2010/08/16 Javascript
THREE.JS入门教程(5)你应当知道的十件事
2013/01/24 Javascript
同时使用n个window onload加载实例介绍
2013/04/25 Javascript
JavaScript原生xmlHttp与jquery的ajax方法json数据格式实例
2015/12/04 Javascript
JavaScript中rem布局在react中的应用
2015/12/09 Javascript
详解AngularJS中的filter过滤器用法
2016/01/04 Javascript
Angularjs 制作购物车功能实例代码
2016/09/14 Javascript
JS作用域闭包、预解释和this关键字综合实例解析
2016/12/16 Javascript
微信小程序 基础知识css样式media标签
2017/02/15 Javascript
Vue自定义指令拖拽功能示例
2017/02/17 Javascript
详解AngularJS controller调用factory
2017/05/19 Javascript
微信小程序tabBar底部导航中文注解api详解
2017/08/16 Javascript
vue中node_modules中第三方模块的修改使用详解
2019/05/31 Javascript
在小程序中推送模板消息的实现方法
2019/07/22 Javascript
小程序实现上下移动切换位置
2019/09/23 Javascript
vue中keep-alive,include的缓存问题
2019/11/26 Javascript
如何区分vue中的v-show 与 v-if
2020/09/08 Javascript
vue实现顶部菜单栏
2020/11/08 Javascript
[01:33:07]VGJ.T vs Newbee Supermajor 败者组 BO3 第一场 6.6
2018/06/07 DOTA
python实现在无须过多援引的情况下创建字典的方法
2014/09/25 Python
Python实现大文件排序的方法
2015/07/10 Python
发布你的Python模块详解
2016/09/15 Python
详解python中asyncio模块
2018/03/03 Python
详解如何将python3.6软件的py文件打包成exe程序
2018/10/09 Python
Python操作Mongodb数据库的方法小结
2019/09/10 Python
Python如何获取Win7,Win10系统缩放大小
2020/01/10 Python
Python类的动态绑定实现原理
2020/03/21 Python
keras训练浅层卷积网络并保存和加载模型实例
2020/07/02 Python
Python自省及反射原理实例详解
2020/07/06 Python
英国最大的女性服装零售商:Dorothy Perkins
2017/03/30 全球购物
常用UNIX 命令(Linux的常用命令)
2013/07/10 面试题
事业单位个人应聘自荐信
2013/09/21 职场文书
个人担保书格式范文
2014/05/12 职场文书
使用Mysql计算地址的经纬度距离和实时位置信息
2022/04/29 MySQL