Redis 设计与实现--事件 中有很清晰的说明。
redis 要处理的事件有两种类型:
- 时间事件:网络连接套接字。服务器与多个客户端通过网络套接字连接,当对应套接字上出现“读”或“写”需求时,对应的事件就会触发;
- 文件事件:在指定时间点运行的事件。如持续运行的服务器为了维持一个健康稳定的状态,需要定期对自身的资源和状态进行检查和整理。
时间事件记录着那些要在指定时间点运行的事件, 多个时间事件以无序链表的形式保存在服务器状态中。 每个时间事件主要由三个属性组成:
- when :以毫秒格式的 UNIX 时间戳为单位,记录了应该在什么时间点执行事件处理函数。
- timeProc :事件处理函数。
- next 指向下一个时间事件,形成链表。
根据 timeProc 函数的返回值,可以将时间事件划分为两类:
- 如果事件处理函数返回 ae.h/AE_NOMORE ,那么这个事件为单次执行事件:该事件会在指定的时间被处理一次,之后该事件就会被删除,不再执行。
- 如果事件处理函数返回一个非 AE_NOMORE 的整数值,那么这个事件为循环执行事件:该事件会在指定的时间被处理,之后它会按照事件处理函数的返回值,更新事件的 when 属性,让这个事件在之后的某个时间点再次运行,并以这种方式一直更新并运行下去。
这些常规操作主要包括:
- 更新服务器的各类统计信息,比如时间、内存占用、数据库占用情况等。
- 清理数据库中的过期键值对。
- 对不合理的数据库进行大小调整。
- 关闭和清理连接失效的客户端。
- 尝试进行 AOF 或 RDB 持久化操作。
- 如果服务器是主节点的话,对附属节点进行定期同步。
- 如果处于集群模式的话,对集群进行定期同步和连接测试。
Redis 服务器通过在多个客户端之间进行多路复用, 从而实现高效的命令请求处理: 多个客户端通过套接字连接到 Redis 服务器中, 但只有在套接字可以无阻塞地进行读或者写时, 服务器才会和这些客户端进行交互。
Redis 将这类因为对套接字进行多路复用而产生的事件称为文件事件(file event), 文件事件可以分为读事件和写事件两类。
当一个新的客户端连接到服务器时, 服务器会给为该客户端绑定读事件, 直到客户端断开连接之后, 这个读事件才会被移除。
有两种状态:
- 等待:客户端只是连接到服务器,并没有发送命令;
- 就绪:客户端给服务端发送命令请求、并且请求已经到达(相应的套接字可以无阻塞地执行读操作)时,读事件状态更新为“就绪”。
当一个新的客户端连接到服务器
服务器只会在有命令结果要传回给客户端时, 才会为客户端关联写事件, 并且在命令结果传送完毕之后, 客户端和写事件的关联就会被移除。 也只有两种状态:
- 等待:有结果返回,但客户端还未能执行无阻塞写时;
- 就绪:有结果返回,并且能无阻塞写时。
当客户端向服务器发送命令请求, 并且请求被接受并执行之后, 服务器就需要将保存在缓存内的命令执行结果返回给客户端, 这时服务器就会为客户端关联写事件。
读事件只有在客户端断开和服务器的连接时,才会被移除。这也就是说,当客户端关联写事件的时候,实际上它在同时关联读/写两种事件。因为在同一次文件事件处理器的调用中, 单个客户端只能执行其中一种事件(要么读,要么写,但不能又读又写), 当出现读事件和写事件同时就绪的情况时,事件处理器优先处理读事件————也就是说, 当服务器有命令结果要返回客户端, 而客户端又有新命令请求进入时, 服务器先处理新命令请求。
- 为Server端的接口(TCP Socket,Unix Socket,管道)客户端连接的可读事件(在server.c的initServer()函数中)
- 为各个客户端连接的Socket添加读/写事件(在networking.c中)
- AOF的管道(Pipe)添加读/写事件(在aof.c中)
- Cluster集群连接的读/写事件(在cluster.c中)
- 主从复制连接的读/写事件(在replication.c中)
- Redis哨兵模式连接的读/写事件(在sentinel.c中)