redis线程模型
# 文件事件处理器
Redis基于reactor模式开发了网络事件处理器,这个处理器叫做文件事件处理器,file event handler,这个文件事件处理器是单线程的,因此Redis才叫做单线程的模型,采用IO多路复用机制同时监听多个socket,根据socket上的事件来选择相应的事件处理器来处理这个事件。
文件事件处理器是单线程模式下运行的,但是通过IO多路复用机制监听了多个socket,可以实现高性能的网络通信模型,又可以跟内部的其它单线程的模块进行对接,保证了Redis内部的线程模型的简单性。
文件事件处理器的结构包含4个部分:
- 多个socket
- IO多路复用程序
- 文件事件分派器
- 事件处理器
多个socket可能并发的产生不同的操作,每个操作对应不同的文件事件,但是IO多路复用程序会监听多个socket,但是会把socket放入到一个队列中排队,每次从队列中取出一个socket给事件分派器,事件分派器把socket给对应的时间处理器。
# redis 单线程的高并发
- 纯内存操作
- 核心是采用io多路复用机制
- 单线程反而避免了多线程频繁上下文切换的问题
# IO多路复用
# 1. 阻塞IO
就像 socket 通信一样, 连接的客户端一直不发数据,那么服务端线程将会一直阻塞在 read 函数上不返回,也无法接受其他客户端连接。就会发生IO阻塞
# 2. 非阻塞 IO
恳请操作系统为我们提供一个非阻塞的 read 函数。通过轮询或者回调的方法实现
# 3. IO多路复用
doc: https://mp.weixin.qq.com/s/kebjG5UosHmXa7AKCatSrA
- 实现IO多路复用的方法:
- select : 通过遍历(系统遍历)的方式实现, select 只能监听 1024 个文件描述符. windows,linux
- poll: 通过遍历(系统遍历)的方式实现, 不再有文件描述符数量限制. linux
- epoll: 通过回调的方式实现IO多路复用, 没有文件描述符限制, 效率最高. linux
# 3.1 select
系统遍历
select 调用需要传入 fd 数组,需要拷贝一份到内核,高并发场景下这样的拷贝消耗的资源是惊人的。(可优化为不复制)
select 在内核层仍然是通过遍历的方式检查文件描述符的就绪状态,是个同步过程,只不过无系统调用切换上下文的开销。(内核层可优化为异步事件通知)
select 仅仅返回可读文件描述符的个数,具体哪个可读还是要用户自己遍历。(可优化为只返回给用户就绪的文件描述符,无需用户做无效的遍历)
- 我们可以开设一个代收网点,让快递员全部送到代收点。这个网店管理员叫select。这样我们就可以在家休息了,麻烦的事交给select就好了。当有快递的时候,select负责给我们打电话,期间在家休息睡觉就好了。
- 但select 代收员比较懒,她记不住快递员的单号,还有快递货物的数量。她只会告诉你快递到了,但是是谁到的,你需要挨个快递员问一遍。
# 3.2 poll
系统遍历
poll 也是操作系统提供的系统调用函数。
它和 select 的主要区别就是,去掉了 select 只能监听 1024 个文件描述符的限制。
# 3.2 epoll
回调(异步 IO 事件唤醒)
内核中保存一份文件描述符集合,无需用户每次都重新传入,只需告诉内核修改的部分即可。
内核不再通过轮询的方式找到就绪的文件描述符,而是通过异步 IO 事件唤醒。
内核仅会将有 IO 事件的文件描述符返回给用户,用户也无需遍历整个文件描述符集合。