上篇中有关beforeSleep/afterSleep没有展开来将,这里是做补充
beforeSleep:
1.基于当前redis内存使用量,更新内存使用峰值。
2.之后根据ProcessingEventsWhileBlocked是否为true,分为2个分支。这个标记代表当前还处于数据恢复阶段。但是恢复时间比较长,会分出一小部分时间去事件循环中处理任务。(否则某些准备好的连接,或者说定时任务无法执行)
3.假设此时ProcessingEventsWhileBlocked为true
(1) 执行handleClientsWithPendingReadsUsingThreads().
大体描述下这个方法做了什么。首先每当事件循环组准备好了读取事件时会触发readQueryFromClient函数。正常情况下会读取缓冲区中的数据解析成command,以及执行command需要的参数。之后执行command。当发现开启了io线程,并且支持使用io线程解析和读取数据时,会将当前client标记成pending_read,并加入到pending_read队列中,这样command的执行就被异步化了。在before中执行了handleClientsWithPendingReadsUsingThreads,就是将pending_read中的client分发到不同的io线程,通过这些io线程去读取和解析缓冲区的数据。这里有一个特殊的设计点。redis被设计成单线程执行command。所以在io线程准备好command以及参数后,并不会立即执行command,会打上一个pending_command标记。在主线程等待所有io线程完成解析任务后。一次性执行所有command。注意缓冲区中存储的数据可能不只对应一个command。所以主线程最后还要执行一次processInputBuffer解析剩余的数据并尝试执行command。
(2) 执行handleClientsWithPendingWrites()
同样描述一下该方法做了什么,通过查看network模块的代码可以发现,读取socket的数据可以在io线程中执行,此时被标记成pending_read,在没有配置的情况下会立即解析数据并处理。而将数据返回给client(写出数据),必然是为client设置pending_write标记,代表写入操作总是异步的。执行handleClientsWithPendingWrites时会找到所有处于pending_write的client并写入数据,如果socket缓冲区没有空间了,就会设置writeHandler,当写事件准备完成后就会将剩余的数据写入socket缓冲区。
(3)关闭所有在clients_to_close队列中的client
至此已经处理完ProcessingEventsWhileBlocked为true的逻辑了
4.现在看ProcessingEventsWhileBlocked为false的情况
(1)检查server.clients_timeout_table内部的client是否满足超时条件 并从阻塞状态解除。就是当执行blockClient时,才会将client设置到这个table中
(2)也是执行handleClientsWithPendingReadsUsingThreads
(3)如果当前节点在集群模式下打开 执行clusterBeforeSleep,有关集群的之后在讲
(4)抽样检测db中过期的数据,逻辑与serverCron中的db抽样大体相同
(5)在执行waitCommand时某个slave可能会要求一个master下的其他slave中有一定数量的数据同步偏移量跟上了本节点。这里检测副本中数量是否满足。主从结构在redis中就是master->slave.而集群中还有分片的概念,也就体现在多master。
(6)针对此时已经解除阻塞的所有client,解析他们缓冲区中的数据并执行command,此时无法使用io线程进行优化。
(7)如果发现了server.get_ack_from_slaves标记,就像所有slave节点发送请求,获取对端最新的信息,比如同步偏移量。
(8)对aof文件进行刷盘
(9)执行handleClientsWithPendingWritesUsingThreads(), 该方法与handleClientsWithPendingWrites的区别就是使用io线程来执行写入任务。写入任务不涉及数据的访问所以可以并行执行。而读取数据时,执行command就必须要放到主线程执行。
(10)关闭所有在clients_to_close队列中的client
(11)执行handleClientsBlockedOnKeys,client想执行某些指令时,发现key还不存在就会基于这个key被阻塞,当key被创建后,就可以将这些client从阻塞状态解除。并执行之前被阻塞的命令。
总的来说,当server还处于数据恢复阶段。beforeSleep执行的是一些简易的逻辑,主要就是处理与其他client的数据交互。
接着是afterSleep (1)此时不处于数据恢复阶段,获取moduleGIL锁
在redis的主流程中,与aof/rdb的写入刷盘逻辑紧耦合。之后会讲解一下aof和rdb