很久以前,我已经构建了这个函数,在当时
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.1rn".
"Host: " . $hostName . "rn".
"Connection: Closern".
join("rn", $headers)."rnrn");
fclose($fp);
}
}
的唯一目的是从客户端请求触发一些脚本,而不减慢请求本身的速度,也不期望得到响应。然而,我今天试着使用这个功能,启动一个websocket服务器,惊讶地发现它根本不是异步的。下面这段代码应该用来启动服务器
MFSystem::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
应该保持脚本执行。
我正在寻找一种方法来保持服务器运行重定向客户端从原始请求后,不使用库和框架。
您可以在后台执行命令行PHP客户端来执行HTTP任务。如果在后台执行,则为异步。
的例子:
process.php此PHP脚本使用PHP命令行运行。
<?php
if (!isset($argv[1]))
{
die("Arguments not givenn");
}
$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.1rn" .
"Host: " . $hostName . "rn" .
"Connection: Closern" .
join("rn", $headers) . "rnrn");
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
将重定向标准输出(例如:你的echo, print等)到一个虚拟文件(写入其中的所有输出都会丢失)。
2>&1
将错误输出重定向到标准输出,写入同一个虚拟且不存在的文件。这样可以避免在apache2/error.log中写入日志。
&
在你的情况下是最重要的:它将分离你的$command执行:所以exec()将立即释放你的PHP代码执行,并创建预期的异步行为。
echo -n $!
将给出您分离执行的PID作为响应:它将由exec()返回,并使您能够使用它(例如,将此PID放入数据库并在一段时间后杀死它以避免僵尸)。
我相信你可以用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 = ReactEventLoopFactory::create();
$socket = new ReactSocketServer($loop);
$http = new ReactHttpServer($socket);
$http->on('request', $app);
$socket->listen(1337);
$loop->run();