msgbartop
PHP语言, PHP扩展, Zend引擎相关的研究,技术,新闻分享 – 左手代码 右手诗
msgbarbottom

14 Apr 08 PHP实现异步调用方法研究

浏览器和服务器之间是通过 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);
}

所以,总体来看,最好用,最简单的还是第一种方法。
最完美的应该是最后一种,但是比较复杂
如果有更好的办法,欢迎交流。


分享到:



Related Posts:

Tags:

41 Responses to “PHP实现异步调用方法研究”

  1. Lussy Asma |

    Great post.

  2. php实现异步处理 – CzRzChao |

    [...] 在鸟哥的博客中有提出4种php实现异步的方式,具体可以看PHP实现异步调用方法研究 [...]

  3. Kerja Organ Jantung |

    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/

  4. Anonymous |

    fastcgi_finish_request() 这个是否可行?

  5. porno |

    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

  6. 杨航 |

    为什么我忽略执行结果后 接收方就接收不到请求呢 麻烦解答一下 谢谢了 在线等
    /*忽略执行结果
    while (!feof($fp)) {
    echo fgets($fp, 128);
    }*/

  7. Florene |

    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.

  8. online company |

    Hi there to all, it’s in fact a good for me to go to see this web site, it consists
    of priceless Information.

  9. świeczka |

    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?

  10. วิธีทํา seo facebook |

    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!

  11. PHP实现异步调用方法研究 | 我就是我 |

    [...] 本文地址: http://www.laruence.com/2008/04/14/318.html [...]

  12. weight loss Tampa |

    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 .

  13. kacer 6 bulan |

    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

  14. Anonymous |

    可以用CURLOPT_TIMEOUT_MS 替代 CURLOPT_TIMEOUT

  15. 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.

    http://www.zhiboju.cn

  16. goedkoopste zonnepanelen |

    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.

  17. เสื้อคู่ |

    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!

  18. rumah dijual di cipaku bandung |

    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!

  19. QWang |

    可以体验下这个PHP扩展,www.swoole.com,让PHP也像node.js一样写异步回调

  20. PHP中使用fsock实现伪异步 « Geek Blog |

    [...] 《PHP实现异步调用方法研究》 [...]

  21. PHP中使用fsock实现伪异步 | 健富的博客 |

    [...] 《PHP实现异步调用方法研究》 [...]

  22. waming |

    facebook有个最新的架构叫Bigpipe或许是一个解决方案啊

  23. |

    第四种的fsockopen方法,如何实现传参呢?而且要传递的是很大的数组!

  24. bosiam |

    $out .= “Connection: Close\r\n\r\n”;
    请教,语句中的换行符,一个和两个有什么区别么?
    经测试发现,两个才能运行!

  25. PHP异步执行后台任务 | 勇气 |

    [...] 参考链接 使用fscok实现异步调用PHP PHP实现异步调用方法研究 说说php的异步请求 本条目发布于 2013 年 2 月 20 日。属于 未分类 [...]

  26. Cupenoruler |

    翻了一下文档~ 发现curl 有了毫秒级的超时设置~
    CURLOPT_CONNECTTIMEOUT_MS 这个选项
    备注: Added in cURL 7.16.2. Available since PHP 5.2.3

    我现在用的cURL 7.24 和 php5.4 ~ 暗爽~

  27. goosman.lei |

    我们现在对这种问题的处理:
    1. webserver处理必要的逻辑(需要响应给用户的), 将用户不关心结果的逻辑作为任务放入一个队列中(memcacheq)
    2. 后台使用pcntl以及自己编写的daemon扩展(暴露系统库中的daemon()库函数)实现一套任务处理系统, 从队列中读取任务, 异步执行任务.

  28. huyet |

    呵呵,下一篇就提到了

  29. huyet |

    第4种方法的话,接受端是否还要加这句:ignore_user_abort(true);

    不然请求端断开连接后,接受端的脚本就停止执行?

  30. drunkard |

    某些特殊的情况,还可以使用header(‘Location: ‘),执行用户请求后的必要逻辑即跳转到别的页面,在header之后可以继续执行其他的东西。

  31. alex |

    还有个方法,是通过数据库做个任务表保存任务
    然后做个shell轮询,执行程序完成后更新任务状态,这样不占用web server的资源

  32. 初学者 |

    咨询一个问题,用curl的话,如果backend.php是一个死循环的话,如何才能杀掉这个进程呢?
    重启apache的话可以杀掉,还有其它的方法么?

  33. 懒虫 |

    不好意思 找到错误了 和 那个/没关系,是头我写错了。

  34. 懒虫 |

    我试了第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
    请问这个是为什么??

  35. laruence |

    @TaoGOGO ignore只是针对服务端脚本的…

  36. TaoGOGO |

    ignore_user_abort也行。。。

  37. Alan |

    如果不要返回值,或者返回者放在header中,也可以参考这个
    http://cn2.php.net/manual/en/function.get-headers.php

  38. 雪候鸟 |

    哦, 呵呵
    恩, 这样生成头部应该也行.;)

  39. sunceenjoy |

    呵呵,我的意思是帮你补充一种方法啊,你看行不。

  40. 雪候鸟 |

    你想说什么呢?

    看来,我得设置回复,一定要输入名字了.呵呵

  41. Anonymous |

    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);

Leave a Reply

*