关于php的libevent扩展的应用

php有个libevent扩展,在一年前我曾经拿它实现了一个thrift socket server,虽然我没有把它放在正式的场合来使用,但是我觉得这个扩展应该可以有更广泛的用途,比如:

  • phpDaemon — 一个异步的服务器端开发框架.
  • tail – 用php实现类似unix下的tail命令行
  • ZeroMQ + libevent in PHP – 用php和ZeroMQ实现的一个事件驱动服务器端

我所想到的一个比较实用的使用场景是,在页面中利用libevent请求多个http接口来获得数据。若是在从前,一个可行的办法是利用curl_multi_exec来同时请求好几个接口,但是这个办法需要用一个do … while循环来完成请求,很是坑爹。那么看看采用libevent的例子:

代码实例 http.php

为了省事,这个php脚本仅仅是重复抓取一个网页5次,并且回调的逻辑我没怎么做处理,仅仅是echo出来而已,可以通过下面命令行来运行这个例子:

php http.php "www.baidu.com"

代码中的http_get($argv[1])这行虽然是靠一个命令行顺序执行,但是不会阻塞后面的代码,直接就进行下一次请求了。而且我们看看回调方法部分是不是很像用javascript调用ajax写的回调方法?这都是php 5.3中闭包的功劳。


event_set($event_fd, $fd, EV_WRITE | EV_PERSIST, function($fd, $events, $arg) {
    //回调方法,后续处理随意
    echo fread($fd, 4096);
    if(feof($fd)) {
        fclose($fd);
	event_base_loopexit($arg[1]);
	echo "done";
    }
}, array($event_fd, $base_fd));

想到更多

在mysqlnd,memcached…这些php扩展中,都已经有delay回调的实现,如果能好好利用,对性能提升岂不是有莫大的帮助?或者在libevent扩展的基础上,实现一个事件驱动的开发框架,也是可行的。

Update 2011.11.10

在这个代码的基础上实现了一个异步http请求的客户端

Update 2011.10.28

event_base_loop是会阻塞后续代码执行的,所以我调整了示例代码,使用同一个event_base,并且用stream_socket_client来进行异步连接,另外在/etc/hosts指定域名的ip会对执行速度有帮助。

16 Replies to “关于php的libevent扩展的应用”

  1. 好文章。正好我最近也在看这方面的东西,所以也在这边讨论一下。

    多个http_get是否应该共用一个event_base?

    函数内定义的event是否会被GC? (我自己做过一个实验,貌似是被GC了)。

    phpDaemon里面没有涉及到worker和master之间的通信问题。我的考虑是这样的——如果语言本身不支持线程级别的操作,则master/worker模型在网络服务器的应用场景下意义不大。比如:一个network process接收数据,再把这个数据通过unix domain socket发给worker处理,最后再发回。这种方式不如直接运行于单进程情况下启动多个进程好了——本身unix domain socket就是一种开销。

    不知道你怎么看?

  2. >> 多个http_get是否应该共用一个event_base?
    共用event_base是否能区分不同的http请求,在请求完成之后,我使用了event_base_loopexit来退出循环,也会对这个产生影响。

    >> 函数内定义的event是否会被GC?
    你自己回答了,呵呵

    另外关于phpDaemon这种我没有做深入研究,现在我主要关注如何利用libevent来减少常规php网页中的阻塞问题。

  3. event_base_loop 执行到这个函数会阻塞住。所以你这个代码实际还是串行的。
    其实就是应该共用同一个 event_base 。区分请求可以通过额外参数,回调的时候会带上。

  4. 测试了一下,不仅是event_base_loop会阻塞,fsockopen也会阻塞,换成stream_socket_client加上STREAM_CLIENT_ASYNC_CONNECT才可以通过,但是这个我没有测试成功。另外event_base的确应该共用,这方面的示例代码少了点。

  5. 正想说event_base_loop会阻塞,然后就看到回复了,呵呵。

    其实可以考虑把event作为类内属性,这样可以直接把array($this, ‘method’)作为callback挂上。

  6. @Jacky 把event做为类内属性完全没问题,从phpDaemon的代码里可以看到很多类似的写法。这个例子里主要是为了方便阅读理解,直板的写了下来。

    另外我已经更新了代码,共用同一个event_base,执行时间有减少,但是有时仍有短暂的阻塞。

  7. @Volcano – 是不是有个问题:为什么在EV_WRITE回调函数里面放read?

  8. 赞,
    我们也实现了php的epoll、mmap等扩展,在这基础上实现了一套php web server,在线上运营效果还不错,期待大家一起交流,请加我qq 272637398详聊,谢谢。

  9. 我这里也用php+libevent实现了一套LBS应用的即时聊天系统的后台SOCKET部份。已经上线跑了一段时间了,目前压力也不大,还算稳定。但是有点担心后期的压力,PHP是单线程的,一个进程持有的用户太多在压力大的时候会有阻塞。所以跑了很多个进程,开了很多端口,总觉得这套方案并不是很好。今天有看到php5.3里有新出来一个eio扩展,好像提供了类似libevent的功能,资料比较少,我没大看明白,而且有看到eio_nthreads这样的函数。不知道博主对这个扩展有了解没

  10. @yangzhu 看起来不太靠谱,看的介绍文档第一行就来了这么一行警告:

    It is important to aware that each request is executed in a thread, and the order of execution of continuously queued requests basically is unpredictable. For instance, the following piece of code is incorrect.

  11. 这里还是有个蛮纠结的点。
    假设php在请求的后端需要5s的处理时间,那这5s内,本次请求会block住。这在mod_php5下,阻塞的是Apache的进程;php-fpm下,阻塞的是一个php-cgi进程,都很不好。有没有办法在等待后端返回的过程中,还可以接别的客人?

  12. 测试好像event_base_loop($base_fd, EVLOOP_NONBLOCK); 方式不行, 自己写代码测的,可以no_block然后下面的代码继续执行有输出,但是定时执行的代码没有执行,另外感觉php本身定时处理好像就有问题,php脚本执行结束就gc了,执行的代码不会留在内存中,更不就谈不上执行,除非能够单独保存callback部分,并且event执行能够有单独的进程或线程来处理,感觉才能OK

  13. I have noticed you don’t monetize ooso.net, don’t waste your traffic, you can earn extra bucks every month
    with new monetization method. This is the best adsense alternative for
    any type of website (they approve all websites), for more details simply search in gooogle:
    murgrabia’s tools

Leave a Reply

Your email address will not be published.