29

我正在编写一个简单的 REST 服务,它响应来自客户端的请求。全部在 PHP 中。

我担心的是,当我的服务器响应请求时,如果客户端发回“ok”响应的速度太慢,它最终可能会占用资源。

如何通过 lib_curl 发送 POST 请求,将其设置为不等待任何响应,而是在发送 POST 数据后立即退出?

这甚至可能吗?谢谢 !

4

8 回答 8

24

您不能只发送数据而没有收到 HTTP 的答复。HTTP 总是请求 -> 响应。即使响应非常短(例如没有文本的简单 200),也需要响应。每个 HTTP 套接字都将等待该响应。

如果您不关心响应,则可以向发出请求的服务器添加一个进程,然后将请求数据送给它(例如在后台运行的服务,检查请求数据库,并且始终每当添加新条目时启动请求)。这样,您将异步发出请求,并在将该请求添加到堆栈后立即退出。

同样正如 meouw 所说,客户端不是您使用 php 进行的任何通信的一部分。php 是一种服务器端语言,因此当客户端请求一个网页(php 文件)时,服务器会执行该文件(并执行所有请求 php 文件状态),然后将结果返回给客户端。

于 2010-02-03T09:40:52.943 回答
13

此解决方案适用于软件最小接收包以继续脚本。如果您不想关心响应并有权访问 exec 而不是使用 exec 并在后台调用脚本。第一个接收文件:

接收器.php

ignore_user_abort(true); //continue script if connetions become close by webbrowser(client) within working script

ob_end_clean(); // this 4 lines just extra sending to web about close connect it just in case
header("Connection: close\r\n"); //send to website close connect 
header("Content-Encoding: none\r\n"); 
header("Content-Length: 1"); //

fastcgi_finish_request(); //close nginx,apache connect to php-fpm (php working but nginx or apache stop communication with php)
//continue scripting 
// ...DO HERE WHAT YOU WANT ...
//check test with your mongo or mysql to sure php still keep connection with db

PHP 请求 HTTP 的 FRONTGROUND: 此解决方案比后台更好,您只需等待 1ms

发件人.php:

 curl_setopt($curl, CURLOPT_TIMEOUT_MS, 1); //HERE MAGIC (We wait only 1ms on connection) Script waiting but (processing of send package to $curl is continue up to successful) so after 1ms we continue scripting and in background php continue already package to destiny. This is like apple on tree, we cut and go, but apple still fallow to destiny but we don't care what happened when fall down :) 
 curl_setopt($curl, CURLOPT_NOSIGNAL, 1); // i'dont know just it works together read manual ;)

---------检查下一个答案以完成解决方案------------

背景 通过服务器请求 HTTP:这将在后台执行 $cmd(没有 cmd 窗口),而无需 PHP 等待它完成,在 Windows 和 Unix 上。@source https://www.php.net/manual/en/function.exec.php

<?php
function execInBackground($cmd) {
    if (substr(php_uname(), 0, 7) == "Windows"){
        pclose(popen("start /B ". $cmd, "r")); 
    }
    else {
        exec($cmd . " > /dev/null &");  
    }
}
?>
于 2012-06-05T10:23:49.107 回答
11

如果您真的不关心响应,则最好exec使用 wget 命令。在传递一些其他答案时提到了这一点,但这是通过这种方法发送_POST包的一个超级简单的函数(它是异步的,需要 1-2 毫秒):

function wget_request($url, $post_array, $check_ssl=true) {

  $cmd = "curl -X POST -H 'Content-Type: application/json'";
  $cmd.= " -d '" . json_encode($post_array) . "' '" . $url . "'";

  if (!$check_ssl){
    $cmd.= "'  --insecure"; // this can speed things up, though it's not secure
  }
  $cmd .= " > /dev/null 2>&1 &"; //just dismiss the response

  exec($cmd, $output, $exit);
  return $exit == 0;
}

致谢:函数改编自 https://segment.com/blog/how-to-make-async-requests-in-php/

于 2016-09-28T16:35:36.603 回答
2

http://curl.haxx.se/mail/lib-2002-05/0090.html

libcurl 没有异步接口。您可以通过使用线程或使用 libcurl 提供的非阻塞“多接口”来自己执行此操作。在此处阅读多界面:

http://curl.haxx.se/libcurl/c/libcurl-multi.html

多接口的PHP示例在这里:

http://www.phpied.com/simultaneuos-http-requests-in-php-with-curl/

于 2010-02-03T09:37:33.630 回答
1

我从未尝试过,但将 设置 CURLOPT_TIMEOUT为非常低的值可能会奏效。尝试00.1

但是,我不知道 cURL 和客户端对此会如何表现,是否会在连接已经建立时主动取消连接,以及达到超时。你必须尝试一下。如果您正在调用 PHP 脚本,也许ignore_user_abort()可以确保您的脚本通过任何一种方式运行。

于 2010-02-03T09:30:43.130 回答
1

如果您有 2 个 PHP 服务器相互通信,例如服务器 1想要向服务器 2发送 JSON 数据,则服务器 2正在做一些繁重的工作,并在收到数据后立即终止连接,因此服务器 1没有等待结果。你可以这样做:

服务器 1(客户端使用 JSON 数据创建 POST 请求):

使用 CURL,不要使用file_get_contents(),因为根据我的经验,file_get_contents()不能正确处理Connection: close HTTP 标头,并且不会按应有的方式终止连接。

    $curl = curl_init('http://server2.com/');
    curl_setopt($curl, CURLOPT_HEADER, false);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_HTTPHEADER, ["Content-type: application/json"]);
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode(['some data']));

    $response = curl_exec($curl);
    $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);

    if ($status !== 200) {
        exit("Failed with status {$status}, response {$response}, curl_error " . curl_error($curl) . ", curl_errno " . curl_errno($curl));
    }
    curl_close($curl);

    echo $response;

服务器 2:

使用了来自bubba-h57 的修改代码。

// Cause we are clever and don't want the rest of the script to be bound by a timeout.
// Set to zero so no time limit is imposed from here on out.
set_time_limit(0);
// Client disconnect should NOT abort our script execution
ignore_user_abort(true);

// Clean (erase) the output buffer and turn off output buffering
// in case there was anything up in there to begin with.
ob_end_clean();
// Turn on output buffering, because ... we just turned it off ...
// if it was on.
ob_start();
echo 'I received the data, closing connection now, bye.';
// Return the length of the output buffer
$size = ob_get_length();
// Send headers to tell the browser to close the connection
// Remember, the headers must be called prior to any actual
// input being sent via our flush(es) below.
header("Connection: close");
// Hack how to turn off mod deflate in Apache (gzip compression).
header("Content-Encoding: none");
header("Content-Length: {$size}");
// Set the HTTP response code
http_response_code(200);
// Flush (send) the output buffer and turn off output buffering
ob_end_flush();
// Flush (send) the output buffer
// This looks like overkill, but trust me. I know, you really don't need this
// unless you do need it, in which case, you will be glad you had it!
@ob_flush();
// Flush system output buffer
// I know, more over kill looking stuff, but this
// Flushes the system write buffers of PHP and whatever backend PHP is using
// (CGI, a web server, etc). This attempts to push current output all the way
// to the browser with a few caveats.
flush();

// Close current session.
session_write_close();

// Here, you can proceed with some heavy work.

echo "This won't be sent, the connection should be already closed";
于 2019-10-21T18:40:42.040 回答
0

正如其他人所说,当您发出 http 请求时,您必须等待响应。

在 PHP 中,您可以做的是使用 exec 函数发出请求。

检查此链接:php exec 命令(或类似命令)不等待结果

于 2014-07-17T10:53:06.760 回答
0

在 Laravel 中

use Illuminate\Support\Facades\Http;


...Some Code here

$prom = Http::timeout(1)->async()->post($URL_STRING, $ARRAY_DATA)->wait();

... Some more important code here

return "Request sent"; //OR whatever you need to return

这对我有用,因为我不需要总是知道响应。

于 2021-06-04T16:40:47.517 回答