服务器
redis服务器负责与多个客户端建立网络连接,处理客户端发送的命令请求,在数据库中保存客户端执行命令所才参数的数据,并通过资源管理来维持服务器自身的运转。 1. 命令请求的执行过程 以SET命令为例: redis SET key valueredis OK 1.1 发送命令请求 1.2读
redis服务器负责与多个客户端建立网络连接,处理客户端发送的命令请求,在数据库中保存客户端执行命令所才参数的数据,并通过资源管理来维持服务器自身的运转。
1. 命令请求的执行过程
以SET命令为例:
redis> SET key value
redis> OK
1.1 发送命令请求
1.2读取命令请求
当客户端与服务器之间的连接套接字因为客户端的写入而变的可读时,服务器将调用命令请求处理器来执行如下操作:
①读取套接字中协议格式的命令请求,保存到客户端状态的输入缓冲区。
②对缓冲区中的命令请求进行分析,提取命令参数及个数,保存到客户端状态的argv属性和argc属性。
③调用命令执行器执行客户端指定的命令。
1.3 命令执行器
(1)查找命令实现
根据客户端状态的 argv[0] 参数, 在命令表(command table)中查找参数所指定的命令, 并将找到的命令保存到客户端状态的 cmd 属性里面。
命令表是一个字典, 字典的键是一个个命令名字,比如 “set” 、 “get” 、 “del” ,等等; 而字典的值则是一个个 redisCommand 结构, 每个 redisCommand 结构记录了一个 Redis 命令的实现信息。
(2)执行预备操作
(3)调用命令的实现函数
client->cmd->proc(client);
(4)执行后续工作
1.4 将命令回复发送给客户端
命令实现函数会将命令回复保存到客户端的输出缓冲区里面, 并为客户端的套接字关联命令回复处理器, 当客户端套接字变为可写状态时, 服务器就会执行命令回复处理器, 将保存在客户端输出缓冲区中的命令回复发送给客户端。
当命令回复发送完毕之后, 回复处理器会清空客户端状态的输出缓冲区, 为处理下一个命令请求做好准备。
1.5 客户端接收并打印命令回复
2. serverCron函数
redis服务器中的serverCron函数默认每隔100毫秒执行一次,这个函数负责管理服务器的资源,并保持服务器自身的良好运转。
主要工作包括:更新服务器状态信息,处理服务器接收的SIGTERM信号,管理客户端资源和数据库状态,检查并执行持久化操作等。
主要工作包括:
更新服务器的各类统计信息,比如时间,内存占用,数据库占用情况等。
清理数据库中过期键值对。
关闭和清理连接失效的客户端
尝试进行AOF或RDB持久化操作。
如果服务器是主服务器,对从服务器进行定期同步。
如果处理集群模式,对集群进行定期同步和连接测试。
redisServer结构/serverCron函数属性
/* This is our timer interrupt, called server.hz times per second.
*
* 这是 Redis 的时间中断器,每秒调用 server.hz 次。
*
* Here is where we do a number of things that need to be done asynchronously.
* For instance:
*
* 以下是需要异步执行的操作:
*
* - Active expired keys collection (it is also performed in a lazy way on
* lookup).
* 主动清除过期键。
*
* - Software watchdog.
* 更新软件 watchdog 的信息。
*
* - Update some statistic.
* 更新统计信息。
*
* - Incremental rehashing of the DBs hash tables.
* 对数据库进行渐增式 Rehash
*
* - Triggering BGSAVE / AOF rewrite, and handling of terminated children.
* 触发 BGSAVE 或者 AOF 重写,并处理之后由 BGSAVE 和 AOF 重写引发的子进程停止。
*
* - Clients timeout of different kinds.
* 处理客户端超时。
*
* - Replication reconnection.
* 复制重连
*
* - Many more...
* 等等。。。
*
* Everything directly called here will be called server.hz times per second,
* so in order to throttle execution of things we want to do less frequently
* a macro is used: run_with_period(milliseconds) { .... }
*
* 因为 serverCron 函数中的所有代码都会每秒调用 server.hz 次,
* 为了对部分代码的调用次数进行限制,
* 使用了一个宏 run_with_period(milliseconds) { ... } ,
* 这个宏可以将被包含代码的执行次数降低为每 milliseconds 执行一次。
*/
int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
int j;
REDIS_NOTUSED(eventLoop);
REDIS_NOTUSED(id);
REDIS_NOTUSED(clientData);
/* Software watchdog: deliver the SIGALRM that will reach the signal
* handler if we don't return here fast enough. */
if (server.watchdog_period) watchdogScheduleSignal(server.watchdog_period);
/* Update the time cache. */
updateCachedTime();
// 记录服务器执行命令的次数
run_with_period(100) trackOperationsPerSecond();
/* We have just REDIS_LRU_BITS bits per object for LRU information.
* So we use an (eventually wrapping) LRU clock.
*
* Note that even if the counter wraps it's not a big problem,
* everything will still work but some object will appear younger
* to Redis. However for this to happen a given object should never be
* touched for all the time needed to the counter to wrap, which is
* not likely.
*
* 即使服务器的时间最终比 1.5 年长也无所谓,
* 对象系统仍会正常运作,不过一些对象可能会比服务器本身的时钟更年轻。
* 不过这要这个对象在 1.5 年内都没有被访问过,才会出现这种现象。
*
* Note that you can change the resolution altering the
* REDIS_LRU_CLOCK_RESOLUTION define.
*
* LRU 时间的精度可以通过修改 REDIS_LRU_CLOCK_RESOLUTION 常量来改变。
*/
server.lruclock = getLRUClock();
/* Record the max memory used since the server was started. */
// 记录服务器的内存峰值
if (zmalloc_used_memory() > server.stat_peak_memory)
server.stat_peak_memory = zmalloc_used_memory();
/* Sample the RSS here since this is a relatively slow call. */
server.resident_set_size = zmalloc_get_rss();
/* We received a SIGTERM, shutting down here in a safe way, as it is
* not ok doing so inside the signal handler. */
// 服务器进程收到 SIGTERM 信号,关闭服务器
if (server.shutdown_asap) {
// 尝试关闭服务器
if (prepareForShutdown(0) == REDIS_OK) exit(0);
// 如果关闭失败,那么打印 LOG ,并移除关闭标识
redisLog(REDIS_WARNING,"SIGTERM received but errors trying to shut down the server, check the logs for more information");
server.shutdown_asap = 0;
}
/* Show some info about non-empty databases */
// 打印数据库的键值对信息
run_with_period(5000) {
for (j = 0; j server