关于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会对执行速度有帮助。

15 replies on “关于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

Comments are closed.