前言
最近小编在开发过程中,遇到一些处理操作,并不需要实时返回结果,怎么绕过等待?这个时候我们需要用到异步进行请求,长话短说。
一、ajax和img标签
如果是web服务器返回html代码给客户端,我们可以使用一些特殊方法来实现所谓的异步,就是在返回给客户端的HTML代码中,嵌入ajax调用,或者,嵌入一个img标签,src指向要执行的耗时脚本(还有一些类似script标签)。如果客户端不是html代码,其实我们也可以拼接出完整的图片链接(该链接包含处理,然后跳转资源到图片),然后客户端进行展示。
二、使用popen()
resource popen ( string command, string mode );
//打开一个指向进程的管道,该进程由派生给定的 command 命令执行而产生。所以可以通过调用它,但忽略它的输出。
pclose(popen("/home/xinchen/backend.php &", 'r'));
这个方法避免了第一个方法的缺点,并且也很快。但是问题是,这种方法不能通过HTTP协议请求另外的一个WebService,只能执行本地的脚本文件。并且只能单向打开,无法穿大量参数给被调用脚本。
并且如果,访问量很高的时候,会产生大量的进程。如果使用到了外部资源,还要自己考虑竞争。
三、使用php的curl扩展
- curl实现方式的本质是, 设置超时时间为1秒或者n毫秒;
- 注意: 如果设置超时时间为毫秒,那么要确认,CURL版本>=7.16.2, PHP 版本>=5.2.3.
/**
* 使用curl方式发送异步请求, put方式
*/
public function _curl($url,$params) {
$ch = curl_init();
$headers = array("Content-type: application/json;charset='utf-8'",
"Accept: application/json",
"Cache-Control: no-cache","Pragma: no-cache");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST,"PUT"); //设置请求方式
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);//设置提交的字符串
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); //设置头信息
curl_setopt($ch, CURLOPT_URL, $url); // 要访问的地址
curl_setopt($ch, CURLOPT_RETURNTRANSFER , 1 ); //获取的信息以文件流的形式返回
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //不进行ssl验证
curl_setopt($ch, CURLOPT_AUTOREFERER, 1); // 自动设置Referer
//设置超时时间为1秒,超过1秒则关闭连接
curl_setopt($ch,CURLOPT_TIMEOUT,1);
//curl_setopt($ch, CURLOPT_NOSIGNAL, 1); //注意,毫秒超时一定要设置这个
//curl_setopt($ch, CURLOPT_TIMEOUT_MS, 200); //超时毫秒,cURL 7.16.2中被加入。从PHP 5.2.3起可使用
curl_setopt($ch, CURLOPT_HEADER, 0); // 设置是否显示返回头信息 1返回 0不返回
curl_setopt($ch, CURLOPT_NOBODY, 0); //不想在输出中包含body部分,设置这个选项为一个非零值
$result = curl_exec($ch);
curl_close($ch);
return json_decode($result);
}
这种方法的缺点就是你必须要等待1秒或者多少毫秒,如果程序执行很快,岂不是白等了?
四、使用exec
使用exec执行linux命令,交给后台完成。
例:
exec("nohup curl xxxx.com &");
五、使用fsockopen
我们创建了一个基于fsockopen的函数,这个函数中利用fsockopen去访问url,但是在访问时,并不要求获取url显示的内容,而是仅仅发出访问请求,请求到达后马上关闭这个访问,缺点是要拼接header。
get方式
path不需要拼接完整url,只需要路径
<?php
$host = "xxxx.com";//主机地址,如localhost
$path = 'test/test.php';//要请求的url,如http://localhost/test/test.php
// $fp = fsockopen( 'ssl://'.$host , 443, $errno, $errstr, 30);//https请求方式
$fp = fsockopen($host , 80, $errno, $errstr, 30);
if(!$fp){
exit('请求失败');
}else{
//拼接header部分
$header = "GET $path?uid=28CSW6JO3B0N&chat_uid=2ABR5GZPRW6F&gift_id=".json_encode(['uniqid'=>2019110610157975])." / HTTP/1.1\r\n";
$header .= "Host: $host\r\n";
$header .= "Connection: Close\r\n\r\n";
fwrite($fp, $header);
// // 输出请求结果(测试时用)
// $receive = '';
// while (!feof($fp))
// {
// $receive .= ''.fgets($fp, 128);
// }
// echo "".$receive;
fclose($fp);
}
post方式
$host = "xxx.com";//主机地址,如localhost
$path = 'http://xxx.com/test/test.php';//要请求的url
$fp = fsockopen( $host , 80, $errno, $errstr, 30);
if(!$fp){
exit('请求失败');
}else{
//要发送的数据
$data['name'] = '测试';
$data['desc'] = '测试';
$post = http_build_query($data);
$len = strlen($post);
//拼接header部分
$header = "POST $path HTTP/1.1\r\n";
$header .= "Host: $host\r\n";
$header .= "Content-type: application/x-www-form-urlencoded\r\n";
$header .= "Connection: Close\r\n";
$header .= "Content-Length: $len\r\n";
$header .= "\r\n";
$header .= $post."\r\n";
fwrite($fp, $header);
// //输出请求结果(测试时用)
// $receive = '';
// while (!feof($fp))
// {
// $receive .= ''.fgets($fp, 128);
// }
// echo "".$receive;
fclose($fp);
}
这种方法是比较好的,但是有一个缺点,你使用后会发现,偶发性请求无效,nginx状态499。
参考:https://blog.csdn.net/panjiapengfly/article/details/103010517 解决上述问题。
小结:小编这里说的其实都是比较轻便的方法,如果一些要求比较高的,可以使用队列形式来完成。