V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
swuhvxee
V2EX  ›  PHP

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

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

    接触 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
    solupro
        13
    solupro  
       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)
    vincenttone
        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   ·   实用小工具   ·   3128 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 14:05 · PVG 22:05 · LAX 06:05 · JFK 09:05
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.