《看完这篇文章大概需要6分钟。》我们之前使用的多进程方式实现的服务端,一次创建多个工作子进程,为客户端提供服务。其实这种做法是有问题的。打个比方:如果我们之前创建的几个进程不能承载当前快速增长的业务,是否还要增加进程数量?我们都知道系统的创建过程需要消耗大量的资源,所以这会导致系统资源不足。那么有没有办法让一个进程同时服务多个客户端呢?接下来要讨论的IO多路复用技术就是对以上问题的最好回答。对于IO多路复用,我们可以通过一个例子很好的理解。(例子来自《TCP/IP网络编程》)某教室有10个学生和1个老师。这些学生会在课堂上不断提问,所以一个老师无法处理这么多问题。然后学校给每个学生分配一个老师,也就是这个班级目前有10个老师。以后只要有新的转校生,都会给这个学生配一个老师,因为转校生也喜欢提问。如果把上面例子中的学生比作客户端,那么老师就是负责数据交换的服务器。然后可以将此示例与多进程方法进行比较。有一天,一位拥有超能力的老师来了。这位老师回答问题很快,所有的问题都能处理。这位老师采用的方法是,学生提问前必须先举手,确认举手的学生后,再回答问题。目前的情况是IO多路复用。目前常用的IO多路复用模型有3种:select、poll、epoll。Selectmodel:简单的说就是将每个client连接的文件描述符,也就是sockets,放到一个集合中。调用select函数后,它会一直监视这些文件描述符中有哪些是可读的。读取描述符然后我们的工作进程读取资源。PHP中有内置函数来完成select系统调用。函数原型:intsocket_select(array&$read,array&$write,array&$except,int$tv_sec[,int$tv_usec=0])函数说明:用于判断一个或多个socket的状态,对于每一个socket,调用者可以查询其可读性、可写性和错误状态信息。参数说明:read:指向一组等待可读性检查的套接字write:指向一组等待可写性检查的套接字socketexcept:指向一组等待错误检查的套接字tv_sec:用于设置等待时间select(),secondstv_usec:用于设置select()的等待时间,这里微妙注意,如果tv_sec设置为0,socket_select立即返回,即非阻塞。如果tv_sec设置为null,socket_select将阻塞直到套接字满足条件。下面是通过代码代码的一个简单例子:poll模型:poll和select的实现很相似,本质区别在于存放fd集合的数据结构不同。select在一个进程中最多可以维护1024个连接,而poll在此基础上进行了增强,可以维护任意数量的连接。但是select和poll方法有一个很大的问题。不难看出,select就是通过循环训练的方式来判断是否可读可写。一个client发送了数据,这里还需要循环这么多次,造成资源浪费。所以后面就出现了epoll系统调用。epoll模型:epoll是select和poll的增强版。和poll一样,epoll有无限数量的文件描述符。epoll是基于内核的反射机制。当有活动的套接字时,系统会调用我们事先设置的回调函数。poll和select都是遍历。然而,epoll并不在所有情况下都比select/poll好。例如以下场景:当大部分客户端处于活跃状态时,系统会唤醒所有的回调函数,从而导致高负载。既然要处理的连接那么多,还是选择简单有效的遍历比较好。在PHP中,我们可以使用libevet扩展来实现epoll。libevent是一个用C语言编写的事件驱动的高性能网络库。支持多种I/O多路复用技术,epoll、poll、dev/poll、select和kqueue等。libevent还提供了文件描述符等事件的监听回调,信号和超时设置。所以这种编程方式也可以说是事件编程。先放代码体验一下:Server:Client:先说简单的客户端。客户端的主要功能是和服务器一样发送两句话。第一句是helloworld!,等两秒后再发送!并且每次发送后,都会收到服务器返回的字节数。在讲解服务器之前,先了解一下时间循环的一些函数:event_base_new创建一个事件库(只需要创建一次)event_new创建一个事件event_set为创建的事件设置要监听的文件描述符fd,事件类型和回调函数event_base_set会创建事件关联事件库event_add将设置的事件添加到事件监听器event_base_loop开启事件循环和event_set的几个参数:EV_TIMEOUT:超时EV_READ:只要网络缓冲区有数据,就会触发回调函数EV_WRITE:只要塞入网络缓冲区的数据被写入后,就会触发回调函数函数被触发服务端三个函数:read_cb()接受数据,发送数据error_cb()错误处理accept_cb()接受请求st并将新的文件描述符添加到事件库中,注册read_cb回调整个服务器的主进程如下:1.创建事件库2.设置事件回调3.绑定事件4.启动事件循环5.如果有符合条件的文件描述符,系统就会开始调用我们事先设置的处理函数。至于详细过程,我就不分析了。大概的流程应该是看得懂的,接下来自己巩固一下。.你必须自己做。当然,本文只是抛砖引玉,欢迎大家指出上述问题。由于作者未开启留言功能,请后台直接回复。(阿毛的Coding之路)我会继续分享一些关于编程和编程自学的文章(不限于PHP),记录自己的自学编程。同时也希望我的分享能够对一些对编程感兴趣,正在编程路上的朋友有所帮助。
