怎样设计高并发系统 epoll编程,如何实现高并发服务器开发?
epoll编程,如何实现高并发服务器开发?
在epoll事件驱动机制在linux中实现之前,我们一般选择使用select或poll等IO复用来实现并发服务程序。在大数据、高并发、集群等术语的时代,select和poll的使用越来越受限,风头已经被epoll占领。
本文将介绍epoll的实现机制,并附带说明选择和轮询。通过比较其不同的实现机制,我们才能真正理解epoll为什么能做到高并发。
选择()和轮询()IO多路复用模式
选择的缺点:
单个进程可以监控的文件描述符的数量有一个最大限制,通常是1024个。当然,该数量可以更改,但是因为select通过轮询扫描文件描述符,所以文件描述符越多,性能越差。(在linux内核头文件中,有这样的定义:#define __FD_SETSIZE 1024)对于内核/用户空间内存复制的问题,select需要复制大量的句柄数据结构,产生巨大的开销;Select返回一个包含整个句柄的数组,应用需要遍历整个数组,找出哪些句柄有事件;select的触发模式是水平触发。如果应用程序未能对就绪文件描述符执行IO操作,那么每个后续的select调用仍将通知进程这些文件描述符。与select模型相比,poll使用链表存储文件描述符,因此对被监控文件的数量没有限制,但其他三个缺点仍然存在。
以精选车型为例。假设我们的服务器需要支持100万个并发连接,如果__FD_SETSIZE为1024,我们至少需要打开1k个进程才能实现100万个并发连接。除了进程间上下文切换的时间消耗,来自内核/用户空间的大量无脑内存拷贝和数组轮询也是系统无法承受的。因此,基于select模型的服务器程序要实现10万级并发访问是一项艰巨的任务。
因此,它 s epoll 轮到我们玩了。
epoll IO复用模型的实现机制
由于epoll的实现机制与select/poll完全不同,所以上面提到的select的缺点在epoll上已经不存在了。
想象以下场景:同时有一百万个客户端通过TCP连接到一个服务器进程。在任何时刻,通常只有数百或数千个TCP连接处于活动状态(事实上,大多数情况下都是这样)。如何实现如此高的并发?
在select/poll时代,服务器进程每次都会把这100万个连接(从用户状态到回复)告诉操作系统把句柄数据结构控制到内核态),让操作系统内核查询这些套接字上是否有事件,轮询后再把句柄数据复制到用户态,这样服务器应用就可以轮询和处理已经发生的网络事件。这个过程消耗了大量的资源。因此,select/poll只能处理数千个并发连接。
epoll的设计和实现与select完全不同。Epoll适用于Linux内核中的简单文件系统(文件系统一般用什么数据结构来实现?b树)。最初的选择/轮询调用分为三个部分:
1)调用epoll_create()创建一个epoll对象(在epoll文件系统中为此句柄对象分配资源)。
2)调用epoll_ctl将这100万个连接的套接字添加到epoll对象中。
3)调用epoll_wait收集发生的事件的连接。
这样,要实现上述场景,我们只需要在流程启动时创建一个epoll对象,然后在需要时添加或删除到这个epoll对象的连接。同时,epoll_wait的效率也很高,因为调用epoll_wait时,这100万个连接的句柄数据并没有复制到操作系统中,内核也不需要遍历所有的连接。
让 让我们来看看epoll机制在Linux内核中的具体实现思路。
当一个进程调用epoll_create方法时,Linux内核会创建一个eventpoll结构,该结构中的两个成员与epoll的用法密切相关。eventpoll结构如下:
[CPP]查看纯文本结构事件轮询{.../*红黑树的根节点,此树存储所有需要监控的事件添加到epoll */ struct rb_root rbr /*双向链表存储将通过epoll _ wait */struct list _ head rd list返回给用户的事件...}每个epoll对象都有一个独立的eventpoll结构,用于存储通过epoll_ctl方法添加到epoll对象的事件。这些事件会被挂载到红黑树中,这样就可以通过红黑树高效的识别出重复添加的事件(红黑树的插入时间效率为lgn,其中n为树的高度)。
添加到epoll中的所有事件都将与设备(网卡)驱动程序建立回调关系,也就是说,当相应的事件发生时,将调用这个回调方法。这个回调方法在内核中称为ep_poll_callback,它会将发生的事件添加到。在rdlist双向链表中。
在epoll中,为每个事件创建一个epitem结构,如下所示:
[CPP]view plain copy struct EP item { structrb _ noderbn//红黑树节点struct list _ heardlink//双向链表节点struct epoll_filefd ffd //事件处理程序信息struct eventpoll * Ep //指向其所属的eventpoll对象,struct epoll _ event预期事件类型}调用epoll_wait检查是否有事件时,只需要检查eventpoll对象中的rdlist双向链表中是否有EP item元素,如果rdlist不为空,事件将被复制到用户模式,事件数将返回给用户。
epoll数据结构示意图
从上面的解释我们可以知道,epoll的效率是通过红黑树和双链表数据结构,结合回调机制实现的。
好了,解释完epoll的机理,我们就可以轻松掌握Epoll的用法了。一句话的描述就是:三部曲。
步骤1: epoll_create()系统调用。这个调用返回一个句柄,所有后续的使用都在这个句柄上被标识。
第二步:epoll_ctl()系统调用。通过此调用添加、删除和修改epoll对象感兴趣的事件。返回0表示成功,返回-1表示失败。
第三部分:epoll_wait()系统调用。通过此调用收集epoll监控中发生的事件。
最后,附上一个epoll编程实例。
[CPP]查看plain copy///Linux///2009-11-05//2013-03-22:一个使用poll的简单echo服务器修改了几个问题,1是/n格式问题,2是去掉了原代码不小心添加的ET模式//原来只是一个简单的原理图程序。通过sparking/# include SLT sys/socket . HGT # include SLT sys/epoll . HGT # include SLT inet/in . HGT # include SLT arpa/inet . HGT # include SLT fcntl . HGT # include SLTnistd . HGT # include ltstdio . HGT # include lter no . HGT # include ltostreamgt使用命名空间STD # define MAX _ EVENTS 500 struct my event _ s { int FD void(* call _ back)(int FD,int :在epoll等待列表中,0不在char buff中[128] // recv数据缓冲区int len,s _ offset long last _ active//last active time }//设置事件void EventSet(myevent_s *ev void * arg){ ev-gtfd FD ev-gt call _ back call _ back ev-gt events 0 ev-gt arg ev-gt status 0 b zero(ev-gt buff,sizeof(ev-gt buff))ev-GTS _ offset 0 ev-gt len 0 ev-gt last _ active time(NULL)}//向epoll void EventAdd(int epollFd,int events,my Event _ s * ev){ struct
如何解决网站大规模高并发访问?
优雅降级是指网站为了应对突如其来的访问高峰,主动关闭部分功能,释放部分系统资源。一种保证正常访问网站核心功能的手段。淘宝 美国年度 "双十一 "推广是突然爆发的非常规访问。淘宝 s的工程师每年都会关闭一些非核心功能,比如评估和确认收货,以保证交易功能的正常运行。
基于移动计算实现自动优雅降级是网站柔性架构的理想状态。监控系统实时监控所有服务器的运行状态,并根据监控参数判断应用访问负载。如果发现有的应用负载过高,有的应用负载过低,就会适当卸载一些低负载的应用服务器,重新安装一些高负载的应用,平衡应用负载。如果所有应用负载都很高,负载压力持续增加,会自动关闭一些不重要的功能,以保证核心功能。
版权声明:本文内容由互联网用户自发贡献,本站不承担相关法律责任.如有侵权/违法内容,本站将立刻删除。