《Redis设计与实现》读书笔记(十六) ——Redis文件事件 (原创内容,转载请注明来源,谢谢)

《Redis设计与实现》读书笔记(十六) ——Redis文件事件

(原创内容,转载请注明来源,谢谢)

一、概述

redis服务器是一个事件驱动程序,服务器需要处理以下两类事件:

1)文件事件(fileevent),redis服务器与客户端通过socket连接,文件事件是对socket的抽象,服务器与客户端通信会产生文件事件,服务器通过监听文件事件产生一系列操作。

2)时间事件(timeevent),redis的部分操作需要定时执行,主要是serverCron函数,例如定时清理过期键、定时aof写入等,时间事件是服务器对此类的抽象。

二、文件事件

1、reactor模式

redis基于reactor模式开发网络事件处理器,将其称之为文件时间处理器。该处理器通过I/O多路复用,同时监听多个socket,并根据socket当前处理的任务来关联不同的事件处理器。

reactor模式如下图所示:

其是处理并发I/O比较常见的一种模式,用于同步I/O。中心思想是将所有要处理的I/O事件注册到一个中心I/O多路复用器上,同时主线程阻塞在多路复用器上。一旦有I/O事件到来或是准备就绪(区别在于多路复用器是边沿触发还是水平触发),多路复用器返回并将相应I/O事件分发到对应的处理器中。

reactor中文称为反应器,即其不是等套接字来调用,而是提前建立好,并主动去调用到来或就绪的套接字。

当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关闭(close)等操作,会产生相应的文件事件。此时,文件事件处理器(即reactor)会调用之前关联好的处理器来处理事件。

I/O多路复用的reactor模式,使得redis虽然是单线程处理,但是仍然具有高效率。

2、文件事件处理器的构成

文件事件处理器由四部分组成——套接字、I/O多路复用程序、文件事件分派器、事件处理器,如下图所示:

通常,一个服务器需要同时处理多个套接字,因此文件事件可能并发出现。I/O多路复用程序是通过监听多个套接字,并将准备好的套接字按准备好的时间顺序转发给文件时间分派器。由文件时间分派器根据具体的事件类型,分派给不同的事件处理器。

I/O多路复用程序将并发出现的多个套接字加入到队列中,以有序、同步、每次一个的方式,将事件发送给文件事件分派器,并且当事件处理完毕后,才会将下一个事件发送过去。如下图所示:

I/O多路复用程序会监听多个套接字的读(ae.h/AE_READABLE)和写事件(ae.h/AE_WRITEABLE),当套接字可读或有新的可应答的套接字出现,产生读事件;当套接字可写,产生写事件。如果同时可读可写,则会先读再写。如果没有任何事件,返回的是ae.h/AE_NONE。

3、I/O多路复用程序

redis的I/O多路复用程序是通过包装操作系统原生的如select、epoll、evport、kqueue等I/O多路复用函数库,来实现I/O多路复用。由于redis底层对每种I/O多路复用都实现了相同的api接口,因此可以根据实际情况互换。每种复用方式,在redis里面是分别保存在一个.c的文件内。

1)select

select是较早的unix多路复用方式,其提供三种流——读、写、异常,调用时需要传入感兴趣的流,select将其拷贝到内核。select会使程序阻塞,让后其轮询每个流,当某个监听的流有操作时,其调用相关函数,返回结果。

由于select会将传入的流修改,并且需要全量传入、全量返回,对于大量的请求下,效率较低。

2)epoll与kqueue

epoll是linux内核的,kqueue是BSD内核的,其原理基本一致。其是通过创建一个句柄,并注册事件函数。即其是提前将有可能的事件都先注册好,当具体事件发生时去调用,而不是select的每次发生时在注册。

4、文件事件处理器

redis针对不同的文件事件,编写了多个文件事件处理器,包括处理各个客户端连接的应答处理器、接收客户端请求的命令请求处理器、向客户端返回命令结果的命令回复处理器、主从复制情况下的复制处理器。

1)应答处理器

名称是networking.c/acceptTcpHandler,用于对连接服务器监听套接字的客户端进行应答。redis初始化的时候,会将此应答处理器和套接字的读事件联系起来。当客户端发送连接请求,就会产生读事件(AE_READABLE)。

2)命令请求处理器

名称是networking.c/readQueryFromClient,用于处理读入客户端发送过来的套接字,当应答处理器连接到套接字的时候,命令请求处理器就会将读事件与其关联起来。当客户端发送请求时,就会产生读事件,命令请求处理器读入套接字中客户端发送的命令事件。

3)命令回复处理器

名称是networking.c/sendReplyToClient,用于将服务器执行命令后得到的回复返回给客户端。当有命令要返回给客户端,redis会将写事件与命令回复处理器关联起来,当客户端准备好接受服务器的回复,就会产生写事件,引发命令处理器将相关要返回给客户端的实际写入套接字。命令发送完毕后,会解除写事件和该客户端的关联。

4)示例

下面讲述一次完整的客户端与服务器连接事件。

当redis服务器正常运作时,监听套接字的事件AE_READABLE处于监听状态,且相应处理该事件的是应答处理器。

当有客户端向redis服务器发送连接请求,会产生AE_READABLE,触发应答处理器执行。处理器会进行连接并回复客户端,并创建客户端套接字,将套接字的AE_READABLE与命令请求处理器关联。

当客户端向redis服务器发送命令,会产生AE_READABLE事件,命令请求处理器会读入套接字中的命令,并传给相关执行程序去执行。

redis服务器执行完毕命令后,将产生相应的回复,服务器会将套接字的AE_WRITEABLE与命令回复处理器关联,当客户端尝试读取回复,客户端套接字将产生AE_WRITEABLE,命令回复处理器将执行,把要返回的内容写入套接字。

整个过程如下图:

——written by linhxx 2017.09.06

原文发布于微信公众号 - 决胜机器学习(phpthinker)

原文发表时间:2017-09-06

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏我是东东强

Linux内核调优参数对比与解释

本文介绍了Linux系统性能优化点常见的内核参数含义及其调优方式,以供学习参考。

1661
来自专栏大闲人柴毛毛

了解你服务器的心情——top命令详解

top是Linux较为常用的命令,可以监控服务器的CPU、内存、进程的运行情况,话不多说,直接操作。 输入top即可启动: ? 下面我们就来逐一介绍top向我们...

36216
来自专栏犀利豆的技术空间

Redis 数据库、键过期的实现

之前的文章讲解了 Redis 的数据结构,这回就可以看看作为内存数据库,Redis 是怎么存储数据的以及键是怎么过期的。

812
来自专栏三丰SanFeng

Linux进程间通信(四) - 共享内存

共享内存的优势 采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用...

2115
来自专栏性能与架构

Redis 实现安全队列

Redis的列表数据结构可以让我们方便的实现消息队列 例如用 LPUSH(BLPUSH)把消息入队,用 RPOP(BRPOP)获取消息 绝大部分的情况下,这...

3465
来自专栏三丰SanFeng

Linux进程间通信(二) - 消息队列

消息队列 消息队列是Linux IPC中很常用的一种通信方式,它通常用来在不同进程间发送特定格式的消息数据。 消息队列和之前讨论过的管道和FIFO有很大的区别,...

2738
来自专栏不止是前端

Node下RabbitMQ的使用

43419
来自专栏Linux驱动

22.Linux-块设备驱动之框架详细分析(详解)

本节目的:     通过分析块设备驱动的框架,知道如何来写驱动 1.之前我们学的都是字符设备驱动,先来回忆一下 字符设备驱动: 当我们的应用层读写(read()...

2785
来自专栏程序员互动联盟

【高级编程】Linux read系统调用

最近一个项目做了一个模拟u盘的设备,但是在read虚拟u盘的内容时必须每次都从磁盘内读取,而不是从系统的cache中读取,由于这个问题,就查资料看了下read的...

34711
来自专栏枕边书

多线程编程 - PHP 实现

前言 前些天帮同事查一个问题,第一次接触到了 PHP 的多线程,原以为 PHP 普遍都是单线程模型,并不适合多线程领域,花些时间翻了几个多线程的项目源码之后,发...

46110

扫码关注云+社区