redis是如何初始化Server-源码的

初始化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 函数进行处理。此函数用于处理 SIGTERMSIGINT 信号,以使 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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值