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制作的意见反馈表源码
Mar 11 PHP
php 将bmp图片转为jpg等其他任意格式的图片
Jun 29 PHP
PHP+XML 制作简单的留言本 图文教程
Nov 02 PHP
php设计模式 Factory(工厂模式)
Jun 26 PHP
利用PHP函数计算中英文字符串长度的方法
Nov 11 PHP
php实现给一张图片加上水印效果
Jan 02 PHP
Yii开启片段缓存的方法
Mar 28 PHP
php与c 实现按行读取文件实例代码
Jan 03 PHP
php插件Xajax使用方法详解
Aug 31 PHP
php实现微信公众号企业转账功能
Oct 01 PHP
thinkphp5+layui实现的分页样式示例
Oct 08 PHP
PHP中->和=>的意思
Mar 31 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模板Smarty的初级使用方法以及心得分享
2013/06/21 PHP
PHP实现简单用户登录界面
2019/10/23 PHP
10个新的最有前途的JavaScript框架
2009/03/12 Javascript
jquery一句话全选/取消全选
2011/03/01 Javascript
不要使用jQuery触发原生事件的方法
2014/03/03 Javascript
基于jQuery实现的向下滑动二级菜单效果代码
2015/08/31 Javascript
JavaScript与jQuery实现的闪烁输入效果
2016/02/18 Javascript
JavaScript编写一个简易购物车功能
2016/09/17 Javascript
BootStrap Table对前台页面表格的支持实例讲解
2016/12/22 Javascript
iconfont的三种使用方式详解
2018/08/05 Javascript
在vue.js中使用JSZip实现在前端解压文件的方法
2018/09/05 Javascript
使用Angular material主题定义自己的组件库的配色体系
2019/09/04 Javascript
js 计算月/周的第一天和最后一天代码
2020/02/01 Javascript
[01:00:54]TI4正赛第二日开场
2014/07/20 DOTA
[04:45]DOTA2上海特级锦标赛主赛事第四日RECAP
2016/03/06 DOTA
[48:32]VGJ.T vs Fnatic 2018国际邀请赛小组赛BO2 第一场 8.16
2018/08/17 DOTA
[原创]python爬虫(入门教程、视频教程)
2018/01/08 Python
Python简单实现查找一个字符串中最长不重复子串的方法
2018/03/26 Python
Python中flatten( )函数及函数用法详解
2018/11/02 Python
对python遍历文件夹中的所有jpg文件的实例详解
2018/12/08 Python
python实现两个dict合并与计算操作示例
2019/07/01 Python
在Python中使用MySQL--PyMySQL的基本使用方法
2019/11/19 Python
基于python调用psutil模块过程解析
2019/12/20 Python
python对文件的操作方法汇总
2020/02/28 Python
Opencv常见图像格式Data Type及代码实例
2020/11/02 Python
全面解析CSS Media媒体查询使用操作(推荐)
2017/08/15 HTML / CSS
以下的初始化有什么区别
2013/12/16 面试题
会计电算一体化个人简历的自我评价
2013/10/15 职场文书
个人求职信范例
2014/01/29 职场文书
副护士长竞聘演讲稿
2014/04/30 职场文书
党员四风剖析材料
2014/08/27 职场文书
赵氏孤儿观后感
2015/06/09 职场文书
新教师2015年度工作总结
2015/07/22 职场文书
酒桌上的祝酒词
2015/08/12 职场文书
MySQL子查询中order by不生效问题的解决方法
2021/08/02 MySQL
Python使用Opencv打开笔记本电脑摄像头报错解问题及解决
2022/06/21 Python