Redis源码分析精通PDF版(redis源码分析pdf)
Redis源码分析:精通PDF版
Redis是一款开源的Key-Value存储数据库,其原理和特点已被广泛涵盖在其他文章中,因此这里不再赘述。本文将由浅入深,逐一分析Redis的源代码,并为初学者提供一份精通Redis源码的PDF版本。
一、基础知识
在开展Redis源码的分析之前,我们需要准备好所需的开发环境,包括Redis的编译环境、C语言基础等。以下是关于Redis基础知识的推荐学习资料:
1.《Redis开发与运维》(原书第2版)
2.《Redis设计与实现》
3.《C Primer Plus》(中文版)
4. 《C程序设计语言》
对于已经具备相关基础知识的开发者,下面将介绍Redis源码的分析。
二、源码结构
Redis的源代码核心代码存放在src目录下,同时还包括一些其他文件,如:
1.测试代码存放在tests目录下
2.默认配置文件存放在配置目录下
3.文档和手册存放在doc目录下
4.扩展模块存放在modules目录下
我们需要在源代码中查找我们需要修改或了解的地方,并编译出Redis的可执行文件。
三、代码分析
从mn函数开始,我们可以看到Redis的启动流程。
首先加载并解析配置文件,然后初始化其他模块(例如网络模块、数据库模块等),最后进入主循环。在主循环中,Redis等待客户端的命令,并根据收到的命令执行相应的操作。
以下是Redis源代码的一部分(src/server.c):
“`c
#include “server.h”
int mn(int argc, char **argv) {
struct timeval tv;
int j;
srand(time(NULL)^getpid());
initServerConfig();
if (argc == 2) {
j = serverLoadConfig(argv[1]);
} else {
j = serverLoadConfig(“/etc/redis/redis.conf”);
if (j == REDIS_ERR) serverLog(LL_WARNING,”Warning: no config file specified, using the default config. In order to specify a config file use %s /path/to/redis.conf”,argv[0]);
}
if (j == REDIS_ERR) exit(1);
initServer();
if (server.daemonize) daemonize();
if (server.pidfile) createPidFile();
if (server.rdb_filename == NULL) server.rdb_filename = zstrdup(“dump.rdb”);
if (server.aof_filename == NULL) server.aof_filename = zstrdup(“appendonly.aof”);
aclInit();
moduleInitModulesSystem();
loadDataFromDisk();
moduleLoadFromQueue();
if (server.cluster_enabled) {
if (verifyClusterConfigWithData() == REDIS_ERR) {
serverLog(LL_WARNING,
“Fled to verify cluster configuration agnst online data. “
“Retrying verification… (Check for errors before the server ‘is able to work’)”);
if (verifyClusterConfigWithData() == REDIS_ERR) {
serverLog(LL_WARNING,”Aborting now.”);
exit(1);
}
}
}
moduleFireServerEvent(REDISMODULE_EVENT_LOADING,0,NULL);
aclLoadUsersFromDisk();
bioInit();
if (server.repl_state == REDIS_REPL_SLAVE) {
if (syncWithMaster(1) == REDIS_ERR) {
serverLog(LL_WARNING,”%s”, “FATAL: The server is not able to synchronize with\nthe master server.”);
exit(1);
}
}
if (server.cluster_enabled && nodeIsSlave(myself)) {
replicationSetMaster(getClusterNodeMaster(), server.port);
}
aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL);
if (!serverSentinelMode) {
if (server.bindaddr_count > 0) {
char ip[16];
int j;
for (j = 0; j
sockaddrToIp(server.bindaddr[j],&ip[0],sizeof(ip));
serverLog(LL_NOTICE,”Binding %s:%d”,ip,server.port);
}
} else {
serverLog(LL_NOTICE,”Server started, Redis version ” REDIS_VERSION);
}
}
adjustOpenFilesLimit();
dictSetHashFunctionSeed(server.hash_seed);
showLogo();
/* When log gets enabled we print an initial log line. */
if (logLevel != LL_NOTICE)
serverLog(logLevel,
“Server started, Redis version ” REDIS_VERSION);
gettimeofday(&tv,NULL);
server.stat_starttime = tv.tv_sec;
/* Things not needed to be done every time the server restarts. */
if (server.stat_starttime != server.lastbgsave_try) {
server.mstime_at_initiate_last_save = mstime();
}
/* Open the listening sockets. */
if (listenToPort(server.port,server.ipfd,&server.ipfd_count) == REDIS_ERR) {
serverLog(LL_WARNING,”Opening port %d: %s”, server.port, strerror(errno));
exit(1);
}
aeCreateFileEvent(server.el, server.ipfd[0], AE_READABLE,
acceptTcpHandler,NULL);
if (server.port && listenToPort(server.tls_port,server.tlsfd,&server.tlsfd_count) == REDIS_ERR) {
serverLog(LL_WARNING,”Opening TLS port %d: %s”, server.tls_port, strerror(errno));
} else if (server.tls_port) {
aeCreateFileEvent(server.el, server.tlsfd[0], AE_READABLE|AE_READ_THREADSAFE,
acceptTLSHandler,NULL);
}
moduleFireServerEvent(REDISMODULE_EVENT_LOADED,0,NULL);
serverLog(LL_NOTICE,”Ready to accept connections”);
aeMn(server.el);
aeDeleteEventLoop(server.el);
return 0;
}
我们可以看到这段代码主要包括加载配置文件、初始化模块、加载数据等操作,最后进入事件处理循环。
在主循环中,Redis使用了一个事件框架(ae),所有的事件被封装成文件事件(File Event)或时间事件(Time Event)进行处理。以下是Redis处理时间事件的示例:
```c/* =========================== ServerCron ================================ */
void serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) { int j;
UNUSED(eventLoop); UNUSED(id);
UNUSED(clientData);
/* Update the time cache. */ updateCachedTime();
/* Increment various statistics tracked by our "slowlog". */ server.slowlog_tick++;
/* Sample the RSS and other metrics here since it is very cheap to grab * and we can collect stats with a resolution up to the cron loop
* period if we do so. */ if (server.cronloops % 10 == 0)
processCpuUsed();
/* Record the max length of the client output buffer. */ if (listLength(server.clients) > 0) {
size_t max = getClientOutputBufferMemoryUsage(server.clients->head->value); if (server.stat_peak_client_output_buffer
server.stat_peak_client_output_buffer = max; }
/* Close clients that need to be closed asynchronous */ freeClientsInAsyncFreeQueue();
/* Update information about connected clients. */ trackInstantaneousMetric(REDIS_METRIC_COMMAND, (long long) server.stat_numcommands);
trackInstantaneousMetric(REDIS_METRIC_NET_INPUT, server.stat_net_input_bytes); trackInstantaneousMetric(REDIS_METRIC_NET_OUTPUT, server.stat_net_output_bytes);
trackInstantaneousMetric(REDIS_METRIC_NET_INPUT_BYTES_PER_SEC, server.stat_net_input_bytes_sec); trackInstantaneousMetric(REDIS_METRIC_NET_OUTPUT_BYTES_PER_SEC, server.stat_net_output_bytes_sec);
trackInstantaneousMetric(REDIS_METRIC_EXPIRED_KEYS, server.stat_expiredkeys);
/*定期执行的操作*/ server.cronloops++;
/* Replication cron function -- used to reconnect to master and * detect transfer flures. */
replicationCron(); moduleHandleSystemTimerEvents();
aeDeleteTimeEvents(server.el, AE_TIME_EVENT_ALL_IDS); for (j = 0; j
int i;
/* We have just 10 milliseconds to do everything. The mn goal of * this first loop is serving the clients: all the other operations
* should be executed as a consequence of the processing we do * in this loop. */
long long start = ustime(); if (server.cluster_enabled) clusterUpdateState();
if (server.lua_timedout) handleClientsTimeout(); if (server.cluster_migration_barrier_time &&
server.cluster_migration_barrier_time {
server.cluster_migration