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小型企业库存管理系统的设计与实现代码
May 16 PHP
解析如何用php screw加密php源代码
Jun 20 PHP
PHP同时连接多个mysql数据库示例代码
Mar 17 PHP
php实现递归的三种基本方式
Jul 04 PHP
Session 失效的原因汇总及解决丢失办法
Sep 30 PHP
php如何连接sql server
Oct 16 PHP
CI框架中redis缓存相关操作文件示例代码
May 17 PHP
PHP实现多关键字加亮功能
Oct 21 PHP
ThinkPHP中Widget扩展的两种写法及调用方法详解
May 04 PHP
php微信公众号开发之翻页查询
Oct 20 PHP
tp5框架的增删改查操作示例
Oct 31 PHP
Thinkphp 框架基础之入口文件功能、定义与用法分析
Apr 27 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和数据库结合的一个简单的web实例 代码分析 (php初学者)
2011/07/28 PHP
Yii框架表单提交验证功能分析
2017/01/07 PHP
在IE下获取object(ActiveX)的Param的代码
2009/09/15 Javascript
一些mootools的学习资源
2010/02/07 Javascript
jquery 选项卡效果 新手代码
2011/07/08 Javascript
JS多物体 任意值 链式 缓冲运动
2012/08/10 Javascript
浅析javascript中的DOM
2015/03/01 Javascript
针对初学者的jQuery入门指南
2015/08/15 Javascript
详解BootStrap中Affix控件的使用及保持布局的美观的方法
2016/07/08 Javascript
canvas简单快速的实现知乎登录页背景效果
2017/05/08 Javascript
详解angular 中的自定义指令之详解API
2017/06/20 Javascript
Node.js使用Express.Router的方法
2017/11/14 Javascript
vue实现文字横向无缝走马灯组件效果的实例代码
2019/04/09 Javascript
JavaScript实现拖拽盒子效果
2020/02/06 Javascript
vue使用map代替Aarry数组循环遍历的方法
2020/04/30 Javascript
JavaScript中继承原理与用法实例入门
2020/05/09 Javascript
浅谈JavaScript中this的指向更改
2020/07/28 Javascript
JavaScript中作用域链的概念及用途讲解
2020/08/06 Javascript
[06:21]完美世界亚洲区首席发行官竺琦TI3采访
2013/08/26 DOTA
python3利用smtplib通过qq邮箱发送邮件方法示例
2017/12/03 Python
详解python使用Nginx和uWSGI来运行Python应用
2018/01/09 Python
python向已存在的excel中新增表,不覆盖原数据的实例
2018/05/02 Python
python 实现一次性在文件中写入多行的方法
2019/01/28 Python
python plotly绘制直方图实例详解
2019/07/22 Python
python3 deque 双向队列创建与使用方法分析
2020/03/24 Python
屈臣氏越南官网:Watsons越南
2021/01/14 全球购物
Java里面Pass by value和Pass by Reference是什么意思
2016/05/02 面试题
国际政治个人自荐信范文
2013/11/26 职场文书
付款承诺函范文
2015/01/21 职场文书
人事任命书范本
2015/09/21 职场文书
当你焦虑迷茫时,请读读这6句话
2019/07/24 职场文书
用Python写一个简易版弹球游戏
2021/04/13 Python
Nginx进程管理和重载原理详解
2021/04/22 Servers
css3带你实现3D转换效果
2022/02/24 HTML / CSS
Apache Hudi数据布局黑科技降低一半查询时间
2022/03/31 Servers
如何开启Apache,Nginx和IIS服务器的GZIP压缩功能
2022/04/29 Servers