谈到高性能高并发服务器的设计与开发,很关键的一模块就是高效的I/O处理了。
那么如何高效的处理I/O呢?当然是首推epoll来实现I/O复用了!
首先我们来简单的了解下epoll,有经验的工程师都知道的。epoll是目前linux操作系统上最强大的事件管理机制,也是linux操作系统上特有的I/O复用技术。
它在实现和使用上与select、poll都有很大的差异。epoll使用一组函数(epoll_create、epoll_wait、epoll_ctl)来完成任务,epoll把用户关心的文件描述符上的事件放在内核里的事件列表中,而不需要像select和poll那样每次调用都要重复传入文件描述符集或事件集(有关epoll的使用后面会稍微详细的分析一下)。
不多扯了,切入正题:
高并发服务器的框架设计,虽然种类繁多,但基本都差不多,万变不离其宗。都可分为I/O处理模块,逻辑处理模块和网络存储模块。如下图所示
对于I/O的并发处理我的思路是,主线程护着一个epoll事物处理负责监听,接受网络上的连接请求,然后把连接套接字分发给各个逻辑处理线程。
同时逻辑处理线程也维护着一个epoll事物处理,而逻辑处理线程的epoll只负责处理与连接套接字相关的接收、发送和关闭连接等事物。
这样每个业务处理线程都同时可以处理多个客户连接请求。这样一来,每个线程都可以在无锁状态下保持高效的工作。负载均衡由主线程来承担。
线程的个数可以与服务器的cpu核数相关,线程过少则无法发挥出服务器硬件的潜力。线程过多则给服务器带来了线程频繁切换的负担。
那么主线程如何把连接套接字分发给逻辑处理线程的呢?我的建议是用linux的高级IO管道进行通信,所有的事件触发都可以在epoll中来完成。
这里不建议用锁实现线程通信,个人比较排斥锁,能不用锁就尽量不用锁。
如下图所示:
从图中可以看出这个设计的优点,每个线程都维护者自己的事件循环,各自都独立的监听这自己感兴趣的事件。