PHP:发送持久异步请求

时间:2014-07-27 10:41:27

标签: php asynchronous websocket request

很久以前我已经建立了这个功能,当时看来还不错

public static function sendAsyncHTTPRequest($hostName, $port = 80, $method, $uri, $headers = array()){
    $fp = fsockopen($hostName, $port, $errno, $errstr, 30);
    if (!$fp) {
        throw new \Exception($errstr, $errno);
    } else {
        fwrite($fp, "$method $uri HTTP/1.1\r\n".
                    "Host: " . $hostName . "\r\n".
                    "Connection: Close\r\n".
                    join("\r\n", $headers)."\r\n\r\n");
        fclose($fp);
    }
}

其唯一目的是从客户端请求触发某些脚本,而不会降低请求本身的速度,也不会产生响应。但是我今天尝试使用该功能来启动websocket服务器并且令人惊讶地发现它根本不是异步的。这是应该启动服务器的代码段

\MF\System::sendAsyncHTTPRequest(SITE_DOMAIN, 80, 'GET', '/battleWS/startServer/'.$battleId);
header('Location: '.SITE_URL.'/battleWS/field/'.$battleId);

如您所见,我启动服务器,然后立即将客户端重定向到连接到服务器的页面。显然,当客户端被重定向时,服务器脚本会停止执行,这对我来说意外,因为我相信我发送的是异步请求。我可以确认这一点,因为如果我在这两行之间放置一个sleep,我就开始在日志文件中看到服务器的自动关闭倒计时。我尝试从fsockopen切换到stream_socket_client但没有运气。这也是服务器启动脚本的开始(用sendAsyncHTTPRequest()调用)

set_time_limit(0);
ignore_user_abort(true);

这让我更加困惑,因为ìgnore_user_abort应该让脚本继续执行。

我正在寻找一种在不使用库和框架的情况下从原始请求重定向客户端后保持该服务器运行的方法。

2 个答案:

答案 0 :(得分:3)

您可以在后台执行命令行PHP客户端来执行HTTP作业。如果它在后台执行,则它是异步的。

示例:

process.php 这个PHP脚本使用PHP Cli运行。

<?php

if (!isset($argv[1]))
{
   die("Arguments not given\n");
}

$args = json_decode($argv[1]);
list($hostName, $port, $method, $uri, $headers) = $args;
sendAsyncHTTPRequest($hostName, $port, $method, $uri, $headers);


function sendAsyncHTTPRequest($hostName, $port, $method, $uri, $headers = array ())
{
   $fp = fsockopen($hostName, $port, $errno, $errstr, 30);
   if (!$fp)
   {
      // as your code is asynchronous, no way to catch this exception
      throw new \Exception($errstr, $errno);
   }
   else
   {
      fwrite($fp,
         "$method $uri HTTP/1.1\r\n" .
         "Host: " . $hostName . "\r\n" .
         "Connection: Close\r\n" .
         join("\r\n", $headers) . "\r\n\r\n");
      fclose($fp);
   }
}

execute.php 此代码由apache执行(您当前执行sendAsyncHTTPRequest方法)。

$escaped_script = escapeshellarg(__DIR__ . '/process.php');
$escaped_args = escapeshellarg(json_encode(array($hostName, $port, $method, $uri, $headers)));
exec("/usr/bin/php {$escaped_script} {$escaped_args} > /dev/null 2>&1 & echo -n \$!");

一些细节:

> /dev/null

会将标准输出(即你的回声,打印等)重定向到一个虚拟文件(写在其中的所有输出都会丢失)。

2>&1

将错误输出重定向到标准输出,写入相同的虚拟和非现有文件。这样可以避免将日志记录到apache2 / error.log中。

&

是你最重要的事情:它将分离你的$ command的执行:所以exec()会立即释放你的php代码执行并创建预期的异步行为。

echo -n \$!

会将您的分离执行的PID作为响应给出:它将由exec()返回并使您能够使用它(例如,将此pid放入数据库并在一段时间后将其终止以避免僵尸)。

答案 1 :(得分:-1)

我相信你可以使用ReactPHP

做你想做的事

https://github.com/reactphp/react

例如,像这样:

<?php

$i = 0;

$app = function ($request, $response) use (&$i) {
    $i++;

    $text = "This is request number $i.\n";
    $headers = array('Location:' => 'http://domain.com');

    $response->writeHead(200, $headers);
    $response->end($text);
};

$loop = React\EventLoop\Factory::create();
$socket = new React\Socket\Server($loop);
$http = new React\Http\Server($socket);

$http->on('request', $app);

$socket->listen(1337);

$loop->run();