通常,我们设计一个事件处理模型的程序有两种思路,一种是通过轮询的方式,一种通过事件驱动的方式,事件驱动方式也被称为消息通知方式。下面举个简单收信的例子,来说明两种实现方式的不同:
(1)传统的邮件,邮递员把它放到你家的邮箱里。因为你不知道什么时候有邮件,所以你要经常去检查邮箱,最近到底有没有邮件。这就是所谓的轮询方式,你要时常去检查,有没有发生事件发生,当你检查到有事件发生时,你采取相应措施,处理相关事件。
(2)现代的电子邮件,你不用自己去查看邮箱,如果有新邮件,电脑会给你发消息,提示你有新邮件,然后你去查看邮箱。这就是所谓的事件驱动(消息通知), 你不用去关心事件什么时候发生,当有事件发生时,会有人通知你,事件发生了,然后你再采取相应的措施,处理相关事件。
通过上面的例子,可以看出,轮询的最大的弊端在于,你要做许多无谓的检查,具体到程序中,就是会有CPU资源的浪费,换言之,就是CPU利用率不高。而事 件驱动,就很好的解决了这个问题。不过事件驱动的缺点在于,模型较为复杂,程序写起来会比较复杂,但是,一旦掌握了事件驱动这种模型的基本设计思路,必定 能达到事半功倍的效果。
在这里我要说一说,通常的网络服务器设计过程中的事件驱动模型,希望能对需要帮助的朋友一点小小的帮助,这将是我很乐意看到的。通常,高吞吐、大并发的网络服务器,都会采用事件驱动模型,其优点就是CPU利用率高,写出来的程序效率比较高。
通常,我们会用select/poll/epoll,这些由系统提供的I/O复用的API来实现事件驱动模型。网络程序中的事件,通常可以分为三类,一类 是可读事件(数据已到达内核,上层应用可以调用相关读接口进行数据读取),一类是可写事件(内核的写缓冲区有空余,上层应用可以调用相关写接口进行数据写 入),一类是异常(或出错)事件(发生了异常情况,需要进行异常处理)。select和poll, 与epoll相比,当并发数比较大的时候,性能差别会比较大,主要是因为内核通知事件的方式不同。在select和poll中,内核只是在通知有多少个 fd上发生了事件,并在相应的fd上做了标记,上层应用要想知道具体是那个fd上发生了什么事件,就必须去遍历整个fd的集合。而在epoll中,操作系 统通知事件的方式是,把有事件发生的fd集合返回给上层应用,这样就不需要再去做无谓的遍历了。尤其在并发数比较大(fd集合中fd个数多)的时候,它们 之间的性能差别还是挺大的。因此,性能要求比较高的网络服务器的实现,大多都使用epoll。
下图所示是一种比较常见的事件驱动(消息通知)模型的简单实现:
上图中,EventMonitor是用来从操作系统获取事件的线程,EventProcessor是实际来处理事件的线程。在EventMonitor中 调用select/poll/epoll_wait来获取事件,然后把发生的事件,以消息的方式通知给EventProcessor, EventProcessor收到消息后,处理所发生的事件。这里EventProcessor可以有多个,同一个fd的上事件,一般在同一个 EventProcessor上处理。这样,EventMonitor的工作就是获取事件,是比较轻量级的,重量级的运算处理工作都由 EventProcessor来处理,通过调整EventProcessor的个数,提高CPU的利用效率,从而提高整个程序的性能。
以上只是笔者是在写网络程序中的一些小小的体会,欢迎读者朋友批评指正。
转载请指明出处:http://www.wuzesheng.com/?p=660