Redis server 启动后会做哪些操作?
Redis 是典型的 Client-Server 架构,一旦 Redis 实例开始运行,Redis server 也就会启动,而 main 函数其实也会负责 Redis server 的启动运行。
Redis 针对以下三个问题的实现思路:
Redis server 启动后具体会做哪些初始化操作?
Redis server 初始化时有哪些要害配置项?
Redis server 如何开始解决客户端申请?
(1) Redis Server 启动
源码地址 https://github.com/redis/redi…
//file: src/server.c
// #L5297 5297 行
int main(int argc, char **argv) {
// 省略局部代码...
// 1. 启动 RedisServer 时 初始化配置及资源
initServer();
// 2. 循环解决申请及事件,直到服务器敞开为止
aeMain(server.el);
}
(1.1) 启动 RedisServer 初始化配置及资源
在 initServer 这个函数内,Redis 做了这么三件重要的事件。
1、创立一个 epoll 对象
2、对配置的监听端口进行 listen
3、把 listen socket 让 epoll 给治理起来
void initServer(void) {
// 1. 创立 epoll
server.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR);
// 2. 监听端口
listenToPort(server.port,server.ipfd,&server.ipfd_count)
// 3. 注册 accept 事件处理器 (只是注册 前面会用到)
for (j = 0; j < server.ipfd_count; j++) {aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,
acceptTcpHandler,NULL)
}
}
(1.2) 循环解决申请
// file: src/ae.c
/**
* 循环处理事件
*
* @param *eventLoop
*/
void aeMain(aeEventLoop *eventLoop) {
eventLoop->stop = 0;
// 循环处理事件
while (!eventLoop->stop) {
// 处理事件
aeProcessEvents(eventLoop, AE_ALL_EVENTS|
AE_CALL_BEFORE_SLEEP|
AE_CALL_AFTER_SLEEP);
}
}
// file: src/ae.c
/**
* 处理事件
*
* @param *eventLoop
* @param flags
*/
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
{
// 省略局部细节...
// 调用多路复用 API 获取就绪事件
numevents = aeApiPoll(eventLoop, tvp);
// 解决写事件
fe->wfileProc(eventLoop,fd,fe->clientData,mask);
// 解决读事件
fe->rfileProc(eventLoop,fd,fe->clientData,mask);
}
其实整个 Redis 的工作过程,就只须要了解分明 main 函数中调用的 initServer
和 aeMain
这两个函数就足够了。
(2) RedisServer 网络申请解决流程
(2.1) 绑定地址并监听套接字 (bind listen)
// file: src/server.c
// 监听端口
/**
* @param port
* @param *fds
* @param *count
*/
int listenToPort(int port, int *fds, int *count) {
// ...
for (j = 0; j < server.bindaddr_count || j == 0; j++) {
// ... 省略 绑定 IPV6 IPV4 的细节 anetTcp6Server anetTcpServer
// 绑定
fds[*count] = anetTcpServer(server.neterr,port,NULL,
server.tcp_backlog);
// ...
}
return C_OK;
}
Redis 是反对开启多个端口的,所以在 listenToPort 中咱们看到是启用一个循环来调用 anetTcpServer。
在 anetTcpServer 中,逐渐会开展调用,直到执行到 bind 和 listen 零碎调用。
// file: src/anet.c
/**
* @param *err
* @param port
* @param *bindaddr
* @param backlog
*/
int anetTcpServer(char *err, int port, char *bindaddr, int backlog)
{return _anetTcpServer(err, port, bindaddr, AF_INET, backlog);
}
/**
* @param *err
* @param port
* @param *bindaddr
* @param af
* @param backlog
*/
static int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backlog)
{
int s = -1, rv;
char _port[6]; /* strlen("65535") */
struct addrinfo hints, *servinfo, *p;
// 创立套接字
s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)
// 设置端口重用
anetSetReuseAddr(err,s)
// 监听
anetListen(err,s,p->ai_addr,p->ai_addrlen,backlog)
/**
* 监听
*
* @param *err
* @param s 对应创立的套接字 fd
* @param *sa socket 地址信息 (协定 地址)
* @param len
* @param backlog
*/
static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len, int backlog) {
// 绑定 ip 端口
bind(s,sa,len)
// 监听套接字
listen(s, backlog)
return ANET_OK;
}
(2.2) 和客户端建设连贯 (accept)
// file: src/networking.c
/**
* 接管 tcp 处理器
*
* @param *el
* @param fd
* @param *privdata
* @param mask
*/
void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
// ...
// 接管 tcp 申请
cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);
// ...
// 接管通用解决
acceptCommonHandler(connCreateAcceptedSocket(cfd),0,cip);
}
(2.2.1) 接管 tcp 申请 -anetTcpAccept
// file: src/anet.c
/**
* 接管 tcp 申请
*
* @param *err
* @param s fd
* @param *ip
* @param ip_len
* @param *port
*/
int anetTcpAccept(char *err, int s, char *ip, size_t ip_len, int *port) {
int fd;
struct sockaddr_storage sa; // 套接字地址存储构造体
socklen_t salen = sizeof(sa);
// 接管申请
fd = anetGenericAccept(err,s,(struct sockaddr*)&sa,&salen)
// ...
return fd;
}
// file: src/anet.c
/**
* @param *err
* @param s
* @param *sa
* @param *len
*/
static int anetGenericAccept(char *err, int s, struct sockaddr *sa, socklen_t *len) {
int fd;
while(1) {
// 接管 socket 数据
// fd 是 socket 返回的 socket,指向的定义的 SOCKADDR_IN 构造体指针,指针的大小
fd = accept(s,sa,len);
// ...
break;
}
return fd;
}
参考资料
Redis 高性能 IO 模型
https://weikeqin.com/2022/01/…
Redis 源码分析与实战 学习笔记 Day8 08 | Redis server 启动后会做哪些操作?
https://time.geekbang.org/col…