Redis通信协议与事件处理

一,Redis通信协议

Redis是单进程单线程的。 应用系统和Redis通过Redis协议(RESP)进行交互。Redis协议位于TCP层之上,即客户端和Redis实例保持双工的连接。

  • 串行的请求响应模式

串行化是最简单模式,客户端与服务器端建立长连接 连接通过心跳机制检测(ping-pong) ack应答 客户端发送请求,服务端响应,客户端收到响应后,再发起第二个请求,服务器端再响应。这种模式下,消耗在网络协议的连接上,有问有答,性能很低。

  •  双工的请求响应模式

该模式下批量请求,平凉反应,求响应交叉进行,不会混淆(TCP双工),pipeline通过将一批命令进行打包,然后发送给服务器,服务器执行完按顺序打包返回。

通过pipeline,一次pipeline(n条命令)=一次网络时间 + n次命令时间

  •  原子化的批量请求响应模式(事务)

Redis可以利用事务机制批量执行命令。

  • 发布订阅模式(pub/sub)

发布订阅模式是:一个客户端触发,多个客户端被动接收,通过服务器中转。

  • 脚本化的批量执行(lua)

客户端向服务器端提交一个lua脚本,服务器端执行该脚本。

规范格式(redis-cli ) RESP

1、间隔符号,在Linux下是\r\n,在Windows下是\n
2、简单字符串 Simple Strings, 以 "+"加号 开头
3、错误 Errors, 以"-"减号 开头
4、整数型 Integer, 以 ":" 冒号开头
5、大字符串类型 Bulk Strings, 以 "$"美元符号开头,长度限制512M
6、数组类型 Arrays,以 "*"星号开头

实例: 

 实际发送的数据为:

二,命令处理流程 

整个流程包括:服务器启动监听、接收命令请求并解析、执行命令请求、返回命令回复等。

执行命令流程:

 

 三,事件处理机制

Redis服务器是典型的事件驱动系统, Redis将事件分为两大类:文件事件和时间事件。

一,文件事件

文件事件: Socket的读写事件,也就是IO事件(客户端的连接、命令请求、数据回复、连接断开)

Reactor :Redis事件处理机制采用单线程的Reactor模式,属于I/O多路复用的一种常见模式,的通过单个线程管理多个Socket。

Service Handler会同步的将输入的请求(Event)多路复用的分发给相应的Request Handler(处理器),所以从这个结构上看,Redis是单线程分发请求,但是处理请求上其实还是并行的!

 Reactor结构

Handle:I/O操作的基本文件句柄,在linux下就是fd(文件描述符)

Synchronous Event Demultiplexer :同步事件分离器,阻塞等待Handles中的事件发生。(系统)

Reactor: 事件分派器,负责事件的注册,删除以及对所有注册到事件分派器的事件进行监控, 当事件 发生时会调用Event Handler接口来处理事件。

Event Handler: 事件处理器接口,这里需要Concrete Event Handler来实现该接口

Concrete Event Handler:真实的事件处理器,通常都是绑定了一个handle,实现对可读事件 进行读 取或对可写事件进行写入的操作

Reactor事件处理处理时序图:

IO多路复用模型与选择

I/O多路复用就是通过一种机制,一个进程可以监视多个描述符(socket),一旦某个描述符就绪(一 般是读就绪或者写就绪),能够通知程序进行相应的读写操作。

目前Redis IO多路复用的机制有:select,poll,epoll、kqueue四种

  • select

select 函数监视的文件描述符分3类,分别是 :  writefds readfds exceptfds

select函数会阻塞,直到有描述符就绪(有数据 可读、可写、或者有except),或者超时 (timeout指定等待时间,如果立即返回设为null即可),函数返回。当select函数返回后,可以通过遍历fd表,来找到就绪的描述符。

优点: select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。

缺点:单个进程打开的文件描述是有一定限制的,它由FD_SETSIZE设置,默认值是1024,采用数组存储 另外在检查数组中是否有文件描述需要读写时,采用的是线性扫描的方法,即不管这些socket是不是活 跃的,都轮询一遍,所以效率比较低

  • poll

poll使用一个 pollfd的指针实现,pollfd结构包含了要监视的event和发生的event,不再使用select“参数值”传递的方式。

优点:采样链表的形式存储,它监听的描述符数量没有限制,可以超过select默认限制的1024大小

缺点:另外在检查链表中是否有文件描述需要读写时,采用的是线性扫描的方法,即不管这些socket是不是活跃的,都轮询一遍,所以效率比较低。

  • epoll

select和poll使用的時候都需要从系统空间复制到用户空间然后去使用,但是epoll没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次,也就是说可以直接从内核里面去取。

poll的事件注册函数,它不同于select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。

优点:

1,epoll 没有最大并发连接的限制,上限是最大可以打开文件的数目,举个例子,在1GB内存的机器上大约 是10万左右

2,效率提升, epoll 最大的优点就在于它只管你“活跃”的连接 ,而跟连接总数无关,因此在实际的网络环 境 中, epoll 的效率就会远远高于 select 和 poll 。 epoll使用了共享内存,不用做内存拷贝

  • kqueue

注册一批socket描述符到 kqueue 以后,当其中的描述符状态发生变化时, kqueue 将一次性通知应用程序哪些描述符可读、可写或出错了。 

优点: 能批量处理大量数据,性能较高

文件事件分派器

在redis中,对于文件事件的处理采用了Reactor模型。采用的是epoll的实现方式。

事件处理器

当客户端向 Redis 建立 socket时,aeEventLoop 会调用 acceptTcpHandler 处理函数,服务器会为每个链接创建一个 Client 对象,并创建相应文件事件来监听socket的可读事件,并指定事件处理函数。 

请求处理函数 

当客户端通过 socket 发送来数据后,Redis 会调用 readQueryFromClient方法,readQueryFromClient 方法会调用 read 方法从 socket 中读取数据到输入缓冲区中,然后判断其大小是否大于系统设置的 client_max_querybuf_len,如果大于,则向 Redis返回错误信息,并关闭 client。

命令回复处理器 

sendReplyToClient函数是Redis的命令回复处理器,这个处理器负责将服务器执行命令后得到的命令,回复通过套接字返回给客户端。

1、将outbuf内容写入到套接字描述符并传输到客户端 2、aeDeleteFileEvent 用于删除文件写事件(即用即删)


二,时间事件

时间事件分为定时事件周期事件

一个时间事件主要由三个属性组成: id(全局唯一id) 、when (毫秒时间戳,记录了时间事件的到达时间) 、timeProc(时间事件处理器,当时间到达时,Redis就会调用相应的处理器来处理事件)

serverCron & server.hz

  • serverCron函数用以在redis服务器对自身的资源与配置进行定期的调整,从而确保服务器的 长久运行。
  • 在redis/conf中可以配置一秒执行的次数 hz 100

定时事件 & 周期性事件

  • 定时事件:让一段程序在指定的时间之后执行一次
  • 周期性事件:让一段程序每隔指定时间就执行一次

四,时间驱动核心

aeEventLoop

整个事件驱动的核心,Redis自己的事件处理机制 它管理着文件事件表和时间事件列表, 不断地循环处理着就绪的文件事件和到期的时间事件。

生命周期

初始化 Redis 服务端在其初始化函数 initServer 中,会创建事件管理器 aeEventLoop 对象。 函数 aeCreateEventLoop 将创建一个事件管理器,主要是初始化 aeEventLoop 的各个属性值,比如 events 、 fired 、 timeEventHead 和 apidata 

stop 停止标志,1表示停止,初始化为0。

文件事件: events, fired, apidata

时间事件: timeEventHead, beforesleep, aftersleep


aeProcessEvent

aeProcessEvent用于执行时间,首先计算距离当前时间最近的时间事件,以此计算一个超时时间; 然后调用 aeApiPoll 函数去等待底层的I/O多路复用事件就绪; aeApiPoll 函数返回之后,会处理所有已经产生文件事件和已经达到的时间事件.(操作完成释放)

猜你喜欢

转载自blog.csdn.net/qq_42773863/article/details/121399228