Send HTTP request from PHP without waiting for res

2019-01-16 18:19发布

问题:

I want to have an HTTP GET request sent from PHP. Example:

http://tracker.example.com?product_number=5230&price=123.52

The idea is to do server-side web-analytics: Instead of sending tracking information from JavaScript to a server, the server sends tracking information directly to another server.

Requirements:

  • The request should take as little time as possible, in order to not noticeably delay processing of the PHP page.

  • The response from the tracker.example.com does not need to be checked. As examples, some possible responses from tracker.example.com:

    • 200: That's fine, but no need to check that.

    • 404: Bad luck, but - again - no need to check that.

    • 301: Although a redirect would be appropriate, it would delay processing of the PHP page, so don't do that.

    In short: All responses can be discarded.

Ideas for solutions:

  • In a now deleted answer, someone suggested calling command line curl from PHP in a shell process. This seems like a good idea, only that I don't know if forking a lot of shell processes under heavy load is a wise thing to do.

  • I found php-ga, a package for doing server-side Google Analytics from PHP. On the project's page, it is mentioned: "Can be configured to [...] use non-blocking requests." So far I haven't found the time to investigate what method php-ga uses internally, but this method could be it!

In a nutshell: What is the best solution to do generic server-side tracking/analytics from PHP.

回答1:

Unfortunately PHP by definition is blocking. While this holds true for the majority of functions and operations you will normally be handling, the current scenario is different.

The process which I like to call HTTP-Ping, requires that you only touch a specific URI, forcing the specific server to boot-strap it's internal logic. Some functions allow you to achieve something very similar to this HTTP-ping, by not waiting for a response.

Take note that the process of pinging an url, is a two step process:

  1. Resolve the DNS
  2. Making the request

While making the request should be rather fast once the DNS is resolved and the connection is made, there aren't many ways of making the DNS resolve faster.

Some ways of doing an http-ping are:

  1. cURL, by setting CONNECTION_TIMEOUT to a low value
  2. fsockopen by closing immediately after writing
  3. stream_socket_client (same as fsockopen) and also adding STREAM_CLIENT_ASYNC_CONNECT

While both cURL and fsockopen are both blocking while the DNS is being resolved. I have noticed that fsockopen is significantly faster, even in worst case scenarios.

stream_socket_client on the other hand should fix the problem regarding DNS resolving and should be the optimal solution in this scenario, but I have not managed to get it to work.

One final solution is to start another thread/process that does this for you. Making a system call for this should work, but also forking the current process should do that also. Unfortunately both are not really safe in applications where you can't control the environment on which PHP is running.

System calls are more often than not blocked and pcntl is not enabled by default.



回答2:

I would call tracker.example.com this way:

get_headers('http://tracker.example.com?product_number=5230&price=123.52');

and in the tracker script:

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;


回答3:

I implemented function for fast GET request to url without waiting for response:

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);
}


回答4:

You can use shell_exec, and command line curl.

For an example, see this question



回答5:

Came here whilst researching a similar problem. If you have a database connection handy, one other possibility is to quickly stuff the request details into a table, and then have a seperate cron-based process that periodically scans that table for new records to process, and makes the tracking request, freeing up your web application from having to make the HTTP request itself.



回答6:

<?php
// Create a stream
$opts = array(
  'http'=>array(
    'method'=>"GET",
    'header'=>"Accept-language: en" 
  )
);

 $context = stream_context_create($opts);

// Open the file using the HTTP headers set above
$file = file_get_contents('http://tracker.example.com?product_number=5230&price=123.52', false, $context);
?>


回答7:

I needed to do something similar, just ping a url and discard all responses. I used the proc_open command which lets you end the process right away using proc_close. I'm assuming you have lynx installed on your server:

<?php    
function ping($url) {
      $proc = proc_open("lynx $url",[],$pipes);
      proc_close($proc);
    }
?>


回答8:

You can actually do this using CURL directly.

I have both implemented it using a very short timeout (CURLOPT_TIMEOUT_MS) and/or using curl_multi_exec.

Be advised: eventually i quit this method because not every request was correctly made. This could have been caused by my own server though i haven't been able to rule out the option of curl failing.