美文网首页
PHP多进程初探

PHP多进程初探

作者: gao922699 | 来源:发表于2022-08-04 08:04 被阅读0次

最近遇到用phpexcel生成excel文件导出,大数据量的时候会遇到PHP内存不足的问题(memory limit)。经测试差不多生成10M左右的excel文件要占用100M左右的内存。解决这个问题准备了三种方案:

  1. 调大php的执行内存限制

  2. 分批导出excel文件,用php开子进程的方式在每个子进程中处理单个excel的生成,在主进程中用zip打包供用户下载

  3. 分批导出excel文件,用php内部调用接口的方式实现,在接口中生成单个excel文件,通过多次调用接口的方式突破内存限制

第一种方案最简单,在代码执行前通过ini_set('limit_memory','1024M')方法来调整内存限制;

第二种方法通过开子进程的方法,只要每个子进程内处理的的单个excel文件不超过内存限制就可以;

第三种方法通过接口实现单个excel的生成,再在主逻辑中调用接口来拼接zip包,循环中调用接口是串行的,所以效率会比较低,不过用curl_multi方法可以实现并行请求,没有实践,预计可行。

主要介绍第二种方法的实现:

  1. 安装php的zip扩展和pnctl扩展

  2. 概念介绍

    官方wiki:https://www.php.net/manual/zh/book.pcntl.php

    多进程

    几个重要函数:

    pcntl_fork — 在当前进程当前位置产生分支(子进程)。译注:fork是创建了一个子进程,父进程和子进程 都从fork的位置开始向下继续执行,不同的是父进程执行过程中,得到的fork返回值为子进程 号,而子进程得到的是0。

    pcntl_wait — 挂起当前进程的执行直到一个子进程退出或接收到一个信号要求中断当前进程或调用一个信号处理函数。

  3. 伪代码:

//每个excel文件处理2000行数据
$maxCount = 2000;
//带条件的查询
$query = XXXX;
//数据总数
$total = $query->count();
//随机的批次号
$batchId = rand(1000,9999).'-'.date('Ymd-His');
Redis::set('offset_'.$batchId,0);
//zip文件名
$zipFileName = "var/export/" .$batchId . ".zip";
//创建zip对象
$zip = new ZipArchive();
$zip->open($zipFileName,ZipArchive::CREATE);
//存储生成的单个excel文件名
$tempFileArray = [];
//用while循环开多进程
while (Redis::get('offset_'.$batchId) < $total){
        //开子进程
        $pids = pcntl_fork();
        if ($pids == -1) {
            //开子进程失败
            die('fork error');
        } else if ($pids) {
            //这里是主进程
            pcntl_wait($status);    //在子进程结束前挂起主进程,防止出现僵尸进程
        } else {
            //这里是子进程.子进程会复制父进程中的变量,但是如果想要修改值是不行的,变量不共享(这里的$maxCount,$batchId);
            //如果想要共享变量可以使用第三方存储,如redis(这里的$offset);
            //获取分页的数据库数据
            $offset = Redis::get('offset_'.$batchId);
            $collection = $query->limit($maxCount)->offset($offset)->all();
            //生成excel文件的逻辑
            .....
            $excelFileName = 'var/export/'.$batchId.'-'.$offset.'-'.($offset+$maxCount).'.excel';
            //添加excel文件到zip包内
            $zip->addFile($excelFileName);
       }
           //添加excel文件名到临时数组(命名规则保持一致,如果在子进程中记录需要把tempFileArray记录到redis中去)
            $tempFileArray[] = 'var/export/'.$batchId.'-'.$offset.'-'.($offset+$maxCount).'.excel';
           //更新Redis中的offset
           $offset += $maxCount;
           Redis::set('offset_'.$batchId,$offset);
       }
  }
  $zip->close();
  //销毁临时文件
  foreach ($tempFileArray as $fileName) {
       unlink($fileName);
  }
  //返回zip包地址
  return $zipFileName;

4. 可能遇到的坑

  • 控制总的进程数,防止内存爆掉

  • for循环内进程是串行执行的,但是为了以防万一的并发问题,如果操作同一个数据表或者文件还是加个队列比较好

相关文章

  • PHP多进程初探

    最近遇到用phpexcel生成excel文件导出,大数据量的时候会遇到PHP内存不足的问题(memory limi...

  • PHP中的“进程”系列1——PHP-FPM模型

    PHP中的“进程”系列 这个系列会分几个部分,从PHP-FPM进程模式起,到Linux进程,最后回到PHP本身谈一...

  • 杀掉所有grep到的进程

    以php进程为例 grep php 查询关键字带有php的进程(查询结果会带有grep php)grep -v g...

  • PHP创建守护进程

    PHP 创建守护进程 执行守护进程

  • 2019-06-27

    用PHP玩转进程之二 — 多进程PHPServer 2018-09-02 系统设计 语言 PHP 经过用 PHP ...

  • php| 初探 rabbitmq

    date: 2018-09-03 21:30:23title: php| 初探 rabbitmqdescripti...

  • [PHP] - 编译参数 --enable-pcntl

    官网:PHP - PCNTL 红框翻译 PHP进程控制支持 实现了类unix的进程创建、程序运行、消息处理、进程终...

  • php-fpm

    php-fpm说明 php-fpm是FastCGI的实现,并提供进程管理的功能。进程包括master进程和work...

  • 初探php

    常量与变量 变量:$; 常量:const;可以使用define定义常量;

  • php-fpm解读-进程管理的三种模式

    《我是程序媛》系列——php-fpm进程管理,感谢大表哥亲情赞助时间,读了php-fpm源码。 php-fpm进程...

网友评论

      本文标题:PHP多进程初探

      本文链接:https://www.haomeiwen.com/subject/agaswrtx.html