在PHP中,有没有一种方法可以发出HTTP请求而不等待响应? 我不关心响应,我只想做一些类似于file_get_contents()
的事情,但在执行我的代码的其余部分之前不等待请求完成。这对于在我的应用程序中设置“事件”或启动长时间的进程非常有用。
有什么想法吗?
在PHP中,有没有一种方法可以发出HTTP请求而不等待响应? 我不关心响应,我只想做一些类似于file_get_contents()
的事情,但在执行我的代码的其余部分之前不等待请求完成。这对于在我的应用程序中设置“事件”或启动长时间的进程非常有用。
有什么想法吗?
我之前接受的答案没有起作用。它仍然等待响应。但是这个方法有效,取自如何在PHP中进行异步GET请求?
function post_without_wait($url, $params)
{
foreach ($params as $key => &$val) {
if (is_array($val)) $val = implode(',', $val);
$post_params[] = $key.'='.urlencode($val);
}
$post_string = implode('&', $post_params);
$parts=parse_url($url);
$fp = fsockopen($parts['host'],
isset($parts['port'])?$parts['port']:80,
$errno, $errstr, 30);
$out = "POST ".$parts['path']." HTTP/1.1\r\n";
$out.= "Host: ".$parts['host']."\r\n";
$out.= "Content-Type: application/x-www-form-urlencoded\r\n";
$out.= "Content-Length: ".strlen($post_string)."\r\n";
$out.= "Connection: Close\r\n\r\n";
if (isset($post_string)) $out.= $post_string;
fwrite($fp, $out);
fclose($fp);
}
while(ob_get_level()) ob_end_clean();
header('Connection: close');
ignore_user_abort();
ob_start();
echo('Connection Closed');
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush();
flush();
这段代码是从PHP手册的用户贡献注释中复制而来,并进行了一些改进。
while(true);
。页面将会挂起,这意味着它仍在前台运行。 - Sos.你可以使用exec()来调用可以进行HTTP请求的工具,比如wget,但你必须将程序的所有输出都重定向到某个地方,比如一个文件或者/dev/null,否则PHP进程会等待该输出。
如果你想要完全将进程与Apache线程分离,请尝试类似以下方式(我不确定,但希望你能理解):
exec('bash -c "wget -O (url goes here) > /dev/null 2>&1 &"');
这不是一项好的业务,你可能需要类似于cron job调用心跳脚本来轮询一个实际的数据库事件队列以执行真正的异步事件。
exec("curl $url > /dev/null 2>&1 &");
是这里最快的解决方案之一。它比上面“已接受”的答案中的post_without_wait()
函数(14.8秒)极快(100次迭代只需1.9秒)。而且这是一个单行命令... - rinogoexec()
不是被禁用了吗? - RedGuy11<?php
$request = new cURL\Request('http://yahoo.com/');
$request->getOptions()->set(CURLOPT_RETURNTRANSFER, true);
// Specify function to be called when your request is complete
$request->addListener('complete', function (cURL\Event $event) {
$response = $event->response;
$httpCode = $response->getInfo(CURLINFO_HTTP_CODE);
$html = $response->getContent();
echo "\nDone.\n";
});
// Loop below will run as long as request is processed
$timeStart = microtime(true);
while ($request->socketPerform()) {
printf("Running time: %dms \r", (microtime(true) - $timeStart)*1000);
// Here you can do anything else, while your request is in progress
}
以下是上述示例的控制台输出。它将显示一个简单的实时时钟,指示请求运行了多长时间:
exec()
比接受的答案更好呢? - Boolean_Typeuse GuzzleHttp\Client;
use GuzzleHttp\Promise;
$client = new Client(['base_uri' => 'http://httpbin.org/']);
// Initiate each request but do not block
$promises = [
'image' => $client->getAsync('/image'),
'png' => $client->getAsync('/image/png'),
'jpeg' => $client->getAsync('/image/jpeg'),
'webp' => $client->getAsync('/image/webp')
];
// Wait on all of the requests to complete. Throws a ConnectException
// if any of the requests fail
$results = Promise\unwrap($promises);
// Wait for the requests to complete, even if some of them fail
$results = Promise\settle($promises)->wait();
// You can access each result using the key provided to the unwrap
// function.
echo $results['image']['value']->getHeader('Content-Length')[0]
echo $results['png']['value']->getHeader('Content-Length')[0]
请查看http://docs.guzzlephp.org/en/stable/quickstart.html#concurrent-requests
/**
* Asynchronously execute/include a PHP file. Does not record the output of the file anywhere.
*
* @param string $filename file to execute, relative to calling script
* @param string $options (optional) arguments to pass to file via the command line
*/
function asyncInclude($filename, $options = '') {
exec("/path/to/php -f {$filename} {$options} >> /dev/null &");
}
&
? - philfreoexec()
不是被禁用了吗? - RedGuy11使用 CURL
设置较低的 CURLOPT_TIMEOUT_MS
来模拟请求的中止。
设置 ignore_user_abort(true)
以在连接关闭后继续处理。
使用这种方法,无需通过头文件和缓冲区实现连接处理,过于依赖于操作系统、浏览器和 PHP 版本。
主进程
function async_curl($background_process=''){
//-------------get curl contents----------------
$ch = curl_init($background_process);
curl_setopt_array($ch, array(
CURLOPT_HEADER => 0,
CURLOPT_RETURNTRANSFER =>true,
CURLOPT_NOSIGNAL => 1, //to timeout immediately if the value is < 1000 ms
CURLOPT_TIMEOUT_MS => 50, //The maximum number of mseconds to allow cURL functions to execute
CURLOPT_VERBOSE => 1,
CURLOPT_HEADER => 1
));
$out = curl_exec($ch);
//-------------parse curl contents----------------
//$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
//$header = substr($out, 0, $header_size);
//$body = substr($out, $header_size);
curl_close($ch);
return true;
}
async_curl('http://example.com/background_process_1.php');
后台进程
ignore_user_abort(true);
//do something...
NB
如果您想让cURL在小于一秒钟的时间内超时,可以使用CURLOPT_TIMEOUT_MS属性,但是在“类Unix系统”上有一个错误/“特性”,如果值小于1000毫秒,则导致libcurl立即超时并显示错误“cURL Error(28):Timeout was reached”。造成这种行为的解释如下:
[...]
解决方案是使用CURLOPT_NOSIGNAL禁用信号。
相关资源
CURLOPT_TIMEOUT_MS
,同时还使用CURLOPT_NOSIGNAL
来避免已知的错误https://www.php.net/manual/en/function.curl-setopt.php#104597。 - undefined您可以使用非阻塞套接字和PHP的某个pecl扩展:
您可以使用提供抽象层的库,将您的代码与pecl扩展分离:https://github.com/reactphp/event-loop
您也可以使用基于前一个库的异步HTTP客户端:https://github.com/reactphp/http-client
查看ReactPHP的其他库:http://reactphp.org
异步模型需要小心。 我建议观看此YouTube视频:http://www.youtube.com/watch?v=MWNcItWuKpI
swoole扩展。 https://github.com/matyhtf/swoole 是PHP的异步和并发网络框架。
$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);
$client->on("connect", function($cli) {
$cli->send("hello world\n");
});
$client->on("receive", function($cli, $data){
echo "Receive: $data\n";
});
$client->on("error", function($cli){
echo "connect fail\n";
});
$client->on("close", function($cli){
echo "close\n";
});
$client->connect('127.0.0.1', 9501, 0.5);
让我来展示我的方法 :)
需要在服务器上安装nodejs
(我的服务器发送1000个https get请求只需2秒钟)
url.php:
<?
$urls = array_fill(0, 100, 'http://google.com/blank.html');
function execinbackground($cmd) {
if (substr(php_uname(), 0, 7) == "Windows"){
pclose(popen("start /B ". $cmd, "r"));
}
else {
exec($cmd . " > /dev/null &");
}
}
fwite(fopen("urls.txt","w"),implode("\n",$urls);
execinbackground("nodejs urlscript.js urls.txt");
// { do your work while get requests being executed.. }
?>
urlscript.js >
var https = require('https');
var url = require('url');
var http = require('http');
var fs = require('fs');
var dosya = process.argv[2];
var logdosya = 'log.txt';
var count=0;
http.globalAgent.maxSockets = 300;
https.globalAgent.maxSockets = 300;
setTimeout(timeout,100000); // maximum execution time (in ms)
function trim(string) {
return string.replace(/^\s*|\s*$/g, '')
}
fs.readFile(process.argv[2], 'utf8', function (err, data) {
if (err) {
throw err;
}
parcala(data);
});
function parcala(data) {
var data = data.split("\n");
count=''+data.length+'-'+data[1];
data.forEach(function (d) {
req(trim(d));
});
/*
fs.unlink(dosya, function d() {
console.log('<%s> file deleted', dosya);
});
*/
}
function req(link) {
var linkinfo = url.parse(link);
if (linkinfo.protocol == 'https:') {
var options = {
host: linkinfo.host,
port: 443,
path: linkinfo.path,
method: 'GET'
};
https.get(options, function(res) {res.on('data', function(d) {});}).on('error', function(e) {console.error(e);});
} else {
var options = {
host: linkinfo.host,
port: 80,
path: linkinfo.path,
method: 'GET'
};
http.get(options, function(res) {res.on('data', function(d) {});}).on('error', function(e) {console.error(e);});
}
}
process.on('exit', onExit);
function onExit() {
log();
}
function timeout()
{
console.log("i am too far gone");process.exit();
}
function log()
{
var fd = fs.openSync(logdosya, 'a+');
fs.writeSync(fd, dosya + '-'+count+'\n');
fs.closeSync(fd);
}
exec()
吗?此外,我需要一个纯 PHP 的解决方案。 - RedGuy11