【问题标题】:Why is Apache returning a zero content length after 200 seconds to a PHP POST request?为什么 Apache 在 200 秒后向 PHP POST 请求返回零内容长度?
【发布时间】:2015-02-09 04:35:52
【问题描述】:

我有一个向远程 API 发布请求的 PHP 脚本。如果响应需要超过大约 200 秒才能返回,那么我只会在响应中得到内容长度为零。我试图弄清楚为什么会这样。

为了解决此问题,我已将 Apache 和 PHP 的配置文件中的每个可能的变量设置为超过 300 秒以解决此问题,如下面的第一个答案所建议的那样。我设置为 300 秒的东西:

  • Apache 超时
  • Apache keep_alive 时间
  • PHP 最大响应时间
  • PHP session.cache_expire 时间
  • PHP 最大执行时间

尽管我仍然始终在 200 秒左右获得零内容长度响应。但是,如果时间少于 200 秒,则不会出现问题。

下面我将描述我们的代码是如何设置的。

发生的情况是 crontab 在我们的服务器上运行一个 shell 脚本,它使用 /usr/bin/curl 调用一个 localhost URI。 localhost URI 由 Apache 提供,它是一个 PHP 文件,它本身包含以下代码,然后使用 cURL 调用远程 API。我们发布了大约 10KB 的 XML,并期望收到大约 135KB 的块返回。

这里是请求代码:

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->_xml_url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_VERBOSE, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml'));
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml_str);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $output = curl_exec($ch);        
        curl_close($ch);

我在我们的 Apache 日志记录中打开了调试,下面是我们得到的结果。在此示例中,请求在 19:48:00 发送,响应在 19:51:23 返回,即 200 多秒后。

* About to connect() to api.asdf.com port 443 (#0)
*   Trying 555.555.555.555... * connected
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs
* SSL connection using RC4-SHA
* Server certificate:
*    subject: snip
*    start date: 2014-03-12 10:22:02 GMT
*    expire date: 2015-04-16 12:32:58 GMT
*    subjectAltName: api.asdf.com matched
*    issuer: C=BE; O=GlobalSign nv-sa; CN=GlobalSign Organization Validation CA - G2
*    SSL certificate verify ok.
> POST /xmlservlet HTTP/1.1

Host: api.asdf.com
Accept: */*
Content-Type: text/xml
Content-Length: 10773
Expect: 100-continue

< HTTP/1.1 100 Continue
< HTTP/1.1 200 OK
< Cache-Control: private
< Content-Type: text/xml
< Server: Microsoft-IIS/7.5
< X-AspNet-Version: 4.0.30319
< X-Powered-By: ASP.NET
< Date: Thu, 06 Nov 2014 19:51:23 GMT
< Content-Length: 0
< 

* Connection #0 to host api.asdf.com left intact
* Closing connection #0

我想知道这段代码是否有问题,或者我可能在服务器设置中遗漏了一些可能导致内容长度在 200 秒后归零的内容。

【问题讨论】:

  • 可能与100 Continue状态有关。尝试在 CURL 请求中禁用它 - curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:', 'Content-Type: text/xml'));
  • 我认为问题不在您身边..它与您正在发送数据的远程主机有关..很可能他们的服务器脚本超时设置为 200s..
  • @SyedQarib 是的,正如我在正确答案中标记的那样,似乎极有可能是这种情况。

标签: php apache


【解决方案1】:

听起来很像远程 API 服务器上的超时 - 尝试手动访问它,例如通过浏览器或 wget。

【讨论】:

  • 我认为你赢了。我尝试通过curl -m 1800 -v -S -H "Content-Type: application/xml" --data @test.xml {MYURI} 手动访问,但我遇到了同样的问题,没有涉及 PHP 或 Apache。因此,除非我在这里没有考虑一些神秘的 curl 选项,否则我认为你是正确的——尤其是在谷歌搜索 Microsoft-IIS/7.5 之后,我发现 200 秒是他们推荐的超时值......我'不是说它是微软,但它是微软。您尝试手动访问它的建议使我消除了足够多的其他可能性,从而对此有 99% 的把握。
  • 好吧,我测试了更多不同的 curl 配置选项,但无论我尝试什么,我们都能得到相同的 200 秒。除非我减小查询的大小,否则它会开始下降到 200 秒以下。那么,基于证据的优势,你和 Iserni 的说法似乎很有可能是正确的。 :D
  • 顺便说一句,为了确定,我尝试了 wget 而不是 curl。同样的问题。以及所有可以想象的 curl 论点。
  • 很高兴能帮上忙! :-)
【解决方案2】:

并且是一个 PHP 文件,它本身包含以下代码,其中 反过来使用 cURL 调用远程 API。我们发布了大约 10KB XML 并期望收到大约 135KB 的块返回。

这里是请求代码:

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $this->_xml_url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_VERBOSE, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml'));
    curl_setopt($ch, CURLOPT_POSTFIELDS, $xml_str);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $output = curl_exec($ch);        
    curl_close($ch);

很可能发生的情况是远程服务器 ($this-&gt;_xml_url) 实际上是一个负载均衡器或其他前端块,它有 200 秒的超时并调用后端服务器。

如果后端服务器在时间范围内没有响应,前端服务器会关闭后端连接并继续执行(这可能是错误的做法)并向您的脚本发送“成功”的答案与它所获得的内容。

这没什么。因此内容长度为零。

您能做的最好的事情是通过检查数据长度或 XML 一致性来识别问题,然后重试或通知用户。我会使用低于 200 秒的超时时间 - 比如说 180 秒 - 以确保 将是引发错误的人,即远程服务器永远不会挂断你,会挂断

如果有某种方法可以加快请求速度(不同的 API?不同的编码?缓存结果的可能性?更昂贵的 SLA?等),请尝试通知 API 服务器维护人员。

【讨论】:

  • 恕我直言,您的回答也是正确的,但 Tyron 在技术上是第一位的,并建议尝试使用手动方法来诊断问题并消除其他可能性。通知 API 服务器维护人员的问题是,这些特定的人从不响应打开的票证,没有任何其他 SLA,没有缓存结果的可能性,没有其他编码。你关于负载平衡器的提示可能是正确的,我已经向他们提交了一张票,可能会提示他们,尽管我很确定这张票会直接进入他们的“循环文件”。
  • 完全没问题。我也赞成 Tyron 的简洁性!很遗憾听到维护者缺乏支持...
  • 嗯...我们将看看他们这次是否有什么不同。就是这样。我只是想确保我们的服务器不是原因。谢谢。
【解决方案3】:

您是否尝试过明确设置 HTTP Keepalive ?像这样:

curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Connection: Keep-Alive',
    'Keep-Alive: 300'
));

【讨论】:

  • 刚刚测试过,并没有解决问题。但是感谢您的想法。
  • 注意:我也将此纳入我的手动测试中。同样的事情。
【解决方案4】:

你试过在 PHP 中使用set_time_limit

http://php.net/manual/en/function.set-time-limit.php

php 的默认值通常为 30 秒,可在 php.ini 中找到


有关配置 Apache 的信息,请参阅 timeoutkeepalivekeepalivetimeoutmaxkeepaliverequests

http://users.cis.fiu.edu/~downeyt/cgs4854/timeout


另请参阅 http://www.devside.net/wamp-server/apache-and-php-limits-and-timeouts 以获得良好的整体教程。

我已经能够用 apache 和 php 运行请求了 15 分钟,所以你可以延长它相当长的时间。

【讨论】:

  • 我试过 set_time_limit 但它会被我设置为 300000 的 max_execution_time 自动覆盖。至于 apache,超时设置为 300,所以这不可能是问题。同时keepalivetimeout 只与从外部进入apache 的请求有关。我的脚本在 PHP 中使用 CURL 来发送请求,但 CURL 的默认设置是不对等待响应的时间设置任何限制。它最初对 POST 请求获得 200 OK 响应,但在 200 秒后仅获得一个空白字符串作为响应。为什么?我的系统上任何地方都没有 curl 配置文件。
  • curl 有一个默认超时时间,设置为 300 秒(不是 200 秒),您可以使用 curl_setopt($ch, CURLOPT_TIMEOUT, 400); 覆盖它。无论如何,如果达到 curl 的超时时间,curl_exec 将返回 false,而不是一个空的服务器响应。
【解决方案5】:

确保您使用的是您尝试访问的外部资源的完整 url 路径(因为我们无法从您的代码示例中验证这一点),更重要的是,请确保您的 cron 命令是从您的脚本所在的路径执行的居住在,像这样:

1 1 * * 0 /path/to/your/script php &lt; yourscript.php

这样做将确保正确找到相对位置路径引用的任何内容。

请记住,当您通过浏览器测试某些内容时,您的 apache 日志很好,但是由于您正在执行脚本,因此与 crontab 相关的错误将被发送到您的 cron 输出文件。

【讨论】:

  • 我们不使用 php CLI 来访问脚本。 cron 作业调用 curl 来访问它。 cron 的输出被发送到我们的 crontab 中的 dev null。 Apache error.log 是我们的输出。
  • 默认情况下,cron(本机)只要获得初始握手响应就应该运行超过 200 秒,但您可能需要传递一个最大超时长度(以秒为单位)以防您的服务器设置为小于 200。我也将尝试使用 php cli 方法,而不是循环遍历您的 shell 脚本,因为您实际上是在将 cron 调用从服务器嵌套到自身,然后再到外部世界。
  • 正如其他 cmets 所述,我完全绕过了 cron、php 和 Apache,只是手动运行了 curl 请求(请参阅我对 Tyron 的回答的评论)。我得到了相同的结果,这意味着它与远程服务器无关。
猜你喜欢
  • 1970-01-01
  • 2021-07-07
  • 2017-11-25
  • 2017-10-01
  • 2012-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-06
相关资源
最近更新 更多