初始化Server
hello,又到了本期的博客了,这一期我将会给大家介绍启动时redis是如何初始化网络状态的,大家一起快乐的学习吧!!
先看一看初始化server在main函数被调用的代码:
int main(int argc char * argv[])
{
loadServerConfig(server.configfile, config_from_stdin, options);
/*
*****
*/
initServer();
/*
****
*/
}
复制代码
当将配置文件加载到全局变量server中时,这时redis就会根据配置文件中的内容去初始化服务端状态了,server在全局中的定义如下:
/* Global vars */
struct redisServer server; /* Server global state */
复制代码
接下来我们来看看初始化server具体干了哪些事情。
1 信号
signal(SIGHUP, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
setupSignalHandlers();
makeThreadKillable();
复制代码
signal(SIGHUP, SIG_IGN)
用于忽略SIGHUP信号
,它是一种在Unix和类Unix操作系统中广泛使用的信号。在Unix系统中,当控制终端挂起时,会向进程组中的所有进程发送SIGHUP信号,通常用于重新初始化进程。通常情况下,当一个进程接收到SIGHUP信号时,它会终止执行,但使用signal(SIGHUP, SIG_IGN)
可以忽略这个信号,从而避免进程被终止。在上面的代码中,当Redis服务器接收到SIGHUP信号时,它不会做任何事情。
signal(SIGPIPE, SIG_IGN)
的作用是忽略对于管道/套接字等读取端已经关闭的写入操作而产生的
SIGPIPE信号
。在使用网络套接字进行通信时,如果对方断开连接,而当前套接字仍然在写数据,那么就会产生SIGPIPE
信号,程序默认情况下会退出。通过将SIGPIPE
信号的处理函数设置为SIG_IGN
,程序将忽略该信号,避免程序异常退出。
void setupSignalHandlers(void) {
struct sigaction act;
/* When the SA_SIGINFO flag is set in sa_flags then sa_sigaction is used.
* Otherwise, sa_handler is used. */
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_handler = sigShutdownHandler;
sigaction(SIGTERM, &act, NULL);
sigaction(SIGINT, &act, NULL);
sigemptyset(&act.sa_mask);
act.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO;
act.sa_sigaction = sigsegvHandler;
if(server.crashlog_enabled) {
sigaction(SIGSEGV, &act, NULL);
sigaction(SIGBUS, &act, NULL);
sigaction(SIGFPE, &act, NULL);
sigaction(SIGILL, &act, NULL);
sigaction(SIGABRT, &act, NULL);
}
return;
}
复制代码
setupSignalHandlers()
函数用于设置Redis进程的信号处理程序。在Unix/Linux系统中,信号是一种异步通信机制,用于处理进程之间的异步事件。在Redis中,有多种信号可以被处理,比如SIGTERM
表示终止进程,SIGINT
表示中断进程等。通过设置信号处理程序,Redis可以对这些异步事件做出响应,例如在SIGTERM
信号到来时进行清理工作并优雅地关闭Redis进程。
首先,使用 sigemptyset
函数初始化一个信号集,将其存储在 act.sa_mask
中,以便在信号处理器运行时阻塞其他信号。然后将 act.sa_flags
设置为 0,表明使用默认行为,即不使用 SA_RESTART
重新启动系统调用。最后,将 act.sa_handler
设置为 sigShutdownHandler
函数,表示在接收到信号时调用 sigShutdownHandler
函数进行处理。此函数用于处理 SIGTERM
和 SIGINT
信号,以使 Redis 正常退出。
首先,sigemptyset(&act.sa_mask)
会将 act.sa_mask
初始化为空集,这是为了在信号处理函数运行期间防止进程收到其他信号。
接着,act.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO;
设置了信号处理选项。其中,SA_NODEFER
表示在信号处理函数执行期间禁止该信号被阻塞,SA_RESETHAND
表示在信号处理函数执行完毕后将该信号处理方式重置为默认值,SA_SIGINFO
表示使用带有三个参数的 sa_sigaction
处理函数。
最后,act.sa_sigaction = sigsegvHandler
设置了信号处理函数。如果进程收到SIGSEGV信号,就会调用 sigsegvHandler
函数。
捕获SIGSEGV、SIGBUS、SIGFPE、SIGILL和SIGABRT信号。如果服务器的crashlog_enabled选项设置为true,则向这些信号注册sigsegvHandler()函数作为信号处理程序。如果这些信号被触发,它们将调用sigsegvHandler()函数。此函数是Redis的默认崩溃日志记录器,它将记录崩溃时的上下文信息并尝试将其写入服务器的日志文件。
makeThreadKillable()
复制代码
makeThreadKillable()
函数的作用是将当前线程标记为可被取消的状态。在使用线程时,当线程处于某些关键代码段时,如果接收到取消请求,可能会造成资源泄漏等问题,因此需要将线程标记为可被取消的状态,以便在接收到取消请求时,线程可以安全地停止执行。此函数通常在执行长时间操作的线程中使用,例如执行文件I/O或网络I/O的线程。
2 日志
if (server.syslog_enabled) {
openlog(server.syslog_ident, LOG_PID | LOG_NDELAY | LOG_NOWAIT,
server.syslog_facility);
}
复制代码
这段代码的作用是在启用syslog的情况下,调用openlog函数打开syslog服务,并设置syslog_ident、syslog_facility等参数。syslog是一个系统日志服务,用于记录操作系统或者应用程序的日志信息。在Redis中,如果启用了syslog,Redis就会把一些重要的日志信息输出到syslog中,以方便用户查看和分析。openlog是syslog服务提供的一个函数,用于打开syslog服务,并设置相关参数。参数LOG_PID表示在每条日志信息中加入当前进程ID,参数LOG_NDELAY表示打开syslog服务时不会等待,参数LOG_NOWAIT表示在写入日志信息时,不会等待syslog进程返回。
3 初始化数据结构和变量
server.aof_state = server.aof_enabled ? AOF_ON : AOF_OFF;
server.hz = server.config_hz;
server.pid = getpid();
server.in_fork_child = CHILD_TYPE_NONE;
server.main_thread_id = pthread_self();
server.current_client = NULL;
server.errors = raxNew();
server.fixed_time_expire = 0;
server.in_nested_call = 0;
server.clients = listCreate();
server.clients_index = raxNew();
server.clients_to_close = listCreate();
server.slaves = listCreate();
server.monitors = listCreate();
server.clients_pending_write = listCreate();
server.clients_pending_read = listCreate();
server.clients_timeout_table = raxNew();
server.replication_allowed = 1;
server.slaveseldb = -1; /* Force to emit the first SELECT command. */
server.unblocked_clients = listCreate();
server.ready_keys = listCreate();
server.tracking_pending_keys = listCreate();
server.clients_waiting_acks = listCreate();
server.get_ack_from_slaves = 0;
server.client_pause_type = CLIENT_PAUSE_OFF;
server.client_pause_end_time = 0;
memset(server.client_pause_per_purpose, 0,
sizeof(server.client_pause_per_purpose));
server.postponed_clients = listCreate();
server.events_processed_while_blocked = 0;
server.system_memory_size = zmalloc_get_memory_size();
server.blocked_last_cron = 0;
server.blocking_op_nesting = 0;
server.thp_enabled = 0;
server.cluster_drop_packet_filter = -1;
server.reply_buffer_peak_reset_time = REPLY_BUFFER_DEFAULT_PEAK_RESET_TIME;
server.reply_buffer_resizing_enabled = 1;
= NULL;
复制代码
这是 Redis 服务器在启动时初始化的一些变量和数据结构。以下是每个变量的简要说明:
- server.aof_state: 标志是否启用 AOF(Append Only File)持久化,初始化为启用或禁用状态。
- server.hz: Redis 服务器的运行频率,即每秒执行的事件循环次数。
- server.pid: Redis 服务器的进程 ID。
- server.in_fork_child: 标记当前进程是否为子进程,初始为 NONE。
- server.main_thread_id: Redis 服务器的主线程 ID。
- server.current_client: 当前客户端,初始为 NULL。
- server.errors: 存储 Redis 服务器发生的错误,以及错误发生的次数和时间戳等信息。
- server.fixed_time_expire: 指定某些键的过期时间是否为固定时间。
- server.in_nested_call: 标记 Redis 服务器是否处于嵌套调用状态,初始为 0。
- server.clients: 存储所有已连接的客户端。
- server.clients_index: 用于快速查找客户端。
- server.clients_to_close: 存储待关闭的客户端。
- server.slaves: 存储所有从服务器。
- server.monitors: 存储所有 MONITOR 客户端。
- server.clients_pending_write: 存储需要写入数据的客户端。
- server.clients_pending_read: 存储需要读取数据的客户端。
- server.clients_timeout_table: 存储客户端的超时时间。
- server.replication_allowed: 标志是否允许复制,初始为允许。
- server.slaveseldb: 从服务器的当前数据库,初始化为 -1。
- server.unblocked_clients: 存储非阻塞客户端。
- server.ready_keys: 存储已就绪的键。
- server.tracking_pending_keys: 存储需要追踪的键。
- server.clients_waiting_acks: 存储等待客户端应答的客户端。
- server.get_ack_from_slaves: 标志是否等待从服务器的应答。
- server.client_pause_type: 客户端暂停状态,初始化为未暂停。
- server.client_pause_end_time: 客户端暂停结束时间,初始化为 0。
- server.client_pause_per_purpose: 存储客户端暂停的原因和时间戳。
- server.postponed_clients: 存储已推迟处理的客户端。
- server.events_processed_while_blocked: 存储在阻塞状态下处理的事件数。
- server.system_memory_size: Redis 服务器可用的系统内存大小。
- server.blocked_last_cron: 记录 Redis 服务器最后一次被阻塞的时间。
- server.blocking_op_nesting: 阻塞操作的嵌套深度,初始为 0。
- server.thp_enabled: 是否启用 Transparent Huge Pages。
- server.cluster_drop_packet_filter: 集群节点间通信中过滤器的编号,初始化为 -1。
- server.reply_buffer_peak_reset_time: Redis 回复缓冲区的峰值重置时间
- server.client_mem_usage_buckets跟踪客户端的内存使用情况,如果为
NULL
表示未启用内存使用统计。
4 重置服务器缓冲区
void resetReplicationBuffer(void) {
server.repl_buffer_mem = 0;
server.repl_buffer_blocks = listCreate();
listSetFreeMethod(server.repl_buffer_blocks, (void (*)(void*))zfree);
}
复制代码
resetReplicationBuffer()是Redis服务器中的一个函数,用于重置服务器的复制缓冲区。
复制缓冲区是Redis用来存储复制操作期间生成的命令的缓冲区。这些命令会被发送给从服务器,以便它们可以复制主服务器上执行的操作。在复制期间,如果从服务器断开连接或出现其他错误,那么Redis将重置复制缓冲区以避免数据丢失或混淆。
5 TLS
if ((server.tls_port || server.tls_replication || server.tls_cluster)
&& tlsConfigure(&server.tls_ctx_config) == C_ERR) {
serverLog(LL_WARNING, "Failed to configure TLS. Check logs for more info.");
exit(1);
}
复制代码
这段代码片段是在服务器启动时检查是否需要配置 TLS,并尝试进行配置。如果服务器配置了 TLS 端口、TLS 复制或 TLS 集群,就会调用 tlsConfigure
函数进行配置。如果配置失败,会记录一条警告日志并退出服务器。这里使用了 exit
函数,因此服务器无法继续运行。
TLS是Transport Layer Security(传输层安全协议)的缩写,是一种加密协议,用于保护计算机网络通信的安全。它是SSL(Secure Sockets Layer,安全套接字层)的继任者。TLS协议通过对数据进行加密、认证和完整性保护等手段来保证通信的安全性。在网络通信中,常用的应用层协议,如HTTP、SMTP、POP3等,都可以在TLS协议的基础上实现安全通信。
6 创建共享对象
void createSharedObjects(void) {
int j;
/* Shared command responses */
shared.crlf = createObject(OBJ_STRING,sdsnew("\r\n"));
shared.ok = createObject(OBJ_STRING,sdsnew("+OK\r\n"));
shared.emptybulk = createObject(OBJ_STRING,sdsnew("$0\r\n\r\n"));
shared.czero = createObject(OBJ_STR