【问题标题】:Sending POST Requests without waiting for response?发送 POST 请求而不等待响应?
【发布时间】:2011-01-12 13:09:23
【问题描述】:

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

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

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

这甚至可能吗?谢谢!

【问题讨论】:

  • 发送 200 OK 响应的不是客户端,而是服务器。客户端发出请求,服务器响应,服务器不关心客户端发生了什么(根据 REST 的定义,它是无状态的)。你想做什么,你能详细说明一下吗?

标签: php libcurl


【解决方案1】:

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

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

【讨论】:

  • curl 0 超时会让它运行更长时间
  • 如果您要尝试低超时,请使用毫秒变体 curl_setopt($curl, CURLOPT_TIMEOUT_MS, 1);
【解决方案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/

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

    • 这就是我需要知道的 :) 谢谢
    • @Gotys 旁注,如果您只想发送“请求”而不等待响应,请查看 UDP 协议
    • 有火忘了“花样”。 asyc 请求将触发该请求。执行后,您无需等待即可执行其他操作。在您拥有可靠网络连接的情况下,UDP 是一个选项,但 TCP 更可靠,因为您知道数据包会成功。但是,如果不检查响应,您将不会知道发生了什么。
    • 你能引用 RFC(或其他来源)来证明 “你不能只发送数据而不收到 HTTP 的答复” 吗?从 TCP 的角度来看,可以在不等待响应的情况下发送多个 POST,因此必须在 HTTP 规范的某处明确禁止。
    • @fgrieu RFC 2616 section 1.4 中的第一段:“HTTP 协议是一个请求/响应协议” – 如果没有响应,那么你没有做正确的 HTTP ,它是一种应用协议,因此可能具有与底层 TCP 协议不同的要求。类似地,如果你只是通过网络发送一个 TCP 包而忽略协议的其余部分,那么你也不会因为那个包看起来像而自动执行 TCP。
    【解决方案4】:

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

    recevier.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
    

    FRONTGROUND 由 PHP 向 HTTP 请求: 这个解决方案比后台更好,你只需要等待 1ms

    sender.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 ;)
    

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

    BACKGROUND 通过服务器对 HTTP 的请求:这将在后台执行 $cmd(无 cmd 窗口),无需 PHP 等待它完成,在 Windows 和 Unix 上。 @源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 &");  
        }
    }
    ?>
    

    【讨论】:

    • 我找到了几个包含输出缓冲区部分的解决方案,但在我添加了这些 curl 选项之前,它仍然对我不起作用。谢谢!
    【解决方案5】:

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

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

    查看此链接:php exec command (or similar) to not wait for result

    【讨论】:

      【解决方案6】:

      如果您真的不关心响应,您最好使用exec-ing 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/

      【讨论】:

      • 谢谢。这是最好的.. 我尝试使用 pThreads 但需要我无法安装的 ZTS,我尝试过 GuzzleHttp(具有异步函数名称)但实际上是同步 .. 我可以用这个 curl 进行异步并且非常快;)
      【解决方案7】:

      如果您有 2 个 PHP 服务器相互通信,例如server 1 想要将 JSON 数据发送到 server 2server 2 正在做一些繁重的工作,并在收到数据,因此 server 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";
      

      【讨论】:

        【解决方案8】:

        在 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
        

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-01-06
          • 1970-01-01
          • 2012-12-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多