从PHP发送HTTP请求而不等待响应?

fek*_*lee 40 php curl http server-side web-analytics

我希望从PHP发送HTTP GET请求.例:

http://tracker.example.com?product_number=5230&price=123.52
Run Code Online (Sandbox Code Playgroud)

我们的想法是进行服务器端网络分析:服务器不是将跟踪信息从JavaScript发送到服务器,而是将跟踪信息直接发送到另一台服务器.

要求:

  • 请求应该花费尽可能少的时间,以便不会明显延迟PHP页面的处理.

  • tracker.example.com不需要检查来自的响应.作为示例,一些可能的响应来自 tracker.example.com:

    • 200:没关系,但不需要检查.

    • 404:运气不好,但是 - 再次 - 不需要检查.

    • 301:虽然重定向是合适的,但它会延迟PHP页面的处理,所以不要这样做.

    简而言之:可以丢弃所有回复.

解决方案的想法:

  • 在一个现在删除的答案中,有人建议在shell进程中从PHP 调用命令行 curl.这似乎是一个好主意,只是我不知道在重负载下分配大量shell进程是否明智.

  • 我找到了php-ga,一个用于从PHP进行服务器端Google Analytics的软件包.在项目的页面上,提到:"可以配置为使用非阻塞请求." 到目前为止,我还没有时间研究php-ga内部使用的方法,但这种方法可能就是这样!

简而言之:从PHP进行通用服务器端跟踪/分析的最佳解决方案是什么.

Khe*_*hez 29

不幸的是,PHP的定义是阻塞.虽然这适用于您通常要处理的大多数功能和操作,但当前情况不同.

我喜欢称之为HTTP-Ping的过程要求您只触摸特定的URI,强制特定的服务器启动它的内部逻辑.某些函数允许您通过不等待响应来实现与此HTTP-ping非常相似的操作.

请注意,ping网址的过程分为两个步骤:

  1. 解决DNS
  2. 提出要求

虽然在解析DNS并建立连接后,请求应该相当快,但没有多少方法可以更快地解析DNS.

执行http-ping的一些方法是:

  1. cURL,将CONNECTION_TIMEOUT设置为较低的值
  2. fsockopen写完后立即关闭
  3. stream_socket_client(与fsockopen相同)并且还添加STREAM_CLIENT_ASYNC_CONNECT

虽然两者cURLfsockopen都阻挡而DNS被解决.我注意到即使在最坏的情况下,fsockopen也要快得多.

stream_socket_client 另一方面,应该解决有关DNS解析的问题,并且应该是这种情况下的最佳解决方案,但我还没有设法让它工作.

最后一个解决方案是启动另一个为您执行此操作的线程/进程.对此进行系统调用应该有效,但也要分支当前进程也应该这样做.不幸的是,在无法控制运行PHP的环境的应用程序中,两者都不是很安全.

系统调用通常被阻止,默认情况下不启用pcntl.


Raf*_*shi 14

我会这样调用tracker.example.com:

get_headers('http://tracker.example.com?product_number=5230&price=123.52');
Run Code Online (Sandbox Code Playgroud)

并在跟踪器脚本中:

ob_end_clean();
ignore_user_abort(true);
ob_start();
header("Connection: close");
header("Content-Length: " . ob_get_length());
ob_end_flush();
flush();

// from here the response has been sent. you can now wait as long as you want and do some tracking stuff 

sleep(5); //wait 5 seconds
do_some_stuff();
exit;
Run Code Online (Sandbox Code Playgroud)

  • `ignore_user_abort()` 需要一个值(true),否则它只会返回其当前值。 (2认同)

CET*_*ETb 11

我实现了对url的快速GET请求的功能而无需等待响应:

function fast_request($url)
{
    $parts=parse_url($url);
    $fp = fsockopen($parts['host'],isset($parts['port'])?$parts['port']:80,$errno, $errstr, 30);
    $out = "GET ".$parts['path']." HTTP/1.1\r\n";
    $out.= "Host: ".$parts['host']."\r\n";
    $out.= "Content-Length: 0"."\r\n";
    $out.= "Connection: Close\r\n\r\n";

    fwrite($fp, $out);
    fclose($fp);
}
Run Code Online (Sandbox Code Playgroud)

  • 如果您想将查询参数添加到请求中,请将“GET”行更改为: `$out = "GET ".$parts['path'] 。“?” 。$parts['查询'] 。“HTTP/1.1\r\n”;` (3认同)
  • 简单而优雅的解决方案。谢谢 (2认同)

Nic*_*son 6

我们正在使用fsockopenfwrite组合,然后有一天它停止工作了。或者说是间歇性的。经过一些研究和测试,如果您启用了 fopen 包装器,我最终会使用file_get_contentsstream_context_create函数,并将超时设置为 100 秒。超时参数可以接收浮动值(https://www.php.net/manual/en/context.http.php)。我将它包装在 try...catch 块中,这样它就会默默地失败。它非常适合我们的目的。如果需要,您可以在 catch 中进行记录。如果您不希望函数阻塞运行时,超时是关键。

function fetchWithoutResponseURL( $url )
{

    $context = stream_context_create([
        "http" => [
            "method"=>"GET",
            "timeout" => .01
            ]
        ]
    );

    try {
        file_get_contents($url, 0, $context);
    }catch( Exception $e ){
        // Fail silently
    }
}
Run Code Online (Sandbox Code Playgroud)