一、什么是libevent
libevent是一个事件触发的网络库,适用于windows、linux、bsd等多种平台,内部使用select、epoll、kqueue、IOCP等系统调用管理事件机制。著名分布式缓存软件memcached也是基于libevent,而且libevent在使用上可以做到跨平台,而且根据libevent官方网站上公布的数据统计,似乎也有着非凡的性能。——引用自百度百科
需要注意以下几点:
- libevent是一个事件触发的网络库
- 可以跨平台(windows、linux、bsd等)
- 使用select、epoll(linux)、kqueue(freebsd)、IOCP(windows)等系统调用管理事件机制(IO复用)
- libevent在Linux环境下默认采用epoll作为IO多路复用方法
用户线程使用libevent则通常按以下步骤:
- 用户线程通过event_init()函数创建一个event_base对象。event_base对象管理所有注册到自己内部的IO事件。多线程环境下,event_base对象不能被多个线程共享,即一个event_base对象只能对应一个线程。
- 然后该线程通过event_add函数,将与自己感兴趣的文件描述符相关的IO事件,注册到event_base对象,同时指定事件发生时所要调用的事件处理函数(event handler)。服务器程序通常监听套接字(socket)的可读事件。比如,服务器线程注册套接字sock1的EV_READ事件,并指定event_handler1()为该事件的回调函数。libevent将IO事件封装成struct event类型对象,事件类型用EV_READ/EV_WRITE等常量标志。
- 注册完事件之后,线程调用event_base_loop进入循环监听(monitor)状态。该循环内部会调用epoll等IO复用函数进入阻塞状态,直到描述符上发生自己感兴趣的事件。此时,线程会调用事先指定的回调函数处理该事件。例如,当套接字sock1发生可读事件,即sock1的内核buff中已有可读数据时,被阻塞的线程立即返回(wake up)并调用event_handler1()函数来处理该次事件。
- 处理完这次监听获得的事件后,线程再次进入阻塞状态并监听,直到下次事件发生。
—— 引用自Memcached网络模型
二、php使用libevent扩展实现高性能服务器
此处不再赘述php-libevent扩展的安装。
上述在描述libevent步骤时候,用的并非php函数,php实现是略微有区别,大致流程不变。
在之前的文章『php-socket 实现简单服务器』中,我们使用stream+select的方式来实现一个简单的服务器。
现在我们就使用libevnet来实现更高性能的服务器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
<?php # 创建服务端 $socket = stream_socket_server ('tcp://0.0.0.0:8888', $errno, $errstr); # 设置 不阻塞 stream_set_blocking($socket, 0); # 根据之前的libevent步骤的描述 # 1、创建event_base对象 $base = event_base_new(); # 2、创建感兴趣的文件描述符相关的IO事件 $event = event_new(); # 设置自己感兴趣的事件类型,以及回调方法 event_set($event, $socket, EV_READ | EV_PERSIST, 'callback', $base); event_base_set($event, $base); # 3、将时间注册到event_base对象去 event_add($event); # 4、注册完事件之后,线程调用event_base_loop进入循环监听状态 event_base_loop($base); function callback($socket, $flag, $base) { # 回调方法中主要做接收数据,并返回数据。 $connection = stream_socket_accept($socket); //和客户端建立连接 stream_set_blocking($connection, 0); fwrite($connection, "HTTP/1.1 200 OK\r\n" . "Connection: close\r\n" . "Content-Type: text/html; charset=utf-8\r\n" . "\r\n" . "Hello World! " . date('Y-m-d H:i:s') ."\r\n\r\n" ); fclose($connection); } ?> |
附上libevent的常量
1 2 3 4 5 6 7 8 9 10 |
值 常量名 含义 1 EV_TIMEOUT 超过时间后事件成为激活状态 2 EV_READ FD就绪,可以读取的时候 ,事件成为激活状态 4 EV_WRITE FD就绪,可以写入的时候 ,事件成为激活状态 8 EV_SIGNAL 用于实现信号检测 16 EV_PERSIST 表示事件是持久的 32 EV_ET 表示底层是否支持边沿触发事件 1 EVLOOP_ONCE 如果设置了EVLOOP_ONCE,循环将等待某些事件成为激活的,执行激活的事件直到没有更多的事件可以执行,然会返回。 2 EVLOOP_NONBLOCK 如果设置了EVLOOP_NONBLOCK,循环不会等待事件被触发:循环将仅仅检测是否有事件已经就绪,可以立即触发,如果有,则执 |