深入剖析:基于epoll与主从Reactor模型的高性能服务器设计与实现
一.引言
时代背景:移动互联网、物联网时代下,海量连接与高并发请求成为常态。
通信本质: 通信其实本是就是IO的过程,而对于IO而言,消耗最大的过程其实是等待的过程,假如一个对于单线程而说,如果一个链接一直不给你发消息,那么你会一直卡着对于这个链接的接收过程,
而epoll是基于事件驱动的,所以有了本文基于epoll的服务器设计。对于几种处理IO的方式我在之前的文章中介绍过可参考:C/C++ Select Poll Epoll 多路转接_c++ select epoll-CSDN博客
二.基石理论:深挖epoll与Reactor模式
Epoll底层使用的是红黑树+链表
相比Select和Poll的每次传回来所有事件,我们需要轮询所有事件达到O(N)的复杂度
而Epoll直接传回就绪事件,事件检测和通知的性能是O(1)。
Reactor模式:
一个Reactor 相当于一个一直在Loop的EventLoop
这个Reactor负责:
1.事件监听注册和事件处理注册
2.事件监听与处理
简单的一句话: 可以给Reactor注册一个需要被监听的事件,并给对应的事件就绪时注册对应的处理事件,当每次Reactor监听时,如果事件就绪就会执行对应的事件
三、架构设计:构建主从Reactor服务器骨架
1.Channel模块
本模块是对一个fd所对应事件操作的通道,简而言之,我们需要通过channel对一个fd所对应的某个事件进行操作,假如说如果需要设置某一个fd的可写事件的回调函数,我们需要通过对应的Channel来设置,假如说我们需要对该fd的可写事件进行监听,也是需要通过该组件的对应接口。
2.Buffer模块
该模块 处于 是对某个fd缓冲区 和 上层 业务层数据的 中间处设置缓冲区,作为底层数据到来和上层业务处理数据的缓冲, 上层业务通过该缓冲区的接口进行数据的放入和取出。
3.Socket模块
对Socket的进一步封装,简化操作,提供Server(create bind listen)和Client(create conncet)的各自的独特创建,提供设置是否阻塞模式,提供发送接收数据接口。
4.Acceptor
Acceptor可以看成一个特殊的Channel,负责创建监听fd的fd的事件进行管理。
管理对应的可读事件的监听以及对应回调函数的设置,
当可读事件触发意味着监听到新连接,触发对应的可读事件,以及对应的监听到新连接的事件回调。
5.Poller模块
Poller 正如命名一般,负责监听和管理监听事件。
而我们通过epoll进行监听,所以poller本质就是对epoll的封装,以及管理epoll进行监听的事件。若是我们需要对监听事件进行修改或增删,都是通过该接口进行操作。
6.TimerWheel
本模块是一个定时器模块,可以增加定时任务以及将一个任务刷新,即将一个快要结束的任务任务延后到下一个延迟时间。
7.EventLoop
EventLoop模块可以理解就是我们上边所说的Reactor模块,它是对Poller模块,TimerQueue模块, Socket模块的⼀个整体封装,进⾏所有描述符的事件监控。 EventLoop模块必然是⼀个对象对应⼀个线程的模块,线程内部的⽬的就是运⾏EventLoop的启动函 数。 EventLoop模块为了保证整个服务器的线程安全问题,因此要求使⽤者对于Connection的所有操作⼀ 定要在其对应的EventLoop线程内完成,不能在其他线程中进⾏(⽐如组件使⽤者使⽤Connection发 送数据,以及关闭连接这种操作)。
此外EventLoop采用 Poller+任务队列 的模式进行I/O事件的处理和任务的处理,为了防止Poller因为没有任务到来而阻塞,所以通过eventFd,来进行对Poller的唤醒以此来 执行任务队列。
7.Connection
Connection模块是对Buffer模块,Socket模块,Channel模块的⼀个整体封装,实现了对⼀个通信套 接字的整体的管理,每⼀个进⾏数据通信的套接字(也就是accept获取到的新连接)都会使⽤ Connection进行管理。
• Connection模块内部包含有三个由组件使用者传入的回调函数:连接建⽴完成回调,事件回调, 新数据回调,关闭回调。
• Connection模块内部包含有两个组件使用者提供的接口:数据发送接口;连接关闭接口
• Connection模块内部包含有两个用户态缓冲区:用户态接收缓冲区,用户态发送缓冲区
• Connection模块内部包含有⼀个Socket对象:完成描述符面向系统的IO操作
• Connection模块内部包含有⼀个Channel对象:完成描述符IO事件就绪的处理
四.总结
通过以上八大模块的协同工作,一个高性能、高并发的主从Reactor服务器骨架便得以建立。主Reactor负责高效接入新连接,从Reactor池负责处理已建立连接的I/O,线程池负责处理计算密集型业务,三者各司其职,共同支撑起百万级并发的服务能力