- 本文地址: https://www.laruence.com/2008/04/14/318.html
- 转载请注明出处
浏览器和服务器之间是通过 HTTP 协议进行连接通讯的。这是一种基于请求和响应模型的协议。浏览器通过 URL 向服务器发起请求,Web 服务器接收到请求,执行一段程序,然后做出响应,发送相应的html代码给客户端。
这就有了一个问题,Web 服务器执行一段程序,可能几毫秒就完成,也可能几分钟都完不成。如果程序执行缓慢,用户可能没有耐心等下去,就关闭浏览器了。
而有的时候,我们更本不关心这些耗时的脚本的返回结果,但却还要等他执行完返回,才能继续下一步。
那么有没有什么办法,只是简单的触发调用这些耗时的脚本然后就继续下一步,让这些耗时的脚本在服务端慢慢执行?
经过试验,总结出来几种方法,和大家share:
1. 最简单的办法,就是在返回给客户端的HTML代码中,嵌入AJAX调用,或者,嵌入一个img标签,src指向要执行的耗时脚本。
这种方法最简单,也最快。服务器端不用做任何的调用。
但是缺点是,一般来说Ajax都应该在onLoad以后触发,也就是说,用户点开页面后,就关闭,那就不会触发我们的后台脚本了。
而使用img标签的话,这种方式不能称为严格意义上的异步执行。用户浏览器会长时间等待php脚本的执行完成,也就是用户浏览器的状态栏一直显示还在load。
当然,还可以使用其他的类似原理的方法,比如script标签等等。
2. popen()
resource popen ( string command, string mode );
//打开一个指向进程的管道,该进程由派生给定的 command 命令执行而产生。打开一个指向进程的管道,该进程由派生给定的 command 命令执行而产生。
所以可以通过调用它,但忽略它的输出。
pclose(popen("/home/xinchen/backend.php &", 'r'));
这个方法避免了第一个方法的缺点,并且也很快。但是问题是,这种方法不能通过HTTP协议请求另外的一个WebService,只能执行本地的脚本文件。并且只能单向打开,无法穿大量参数给被调用脚本。
并且如果,访问量很高的时候,会产生大量的进程。如果使用到了外部资源,还要自己考虑竞争。
3. 使用CURL
这个方法,设置CUROPT_TIMEOUT为1(最小为1,郁闷)。也就是说,客户端至少必须等待1秒钟。
$ch = curl_init(); $curl_opt = array(CURLOPT_URL, 'http://www.example.com/backend.php', CURLOPT_RETURNTRANSFER, 1, CURLOPT_TIMEOUT, 1,); curl_setopt_array($ch, $curl_opt); curl_exec($ch); curl_close($ch);
4. 使用fsockopen
这个方法应该是最完美的,但是缺点是,你需要自己拼出HTTP的header部分。
$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30); if (!$fp) { echo "$errstr ($errno)<br />\n"; } else { $out = "GET /backend.php / HTTP/1.1\r\n"; $out .= "Host: www.example.com\r\n"; $out .= "Connection: Close\r\n\r\n"; fwrite($fp, $out); /*忽略执行结果 while (!feof($fp)) { echo fgets($fp, 128); }*/ fclose($fp); }
所以,总体来看,最好用,最简单的还是第一种方法。
最完美的应该是最后一种,但是比较复杂
如果有更好的办法,欢迎交流。
[…] 1、PHP实现异步调用方法研究 | 风雪之隅 http://www.laruence.com/2008/04/14/318.html 2、PHP fsockopen 异步调用接口在nginx上偶尔实效的情况 – 张炜施 – […]
[…] PHP是世界上最好的语言,但是总被“同行们”吐槽不支持异步。其实我们要实现异步也非常简单,之前看到鸟哥的一篇写PHP异步执行的博文 PHP实现异步调用方法研究,这篇文章还是08年的,到今天PHP发展快10年了,对于异步调用也有了更多新的玩法。 […]
[…] inside. See: http://www.swoole.comOr a simpler way, but not suitable for your situation, but can refer to:http://www.laruence.com/2008/04/14/318.htmlWhich of the three ways do you think is more […]
[…] 1. PHP实现异步调用方法研究 2. […]
[…] 鸟哥PHP异步研究:http://www.laruence.com/2008/04/14/318.html 标签:php异步, 异步 […]
[…] fwrite($fp, $out); //忽略执行结果 /* while (!feof($fp)) { echo fgets($fp, 128).'<br/>'; }*/ fclose($fp); } } 更多异步方法 […]
Great post.
[…] 在鸟哥的博客中有提出4种php实现异步的方式,具体可以看PHP实现异步调用方法研究 […]
Thanks for your information, really nice article for me.
i hope you visit back and review my sites here http://obatalamipenyakitjantungkoroner7.wordpress.com/2016/12/28/cara-kerja-organ-jantung/
fastcgi_finish_request() 这个是否可行?
I savour, result in I found just what I was taking a look for.
You have ended my 4 day long hunt! God Bless you man. Have a great day.
Bye
为什么我忽略执行结果后 接收方就接收不到请求呢 麻烦解答一下 谢谢了 在线等
/*忽略执行结果
while (!feof($fp)) {
echo fgets($fp, 128);
}*/
I’ve learn several good stuff here. Certainly price bookmarking for revisiting.
I surprise how so mich effort you pput to create the srt of great informative site.
Hi there to all, it’s in fact a good for me to go to see this web site, it consists
of priceless Information.
Right now it seems like WordPress is the top blogging platform
available right now. (from what I’ve read) Is that what you
are using on your blog?
This is the right blog for everyone who wants to understand this topic.
You understand a whole lot its almost tough to argue
with you (not that I really will need to…HaHa). You definitely
put a fresh spin on a subject that has been written about for a long time.
Excellent stuff, just wonderful!
[…] 本文地址: http://www.laruence.com/2008/04/14/318.html […]
Hi my family member! I wish to say that this article
is amazing, great writtten and come with almost all important infos.
I’d like to peer extra posts like this .
This is the perfect webpage for anybody who would like
tto find out aboiut this topic. You know a whole lot its almost tough to argue with you (not that I
really would want to…HaHa). You deinitely puut a new spin on a topic that’s been discussed for years.
Wonderful stuff, just excellent!
Feel freee to surf tto my blog … kacer 6 bulan
可以用CURLOPT_TIMEOUT_MS 替代 CURLOPT_TIMEOUT
It’s in fact very difficult in this busy life to listen news on TV,
therefore I just use web for that reason, and obtain the newest
news.
http://www.zhiboju.cn
It’s in fact very difficult in this busy life to listen news on TV,
therefore I just use web for that reason, and obtain the newest
news.
I absolutely love your blog and find a lot of your post’s to be exactly I’m looking for.
Would you offer guest writers to write content for
you? I wouldn’t mind composing a post or elaborating on most of the subjects you
write with regards to here. Again, awesome site!
This is a good tip particularly to those new to the blogosphere.
Short but very precise info… Many thanks for sharing this one.
A must read post!
可以体验下这个PHP扩展,www.swoole.com,让PHP也像node.js一样写异步回调
[…] 《PHP实现异步调用方法研究》 […]
[…] 《PHP实现异步调用方法研究》 […]
facebook有个最新的架构叫Bigpipe或许是一个解决方案啊
第四种的fsockopen方法,如何实现传参呢?而且要传递的是很大的数组!
$out .= “Connection: Close\r\n\r\n”;
请教,语句中的换行符,一个和两个有什么区别么?
经测试发现,两个才能运行!
[…] 参考链接 使用fscok实现异步调用PHP PHP实现异步调用方法研究 说说php的异步请求 本条目发布于 2013 年 2 月 20 日。属于 未分类 […]
翻了一下文档~ 发现curl 有了毫秒级的超时设置~
CURLOPT_CONNECTTIMEOUT_MS 这个选项
备注: Added in cURL 7.16.2. Available since PHP 5.2.3
我现在用的cURL 7.24 和 php5.4 ~ 暗爽~
我们现在对这种问题的处理:
1. webserver处理必要的逻辑(需要响应给用户的), 将用户不关心结果的逻辑作为任务放入一个队列中(memcacheq)
2. 后台使用pcntl以及自己编写的daemon扩展(暴露系统库中的daemon()库函数)实现一套任务处理系统, 从队列中读取任务, 异步执行任务.
呵呵,下一篇就提到了
第4种方法的话,接受端是否还要加这句:ignore_user_abort(true);
不然请求端断开连接后,接受端的脚本就停止执行?
某些特殊的情况,还可以使用header(‘Location: ‘),执行用户请求后的必要逻辑即跳转到别的页面,在header之后可以继续执行其他的东西。
还有个方法,是通过数据库做个任务表保存任务
然后做个shell轮询,执行程序完成后更新任务状态,这样不占用web server的资源
咨询一个问题,用curl的话,如果backend.php是一个死循环的话,如何才能杀掉这个进程呢?
重启apache的话可以杀掉,还有其它的方法么?
不好意思 找到错误了 和 那个/没关系,是头我写错了。
我试了第4个 使用fsockopen
$out = “GET /backend.php / HTTP/1.1\r\n”;
变成
$out = “GET /backend.php HTTP/1.1\r\n”;
少了一个 /
这样就会有 HTTP/1.1 400 Bad Request 这个错误
如果加上就不会出现 400 Bad Request
请问这个是为什么??
@TaoGOGO ignore只是针对服务端脚本的…
ignore_user_abort也行。。。
如果不要返回值,或者返回者放在header中,也可以参考这个
http://cn2.php.net/manual/en/function.get-headers.php
哦, 呵呵
恩, 这样生成头部应该也行.;)
呵呵,我的意思是帮你补充一种方法啊,你看行不。
你想说什么呢?
看来,我得设置回复,一定要输入名字了.呵呵
5.
$opts = array(
‘http’=>array(‘method’=>”GET”,
‘header’=>”Accept-language: en\r\nCookie: “.$cookie.”\r\n”
)
);
$context = stream_context_create($opts);
$fp = fopen(‘http://www.example.com’, ‘r’, false, $context);
//while(!feof($fp))
//$html.=fgets($fp);
fclose($fp);