请教关于 PHP foreach 长时间运行中断的一个问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
swuhvxee
V2EX    PHP

请教关于 PHP foreach 长时间运行中断的一个问题

  •  1
     
  •   swuhvxee 2018-03-26 00:06:08 +08:00 5403 次点击
    这是一个创建于 2762 天前的主题,其中的信息可能已经有所发展或是发生改变。

    接触 PHP 不久,正在学习。现在碰到了这么一个问题。

    有一批数据需要读取本地文件,并根据读取出来的每个 id 来请求 API 获取值,然后把值写入数据库。不可避免的用到了 foreach。

    但是每次运行一会的时候,浏览器就提示 500---Internal Server Error 错误。php 代码也加上了 set_time_limit(0);ini_set('memory_limit', '-1')。

    那么请问,这种情况是因为什么原因呢?是浏览器有默认的超时时间吗,脚本长时间不返回,浏览器就默认给超时了?还是因为超出了内存限制,给中断了?

    大家碰到这种需要长时间处理大量数据的时候,是怎么解决的呢?因为是自学,所以没有人指点。有没有函数或者办法,可以解决以下 2 个问题。

    1:浏览器超时(可能我描述不准确,但是说的就是浏览器获取不到返回值默认 500 )

    2:这种循环太多了,内存肯定不够用的。那么是否可以随用随丢?

    24 条回复    2018-04-03 10:49:53 +08:00
    jhdxr
        1
    jhdxr  
       2018-03-26 00:15:24 +08:00   1
    1. 超时不光在 php 这有限制,web server 那同样可能有限制。当然这种场景你现在这么处理的做法可以说就是错的,正确做法(之一)是把任务写到一个队列里去,然后另外在命令行里开一个专门读取然后处理。

    2. 可以。文件读取参考 fopen 及相关函数,避免将文件一次性读入内存中。部分场景可以考虑用 generator 来减少内存的使用。占大量内存的数组在不需要时可以手动 unset,这样 GC 就会及时回收了。
    swuhvxee
        2
    swuhvxee  
    OP
       2018-03-26 00:21:50 +08:00
    @jhdxr 谢谢,我去查一查
    Weny
        3
    Weny  
       2018-03-26 00:25:20 +08:00
    队列
    akira
        4
    akira  
       2018-03-26 00:28:54 +08:00   1
    长时间运行的话,在 php 这边先提前响应数据给页面,然后继续执行后续逻辑代码。
    参考 https://www.jianshu.com/p/398792cf0ed5

    但是在你这个情况里面,个人感觉不是这个问题,更多的像是你代码出错了,检查下错误日志吧
    root123
        5
    root123  
       2018-03-26 00:35:56 +08:00   1
    这种场景就不应该用浏览器去执行,是错的。

    1、用 cli 守护进程,如果要提高效率就的配合 pcntl_fork 子进程。

    2、用 while (!feof($file)) 分段读取
    lsido
        6
    lsido  
       2018-03-26 00:51:41 +08:00 via iPhone
    这种场景就应该用 Python,捂脸捂脸捂脸捂脸(手动滑稽)
    swuhvxee
        7
    swuhvxee  
    OP
       2018-03-26 02:24:58 +08:00
    @akira 代码没错的,500 中断之前,所有的数据处理全部正确,500 之后的数据处理如果分批次执行也正确。应该不是代码的问题
    swuhvxee
        8
    swuhvxee  
    OP
       2018-03-26 02:26:12 +08:00
    @root123 谢谢提醒,刚才 google 到了这个问题,跟你说的差不多
    shiji
        9
    shiji  
       2018-03-26 02:49:06 +08:00
    这种情况我一般都是直接在终端里运行 PHP。
    DavidNineRoc
        10
    DavidNineRoc  
       2018-03-26 07:13:31 +08:00 via Android
    PHP 生成器了解一下
    mcfog
        11
    mcfog  
       2018-03-26 07:55:47 +08:00 via Android
    你应该先学会开 /看 /查错误日志
    linzhoulxyz
        12
    linzhoulxyz  
       2018-03-26 08:38:10 +08:00
    先查下日志是什么原因造成的 500
    solu
        13
    solu  
       2018-03-26 11:35:03 +08:00
    可以试试加上
    ignore_user_abort
    silencefent
        14
    silencefent  
       2018-03-26 11:37:45 +08:00   1
    用命令行 php path/index.php XXX/XXX/api 执行 php 方法
    dangyuluo
        15
    dangyuluo  
       2018-03-26 12:47:07 +08:00
    beanstalkd,搞个伺服程序
    yytsjq
        16
    yytsjq  
       2018-03-26 13:15:37 +08:00
    php-fpm 的 request_terminate_timeout 太小?
    eslizn
        17
    eslizn  
       2018-03-26 13:18:28 +08:00
    cli 了解一下
    xuechaoc
        18
    xuechaoc  
       2018-03-26 15:40:45 +08:00   1
    长时间运行的脚本不应该出现在 web 请求里面,建议使用消息队列等异步方式。或者直接在终端运行脚本。
    另外 500 错误一般也不是由于超时导致的,执行时间过长导致的超时,nginx 会返回错误码 504 Gateway Time Out
    qce7
        19
    qce7  
       2018-03-26 16:56:00 +08:00
    500---Internal Server Error

    这是代码写错了,开启一下 error_display=on;
    swuhvxee
        20
    swuhvxee  
    OP
       2018-03-26 18:40:37 +08:00
    @shiji
    @DavidNineRoc
    @silencefent
    @eslizn
    好的,感谢解惑

    @qce7
    哥,虽然我感谢你的回答,但是你的回答是全部答案里最不靠谱最水的一个。代码错没错我能不知道吗
    joeke
        21
    joeke  
       2018-03-26 18:43:21 +08:00
    可以尝试一下 迭代器啊 ,占用内存小,再加一个 set_time_limit(0)
    img src="https://cdn.v2ex.com/gravatar/ed4127e0b58e6d0f753a987f895abebd?s=48&d=retro" class="avatar" border="0" align="default" alt="vincenttone" data-uid="29884" />
        22
    vincenttone  
       2018-03-26 18:54:55 +08:00
    感觉楼主的应用是实时渲染,但是又有大量的 IO 请求。实际上可以做成异步,crontab 做个定时。
    关于 foreach 的停止,可能是超时或者异常引起的,如果是 php7,可以用 try catch 捕获来防止这种问题。
    如果数量非常多的话,可以考虑对文件内容做个分割,通过 fork 子进程来处理。
    如果做成异步轻量级的应用,可以考虑用 redis 做个缓存或者直接文件缓存。
    qce7
        23
    qce7  
       2018-03-27 09:18:41 +08:00
    nginx 500,你看你现在连是哪行代码抛出异常都不清楚,就去优化,这难道不应该去开错误显示或者日志吗吗。。。
    NowTime
        24
    NowTime  
       2018-04-03 10:49:53 +08:00 via Android
    命令行了解下,你这种效率太低了
    写好代码,比如保存为 run.php
    执行命令
    php run.php
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     891 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 21ms UTC 21:39 PVG 05:39 LAX 14:39 JFK 17:39
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86