PHP多线程模拟实现秒杀抢单

应集团要求给服务号做了个抢单秒杀的功能,需要对秒杀做个测试,想试试PHP多线程,就模拟了下抢单功能

先说秒杀模块的思路:

正常情况下的用户秒杀操作

1、发起秒杀请求 2、进入秒杀队列 3、随机滞后 1 - 2 秒进行秒杀结果查询请求(算是变相分流吧) 4、成功则生成订单 5、返回结果

以下是模拟秒杀的代码

rush: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 '
    ';
    var_dump($val);
    echo '
    ';
    }

/**

  • 写日志
  • @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' );

测试代码机器性能(开发机):

订单代码机器性能(测试机):

系统测试结果:

模拟 1000 并发的情况,单机每秒 300+ 订单,服务器毫无压力。 反倒是测试机受不了了,cpu 飙升 100%。 Apache 偶尔崩溃。

不知道是 PHP 多线程和 Windows 环境的支持不好,还是 PHP 多线程本身的问题,区区 1000 线程跑不动。多线程的地方还是比较需要 Python 和 C 出马。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程之家。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


服务器优化必备:深入了解PHP8底层开发原理
Golang的网络编程:如何快速构建高性能的网络应用?
Golang和其他编程语言的对比:为什么它的开发效率更高?
PHP8底层开发原理揭秘:如何利用新特性创建出色的Web应用
将字符重新排列以形成回文(如果可能)在C++中
掌握PHP8底层开发原理和新特性:创建高效可扩展的应用程序
服务器性能优化必学:掌握PHP8底层开发原理
PHP8新特性和底层开发原理详解:优化应用性能的终极指南
将 C/C++ 代码转换为汇编语言
深入研究PHP8底层开发原理:创建高效可扩展的应用程序
C++程序查找法向量和迹
PHP8底层开发原理实战指南:提升服务器效能
重排数组,使得当 i 为偶数时,arr[i] >= arr[j],当 i 为奇数时,arr[i] <= arr[j],其中 j < i,使用 C++ 语言实现
Golang的垃圾回收:为什么它可以减少开发人员的负担?
C++程序:将一个数组的所有元素复制到另一个数组中
Golang:构建智能系统的基石
为什么AI开发者应该关注Golang?
在C和C++中,逗号(comma)的用法是用来分隔表达式或语句
PHP8底层开发原理解析及新特性应用实例
利用PHP8底层开发原理解析新特性:如何构建出色的Web应用