Как сделать асинхронный запрос GET в PHP? - PullRequest
93 голосов
/ 08 июня 2009

Я хочу сделать простой запрос 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 ]

50 голосов
/ 08 июня 2009

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, немедленно закрыть сокет и вернуться.

32 голосов
/ 15 октября 2010

Вот как заставить ответ маркиза работать с запросами 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);
  }
13 голосов
/ 08 июня 2009

Что касается вашего обновления, о нежелании ждать загрузки полной страницы - я думаю, что 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
6 голосов
/ 08 июня 2009

Ты не. Хотя PHP предлагает множество способов вызова URL, он не предлагает встроенной поддержки для выполнения какой-либо асинхронной / многопоточной обработки за цикл запроса / выполнения. Любой метод отправки запроса на URL-адрес (или оператор SQL, и т. Д.) Будет ожидать ответа типа некоторый . Для этого вам понадобится какая-нибудь вторичная система, работающая на локальном компьютере (Google ищет "очередь заданий php")

6 голосов
/ 25 ноября 2012

Я бы порекомендовал вам хорошо протестированную библиотеку 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
}
4 голосов
/ 20 апреля 2012
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);
}
3 голосов
/ 08 июня 2009

Интересная проблема. Я предполагаю, что вы просто хотите запустить какой-то процесс или действие на другом сервере, но не волнует, каковы будут результаты, и хотите, чтобы ваш сценарий продолжался. Возможно, в cURL есть что-то, что может сделать это, но вы можете рассмотреть возможность использования exec() для запуска другого сценария на сервере, который выполняет вызов, если cURL не может этого сделать. (Обычно люди хотят получить результаты вызова скрипта, поэтому я не уверен, что у PHP есть возможность просто запустить процесс.) С exec() вы можете запустить wget или даже другой скрипт PHP, который делает запрос с помощью file_get_conents().

3 голосов
/ 21 мая 2014

Если вы используете среду Linux, вы можете использовать команду PHP exec для вызова 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 миллисекунд.

2 голосов
/ 21 апреля 2016

Для меня вопрос об асинхронном 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 и т. Д.

2 голосов
/ 08 февраля 2012

позвольте мне показать вам мой путь:)

необходимо установить 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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...