以上图是redis的处理流程。
阻塞机制:
以 Get 请求为例,SimpleKV 为了处理一个 Get 请求,需要监听客户端请求(bind/listen),和客户端建立连接(accept),从 socket 中读取请求(recv),解析客户端发送请求(parse),根据请求类型读取键值数据(get),最后给客户端返回结果,即向 socket 中写回数据(send)。下图显示了这一过程,其中,bind/listen、accept、recv、parse 和 send 属于网络 IO 处理,而 get 属于键值数据操作。既然 Redis 是单线程,那么,最基本的一种实现是在一个线程中依次执行上面说的这些操作
如果客户端向redis发起一个get的请求的话 经历过的步骤:
监听客户端请求(bind/listen) -> 建立连接(accept) -> 从socket读取请求(recv) -> 转换客户端请求(parse) -> 键值数据读写(get) -> 向socket中写回数据(send)
只有get属于键值数据处理。accept和recv的都是阻塞的潜在风险点
非阻塞模式:
非阻塞模式主要体现在三个步骤上:
- socket()方法会返回主动套接字
- 调用listen()方法,可以将主动套接字转换成监听套接字
- 调用accept()方法接受客户端的连接 并且返回已连接套接字。
针对上述条件的可以优化点:
- 当redis调用accept()也就是接受客户端的连接的时候一直没有请求过来的时候,redis的线程可以返回处理其他事件
- 当redis调用recv()接受客户端的数据传输, 如果已连接套接字一直没有数据过来,redis线程同样可以处理其他操作。
但是针对上述两种情况 我们希望当有数据请求的时候或者有请求触达的时候,redis能够感知到,这样才不会导致无法感知到redis即将到达的请求
基于多路复用的高性能I/O模型
简述: 就是redis在多线程的情况下,该机制允许内核中同时存在多个监听套接字和已连接套接字。内核会一直监听这些套接字的连接请求和数据请求,一旦请求到达就会交给redis来处理
FD就是套接字
select/epoll 一旦监测到 FD 上有请求到达时,就会触发相应的事件。 这些事件会被放进一个事件队列,Redis 单线程对该事件队列不断进行处理。这样一来,Redis 无需一直轮询是否有请求实际发生,这就可以避免造成 CPU 资源浪费。同时,Redis 在对事件队列中的事件进行处理时,会调用相应的处理函数,这就实现了基于事件的回调。因为 Redis 一直在对事件队列进行处理,所以能及时响应客户端请求,提升 Redis 的响应性能