Как сделать асинхронный запрос GET в PHP?
Я хочу сделать простой запрос GET к другому сценарию на другом сервере. Как мне это сделать?
В одном случае мне просто нужно запросить внешний скрипт без необходимости какого-либо вывода.
make_request('http://www.externalsite.com/script1.php?variable=45'); //example usage
Во втором случае мне нужно получить вывод текста.
$output = make_request('http://www.externalsite.com/script2.php?variable=45');
echo $output; //string output
Честно говоря, я не хочу возиться с CURL, так как на самом деле это не работа CURL. Я также не хочу использовать http_get, так как у меня нет расширений PECL.
Будет ли fsockopen работать? Если да, то как мне это сделать, не читая содержимое файла? Нет ли другого пути?
Спасибо всем
Обновить
Я должен добавить, в первом случае, я не хочу ждать, пока скрипт ничего не вернет. Как я понимаю file_get_contents() будет ждать полной загрузки страницы и т.д.?
22 ответа
file_get_contents
будет делать то, что вы хотите
$output = file_get_contents('http://www.example.com/');
echo $output;
Редактировать: один из способов отменить запрос GET и немедленно вернуться.
Цитируется с http://petewarden.typepad.com/searchbrowser/2008/06/how-to-post-an.html
function curl_post_async($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);
}
Для этого нужно открыть сокет, запустить запрос get, немедленно закрыть сокет и вернуться.
Вот как заставить ответ Marquis работать с запросами POST и GET:
// $type must equal 'GET' or 'POST'
function curl_request_async($url, $params, $type='POST')
{
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);
// Data goes in the path for a GET request
if('GET' == $type) $parts['path'] .= '?'.$post_string;
$out = "$type ".$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";
// Data goes in the request body for a POST request
if ('POST' == $type && isset($post_string)) $out.= $post_string;
fwrite($fp, $out);
fclose($fp);
}
Что касается вашего обновления, о нежелании ждать полной страницы для загрузки - я думаю, HTTP HEAD
запрос то, что вы ищете..
get_headers должен сделать это - я думаю, что он запрашивает только заголовки, поэтому не будет отправлено полное содержимое страницы.
"PHP / Curl: запрос HEAD занимает много времени на некоторых сайтах" описывает, как сделать HEAD
запрос с использованием PHP / Curl
Если вы хотите инициировать запрос и вообще не задерживать выполнение сценария, есть несколько способов различной сложности.
- Выполните HTTP-запрос как фоновый процесс, php запустите фоновый процесс - в основном вы будете выполнять что-то вроде
"wget -O /dev/null $carefully_escaped_url"
- это будет зависеть от платформы, и вы должны быть очень осторожны при экранировании параметров в команду - Выполнение сценария PHP в фоновом режиме - в основном то же, что и метод процесса UNIX, но выполнение сценария PHP, а не команды оболочки
- Создайте "очередь заданий", используя базу данных (или что-то вроде beanstalkd, которое, вероятно, излишне). Вы добавляете URL-адрес в очередь, а фоновый процесс или cron-job регулярно проверяет наличие новых заданий и выполняет запросы к URL-адресу.
Я бы порекомендовал вам хорошо протестированную библиотеку PHP: curl-easy
<?php
$request = new cURL\Request('http://www.externalsite.com/script2.php?variable=45');
$request->getOptions()
->set(CURLOPT_TIMEOUT, 5)
->set(CURLOPT_RETURNTRANSFER, true);
// add callback when the request will be completed
$request->addListener('complete', function (cURL\Event $event) {
$response = $event->response;
$content = $response->getContent();
echo $content;
});
while ($request->socketPerform()) {
// do anything else when the request is processed
}
Вы не Хотя PHP предлагает множество способов вызова URL, он не предлагает встроенной поддержки для выполнения какой-либо асинхронной / многопоточной обработки за цикл запроса / выполнения. Любой метод отправки запроса на URL-адрес (или оператор SQL, или т. Д.) Будет ожидать какого- то ответа. Вам понадобится какая-то вторичная система, работающая на локальном компьютере, чтобы добиться этого (Google ищет "очередь заданий php")
function make_request($url, $waitResult=true){
$cmi = curl_multi_init();
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_multi_add_handle($cmi, $curl);
$running = null;
do {
curl_multi_exec($cmi, $running);
sleep(.1);
if(!$waitResult)
break;
} while ($running > 0);
curl_multi_remove_handle($cmi, $curl);
if($waitResult){
$curlInfos = curl_getinfo($curl);
if((int) $curlInfos['http_code'] == 200){
curl_multi_close($cmi);
return curl_multi_getcontent($curl);
}
}
curl_multi_close($cmi);
}
Если вы используете среду Linux, вы можете использовать команду exec PHP для вызова linux curl. Вот пример кода, который сделает асинхронный HTTP-пост.
function _async_http_post($url, $json_string) {
$run = "curl -X POST -H 'Content-Type: application/json'";
$run.= " -d '" .$json_string. "' " . "'" . $url . "'";
$run.= " > /dev/null 2>&1 &";
exec($run, $output, $exit);
return $exit == 0;
}
Этот код не требует дополнительных библиотек PHP и может завершить публикацию http менее чем за 10 миллисекунд.
Никто, кажется, не упоминает Guzzle, PHP-клиент PHP, который облегчает отправку HTTP-запросов. Может работать с или без Curl
, Он может отправлять как синхронные, так и асинхронные запросы.
$client = new GuzzleHttp\Client();
$promise = $client->requestAsync('GET', 'http://httpbin.org/get');
$promise->then(
function (ResponseInterface $res) {
echo $res->getStatusCode() . "\n";
},
function (RequestException $e) {
echo $e->getMessage() . "\n";
echo $e->getRequest()->getMethod();
}
);
Интересная проблема. Я предполагаю, что вы просто хотите запустить какой-то процесс или действие на другом сервере, но не волнует, каковы будут результаты, и хотите, чтобы ваш сценарий продолжался. Вероятно, в cURL есть что-то, что может сделать это, но вы можете рассмотреть возможность использования exec()
запустить другой скрипт на сервере, который выполняет вызов, если cURL не может этого сделать. (Обычно люди хотят получить результаты вызова скрипта, поэтому я не уверен, что в PHP есть возможность просто запустить процесс.) exec()
Вы могли бы запустить wget
или даже другой скрипт PHP, который делает запрос с file_get_conents()
,
Вам лучше рассмотреть возможность использования очереди сообщений вместо рекомендованных методов. Я уверен, что это будет лучшим решением, хотя оно требует немного больше работы, чем просто отправка запроса.
Для меня вопрос об асинхронном GET-запросе возник из-за того, что я столкнулся с ситуацией, когда мне нужно выполнять сотни запросов, получать и обрабатывать данные результатов по каждому запросу, и каждый запрос занимает значительные миллисекунды выполнения, что приводит к минутам (!) общее выполнение с простым file_get_contents
,
В этом случае это был очень полезный комментарий w_haigh на php.net о функции http://php.net/manual/en/function.curl-multi-init.php
Итак, вот моя обновленная и очищенная версия для одновременного выполнения большого количества запросов. Для моего случая это эквивалентно "асинхронному" способу. Может быть, это помогает кому-то!
// Build the multi-curl handle, adding both $ch
$mh = curl_multi_init();
// Build the individual requests, but do not execute them
$chs = [];
$chs['ID0001'] = curl_init('http://webservice.example.com/?method=say&word=Hello');
$chs['ID0002'] = curl_init('http://webservice.example.com/?method=say&word=World');
// $chs[] = ...
foreach ($chs as $ch) {
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true, // Return requested content as string
CURLOPT_HEADER => false, // Don't save returned headers to result
CURLOPT_CONNECTTIMEOUT => 10, // Max seconds wait for connect
CURLOPT_TIMEOUT => 20, // Max seconds on all of request
CURLOPT_USERAGENT => 'Robot YetAnotherRobo 1.0',
]);
// Well, with a little more of code you can use POST queries too
// Also, useful options above can be CURLOPT_SSL_VERIFYHOST => 0
// and CURLOPT_SSL_VERIFYPEER => false ...
// Add every $ch to the multi-curl handle
curl_multi_add_handle($mh, $ch);
}
// Execute all of queries simultaneously, and continue when ALL OF THEM are complete
$running = null;
do {
curl_multi_exec($mh, $running);
} while ($running);
// Close the handles
foreach ($chs as $ch) {
curl_multi_remove_handle($mh, $ch);
}
curl_multi_close($mh);
// All of our requests are done, we can now access the results
// With a help of ids we can understand what response was given
// on every concrete our request
$responses = [];
foreach ($chs as $id => $ch) {
$responses[$id] = curl_multi_getcontent($ch);
curl_close($ch);
}
unset($chs); // Finita, no more need any curls :-)
print_r($responses); // output results
Это легко переписать для обработки запросов POST или других типов HTTP(S) или любых их комбинаций. Поддержка Cookie, перенаправления, http-auth и т. Д.
Позвольте мне показать вам мой путь:)
нужен nodejs, установленный на сервере
(мой сервер отправляет 1000 запросов на получение https занимает всего 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);
}
Несколько исправлений в сценариях, опубликованных выше. Следующее работает на меня
function curl_request_async($url, $params, $type='GET')
{
$post_params = array();
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);
echo print_r($parts, TRUE);
$fp = fsockopen($parts['host'],
(isset($parts['scheme']) && $parts['scheme'] == 'https')? 443 : 80,
$errno, $errstr, 30);
$out = "$type ".$parts['path'] . (isset($parts['query']) ? '?'.$parts['query'] : '') ." 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";
// Data goes in the request body for a POST request
if ('POST' == $type && isset($post_string)) $out.= $post_string;
fwrite($fp, $out);
fclose($fp);
}
Вот адаптация принятого ответа для выполнения простого запроса GET.
Стоит отметить, что если сервер выполняет перезапись URL, это не сработает. Вам нужно будет использовать более полнофункциональный http-клиент.
/**
* Performs an async get request (doesn't wait for response)
* Note: One limitation of this approach is it will not work if server does any URL rewriting
*/
function async_get($url)
{
$parts=parse_url($url);
$fp = fsockopen($parts['host'],
isset($parts['port'])?$parts['port']:80,
$errno, $errstr, 30);
$out = "GET ".$parts['path']." HTTP/1.1\r\n";
$out.= "Host: ".$parts['host']."\r\n";
$out.= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
fclose($fp);
}
Я нашел эту интересную ссылку для асинхронной обработки (получить запрос).
Кроме того, вы можете выполнять асинхронную обработку, используя очередь сообщений, например, beanstalkd.
Пытаться:
//Your Code here
$pid = pcntl_fork();
if ($pid == -1) {
die('could not fork');
}
else if ($pid)
{
echo("Bye")
}
else
{
//Do Post Processing
}
Это НЕ будет работать как модуль Apache, вам нужно использовать CGI.
Предложение: отформатируйте страницу FRAMESET HTML, которая содержит, скажем, 9 кадров внутри. Каждый фрейм ПОЛУЧИТ отдельный "экземпляр" вашей страницы myapp.php. Параллельно на веб-сервере будет работать 9 различных потоков.
Основываясь на этой теме, я сделал это для своего проекта codeigniter. Работает просто отлично. Вы можете обработать любую функцию в фоновом режиме.
Контроллер, который принимает асинхронные вызовы.
class Daemon extends CI_Controller
{
// Remember to disable CI's csrf-checks for this controller
function index( )
{
ignore_user_abort( 1 );
try
{
if ( strcmp( $_SERVER['REMOTE_ADDR'], $_SERVER['SERVER_ADDR'] ) != 0 && !in_array( $_SERVER['REMOTE_ADDR'], $this->config->item( 'proxy_ips' ) ) )
{
log_message( "error", "Daemon called from untrusted IP-address: " . $_SERVER['REMOTE_ADDR'] );
show_404( '/daemon' );
return;
}
$this->load->library( 'encrypt' );
$params = unserialize( urldecode( $this->encrypt->decode( $_POST['data'] ) ) );
unset( $_POST );
$model = array_shift( $params );
$method = array_shift( $params );
$this->load->model( $model );
if ( call_user_func_array( array( $this->$model, $method ), $params ) === FALSE )
{
log_message( "error", "Daemon could not call: " . $model . "::" . $method . "()" );
}
}
catch(Exception $e)
{
log_message( "error", "Daemon has error: " . $e->getMessage( ) . $e->getFile( ) . $e->getLine( ) );
}
}
}
И библиотека, которая делает асинхронные вызовы
class Daemon
{
public function execute_background( /* model, method, params */ )
{
$ci = &get_instance( );
// The callback URL (its ourselves)
$parts = parse_url( $ci->config->item( 'base_url' ) . "/daemon" );
if ( strcmp( $parts['scheme'], 'https' ) == 0 )
{
$port = 443;
$host = "ssl://" . $parts['host'];
}
else
{
$port = 80;
$host = $parts['host'];
}
if ( ( $fp = fsockopen( $host, isset( $parts['port'] ) ? $parts['port'] : $port, $errno, $errstr, 30 ) ) === FALSE )
{
throw new Exception( "Internal server error: background process could not be started" );
}
$ci->load->library( 'encrypt' );
$post_string = "data=" . urlencode( $ci->encrypt->encode( serialize( func_get_args( ) ) ) );
$out = "POST " . $parts['path'] . " HTTP/1.1\r\n";
$out .= "Host: " . $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";
$out .= $post_string;
fwrite( $fp, $out );
fclose( $fp );
}
}
Этот метод может быть вызван для обработки любой модели::method() в фоновом режиме. Он использует переменные аргументы.
$this->load->library('daemon');
$this->daemon->execute_background( 'model', 'method', $arg1, $arg2, ... );
Для PHP5.5 +, mpyw / co является окончательным решением. Это работает, как будто это TJ / Co в JavaScript.
пример
Предположим, что вы хотите загрузить указанные аватары нескольких пользователей GitHub. Следующие шаги необходимы для каждого пользователя.
- Получить содержание http://github.com/mpyw (GET HTML)
- найти
<img class="avatar" src="...">
и запросить его (ПОЛУЧИТЬ ИЗОБРАЖЕНИЕ)
---
: Жду моего ответа...
: Ожидание другого ответа в параллельных потоках
Многие известные curl_multi
основанные сценарии уже предоставляют нам следующие потоки.
/-----------GET HTML\ /--GET IMAGE.........\
/ \/ \
[Start] GET HTML..............----------------GET IMAGE [Finish]
\ /\ /
\-----GET HTML....../ \-----GET IMAGE....../
Однако это недостаточно эффективно. Хотите сократить время ожидания? ...
?
/-----------GET HTML--GET IMAGE\
/ \
[Start] GET HTML----------------GET IMAGE [Finish]
\ /
\-----GET HTML-----GET IMAGE.../
Да, это очень легко с mpyw/co. Для получения более подробной информации посетите страницу хранилища.
Вот моя собственная функция PHP, когда я делаю POST для определенного URL любой страницы....
Пример: * использование моей функции...
<?php
parse_str("email=myemail@ehehehahaha.com&subject=this is just a test");
$_POST['email']=$email;
$_POST['subject']=$subject;
echo HTTP_Post("http://example.com/mail.php",$_POST);***
exit;
?>
<?php
/*********HTTP POST using FSOCKOPEN **************/
// by ArbZ
function HTTP_Post($URL,$data, $referrer="") {
// parsing the given URL
$URL_Info=parse_url($URL);
// Building referrer
if($referrer=="") // if not given use this script as referrer
$referrer=$_SERVER["SCRIPT_URI"];
// making string from $data
foreach($data as $key=>$value)
$values[]="$key=".urlencode($value);
$data_string=implode("&",$values);
// Find out which port is needed - if not given use standard (=80)
if(!isset($URL_Info["port"]))
$URL_Info["port"]=80;
// building POST-request: HTTP_HEADERs
$request.="POST ".$URL_Info["path"]." HTTP/1.1\n";
$request.="Host: ".$URL_Info["host"]."\n";
$request.="Referer: $referer\n";
$request.="Content-type: application/x-www-form-urlencoded\n";
$request.="Content-length: ".strlen($data_string)."\n";
$request.="Connection: close\n";
$request.="\n";
$request.=$data_string."\n";
$fp = fsockopen($URL_Info["host"],$URL_Info["port"]);
fputs($fp, $request);
while(!feof($fp)) {
$result .= fgets($fp, 128);
}
fclose($fp); //$eco = nl2br();
function getTextBetweenTags($string, $tagname) {
$pattern = "/<$tagname ?.*>(.*)<\/$tagname>/";
preg_match($pattern, $string, $matches);
return $matches[1]; }
//STORE THE FETCHED CONTENTS to a VARIABLE, because its way better and fast...
$str = $result;
$txt = getTextBetweenTags($str, "span"); $eco = $txt; $result = explode("&",$result);
return $result[1];
<span style=background-color:LightYellow;color:blue>".trim($_GET['em'])."</span>
</pre> ";
}
</pre>
Попробуйте этот код....
$chu = curl_init();
curl_setopt($chu, CURLOPT_URL, 'http://www.myapp.com/test.php?someprm=xyz');
curl_setopt($chu, CURLOPT_FRESH_CONNECT, true);
curl_setopt($chu, CURLOPT_TIMEOUT, 1);
curl_exec($chu);
curl_close($chu);
Пожалуйста, не забудьте включить расширение CURL php.
Это прекрасно работает для меня, к сожалению, вы не можете получить ответ на ваш запрос:
<?php
header("http://mahwebsite.net/myapp.php?var=dsafs");
?>
Работает очень быстро, нет необходимости в необработанных TCP-сокетах:)