如何在PHP中进行asynchronousGET请求?
我希望对另一台服务器上的另一个脚本进行简单的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()将等待页面加载完全等?
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请求,并立即closures套接字并返回。
这是如何使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请求在某些网站上花费很长时间”介绍了如何使用PHP / Curl来执行HEAD
请求
如果你想触发这个请求,而不是阻止这个脚本,有几种方法,各种各样的复杂性。
- 执行HTTP请求作为后台进程, php执行一个后台进程 – 基本上你会执行类似于
"wget -O /dev/null $carefully_escaped_url"
– 这将是特定于平台的,你必须非常小心转义参数命令 - 在后台执行PHP脚本 – 基本上与UNIX进程方法相同,但执行PHP脚本而不是shell命令
- 有一个“工作队列”,使用数据库(或类似beanstalkd这可能是矫枉过正)。 您将一个URL添加到队列中,后台进程或cron-job例行检查新的作业,并对该URL执行请求
你没有。 虽然PHP提供了很多方法来调用一个URL,但是它并不提供对每个请求/执行周期进行任何types的asynchronous/线程处理的支持。 任何发送URL请求(或SQL语句等)的方法都将等待某种响应。 你需要在本地机器上运行一些辅助系统来实现这一点(谷歌周围的“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); }
有趣的问题。 我猜你只是想在其他服务器上触发一些进程或动作,但不关心结果是什么,并希望脚本继续。 在cURL中可能会有这样的情况发生,但是如果cURL不能这样做,你可能要考虑使用exec()
在服务器上运行另一个脚本。 (通常人们需要脚本调用的结果,所以我不确定PHP是否有能力触发这个过程)。用exec()
你可以运行一个wget
甚至另外一个PHP脚本,用file_get_conents()
。
我会build议你testing良好的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 }
让我告诉你我的方式:)
需要在服务器上安装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); }
如果您正在使用Linux环境,那么您可以使用PHP的exec命令来调用linux curl。 这里是一个示例代码,它将构build一个asynchronousHTTP发布。
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库,它可以在不到10毫秒内完成http post。
尝试:
//Your Code here $pid = pcntl_fork(); if ($pid == -1) { die('could not fork'); } else if ($pid) { echo("Bye") } else { //Do Post Processing }
这不会作为一个Apache模块,你需要使用CGI。
我发现这个有趣的链接做asynchronous处理(获取请求)。
askapache
此外,你可以通过使用消息队列来进行asynchronous处理,比如beanstalkd。
你最好考虑使用消息队列而不是build议的方法。 我相信这将是更好的解决scheme,虽然它只需要发送一个请求多一点的工作。
下面是执行一个简单的GET请求的接受答案的改编。
有一件事要注意,如果服务器做任何url重写,这是行不通的。 您需要使用function更全的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); }
对于我来说关于asynchronousGET请求的问题是因为我遇到了需要做数百个请求的情况 , 所以在每个请求上得到并处理结果数据,每个请求都需要大量的毫秒执行 ,导致分钟(!)用简单的file_get_contents
执行。
在这种情况下,这是非常有用的评论在php.net上functionhttp://php.net/manual/en/function.curl-multi-init.php
所以,这里是我同时提出很多请求的升级和清理版本。 对我而言,这相当于“asynchronous”的方式。 可能是对某人有帮助!
// 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或其他types的HTTP(S)请求或它们的任何组合。 和Cookie支持,redirect,http-auth等
基于这个线程,我为我的codeigniter项目做了这个。 它工作得很好。 您可以在后台处理任何function。
接受asynchronous呼叫的控制器。
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( ) ); } } }
和一个库,做asynchronous调用
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 ); } }
可以调用此方法来处理“背景”中的任何model :: method()。 它使用可变参数。
$this->load->library('daemon'); $this->daemon->execute_background( 'model', 'method', $arg1, $arg2, ... );
build议:格式化FRAMESET HTML页面,其中包含9帧内容。 每个框架将获得您的myapp.php页面的不同的“实例”。 在Web服务器上将有9个不同的线程并行运行。
只是在上面张贴的脚本的一些更正。 以下是为我工作
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); }
对于PHP5.5 +, mpyw / co是最终的解决scheme。 它的工作方式就好像它是JavaScript中的tj / co 。
例
假设你想下载指定的多个GitHub用户的头像。 每个用户需要执行以下步骤。
- 获取http://github.com/mpyw(GET HTML)的内容
- find
<img class="avatar" src="...">
并请求它(GET IMAGE)
---
:等待我的回应
...
:在并行stream程中等待其他响应
许多着curl_multi
基于curl_multi
的脚本已经为我们提供了以下stream程。
/-----------GET HTML\ /--GET IMAGE.........\ / \/ \ [Start] GET HTML..............----------------GET IMAGE [Finish] \ /\ / \-----GET HTML....../ \-----GET IMAGE....../
但是,这不够有效。 你想减less毫无价值的等待时间...
?
/-----------GET HTML--GET IMAGE\ / \ [Start] GET HTML----------------GET IMAGE [Finish] \ / \-----GET HTML-----GET IMAGE.../
是的,这是很容易与mpyw / co。 有关更多详细信息,请访问存储库页面。
这里是我自己的PHP函数,当我POST任何页面的特定url….
示例: *使用我的function…
<?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"); ?>
它工作得非常快,不需要原始的tcpsockets:)