Redis 网络 IO 处理

本文将介绍 Redis 对网络 IO 的处理方式。

一、请求处理流程

对于一个客户端请求,处理流程如下:

  • 监听客户端请求
  • 与客户端建立连接
  • 读取请求
  • 解析请求
  • 读写数据
  • 返回响应

二、请求处理方式

1. 堵塞模式

最基本也是最简单的处理方式是:在一个线程中重复建立连接、读取数据、处理请求、关闭连接。

1
2
3
4
5
6
7
8
9
listenfd = socket();   // 打开一个网络通信端口
bind(listenfd); // 绑定
listen(listenfd); // 监听
while (true) {
connfd = accept(listenfd); // 阻塞建立连接
int n = read(connfd, buf); // 阻塞读数据
doSomeThing(buf); // 利用读到的数据做些什么
close(connfd); // 关闭连接,循环等待下一个连接
}

在这种模式下,”建立连接” 和 “读取请求” 往往需要一段 “等待时间”,如果一直没有成功建立请求或者数据一直未接收到,则线程会一直堵塞,导致 Redis 无法处理其它请求。

2. 非阻塞模式

Linux 的 IO 多路复用机制允许一个线程处理多个 IO 流。

简单来说,

  • 系统会维护多个 socket,并在这些 socket 成功建立连接、成功接收请求时触发相应的事件
  • 事件会存储到事件处理队列中,并由系统进行事件出队,事件出队时,会调用相应的事件回调函数进行处理

除了 Linux 的 select/epoll 之外,还有其它 IO 多路复用机制的实现。

通过该机制,Redis 的单个线程从 “一对一” 的处理中解脱,使得它可以同时处理多个线程,不会堵塞在某个线程之上,提升了并发性。

三、多线程的引入

随着网络硬件的性能提升,单线程在处理 “网络 IO” 时出现瓶颈,单个线程处理网络请求的速度跟不上底层网络硬件的速度。

因此,Redis 6.0 版本改用多线程处理 “网络 IO”。

  • Redis 6.0 之前:单线程读取、单线程执行、单线程发送
  • Redis 6.0 之后:多线程读取、单线程执行、多线程发送

参考