Redis的主要脉络梳理
来源:互联网 发布:淘宝上卖牛仔裤的好店 编辑:程序博客网 时间:2024/04/30 20:33
Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
由于工作需要,需要深入研究此开源代码用于选型及设计代码,先从main函数开始吧。
参考版本:Redis-3.2.3
首先从main函数开始@server.c
int main(int argc, char **argv) {// 全局变量 struct redisServer server; /* server global state */ 初始化initServerConfig(); /* Check if we need to start in redis-check-rdb mode. We just execute * the program main. However the program is part of the Redis executable * so that we can easily execute an RDB check on loading errors. */ if (strstr(argv[0],"redis-check-rdb") != NULL) redis_check_rdb_main(argc,argv); // 重置server.saveparams resetServerSaveParams(); // 加载redis.conf作为配置文件 loadServerConfig(configfile,options);// 决定是否以daemon方式启动redisserver.supervised = redisIsSupervised(server.supervised_mode); int background = server.daemonize && !server.supervised; if (background) daemonize();// 初始化服务器--主要工作是createSharedObjects();及aeCreateEventLoop创建事件LoopinitServer();// 判断是否需要从aof文件或者rdb装载数据if (!server.sentinel_mode) {loadDataFromDisk();...}else{sentinelIsRunning();}// 设置每次进入事件处理函数之前需要执行的函数aeSetBeforeSleepProc(server.el,beforeSleep);// 进入事件循环主函数,永不退出,除非服务被终止 aeMain(server.el); // main函数结束、服务器退出 aeDeleteEventLoop(server.el); return 0;}
下面对子过程进行详细分析:
1、客户端连接处理
initServer()@server.c 这个函数中会调用anetTcpServer和anetUnixServer,分别建立tcp端口和unix域套接字的监听。
anetTcpServer@anet.c
int anetTcpServer(char *err, int port, char *bindaddr, int backlog){ return _anetTcpServer(err, port, bindaddr, AF_INET, backlog);}static int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backlog){// 建立TCP套接字监听s = socket(p->ai_family,p->ai_socktype,p->ai_protocol);anetSetReuseAddr(err,s);anetListen(err,s,p->ai_addr,p->ai_addrlen,backlog);return s;}以上只是设置要监听的端口、地址、和地址族,再调用anetListen()函数绑定地址并监听端口,这些工作完成后
anetTcpServer 函数返回,并将创建的套接字复制给server.ipfd.
listenToPort(server.port,server.ipfd,&server.ipfd_count)
-->
int listenToPort(int port, int *fds, int *count) {
fds[*count] = anetTcpServer(server.neterr,port,server.bindaddr[j],
server.tcp_backlog);
}
前面提到有一个EventLoop的事件队列、这个fd将会在此被使用。
void initServer(void) {// 建立主事件队列--EventLoopserver.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR);... /* Create an event handler for accepting new connections in TCP and Unix * domain sockets. */ for (j = 0; j < server.ipfd_count; j++) { if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE, acceptTcpHandler,NULL) == AE_ERR) { serverPanic( "Unrecoverable error creating server.ipfd file event."); } }}
首先,从eventLoop的event这个aeFileEvent数组里,取出当前fd对应的acFileEvent,
主要是为了在下边给它设置对应事件的处理函数;即根据传入的mask来判断是哪一类事件。
这里就是 server.ipfd 加入事件监控中、当发生AE_READABLE可读事件时则触发 acceptTcpHandler 函数。
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, aeFileProc *proc, void *clientData){ if (fd >= eventLoop->setsize) { errno = ERANGE; return AE_ERR; } aeFileEvent *fe = &eventLoop->events[fd];// 加入异步事件监控中 if (aeApiAddEvent(eventLoop, fd, mask) == -1) return AE_ERR; fe->mask |= mask; if (mask & AE_READABLE) fe->rfileProc = proc; if (mask & AE_WRITABLE) fe->wfileProc = proc; fe->clientData = clientData; if (fd > eventLoop->maxfd) eventLoop->maxfd = fd; return AE_OK;}
此时监控建立完毕、此时客户端一个新的连接到来,此时将会触发事件。
主循环事件函数:aeMain@Ae.c
void aeMain(aeEventLoop *eventLoop) { eventLoop->stop = 0; // 只要stop标志一直为0则循环永远运行下去 while (!eventLoop->stop) { if (eventLoop->beforesleep != NULL) eventLoop->beforesleep(eventLoop); aeProcessEvents(eventLoop, AE_ALL_EVENTS); }}/* Process every pending time event, then every pending file event * (that may be registered by time event callbacks just processed). * Without special flags the function sleeps until some file event * fires, or when the next time event occurs (if any). * * If flags is 0, the function does nothing and returns. * if flags has AE_ALL_EVENTS set, all the kind of events are processed. * if flags has AE_FILE_EVENTS set, file events are processed. * if flags has AE_TIME_EVENTS set, time events are processed. * if flags has AE_DONT_WAIT set the function returns ASAP until all * the events that's possible to process without to wait are processed. * * The function returns the number of events processed. */int aeProcessEvents(aeEventLoop *eventLoop, int flags){// 最重要的轮询函数!!! numevents = aeApiPoll(eventLoop, tvp); for (j = 0; j < numevents; j++) { aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd]; int mask = eventLoop->fired[j].mask; int fd = eventLoop->fired[j].fd; int rfired = 0; /* note the fe->mask & mask & ... code: maybe an already processed * event removed an element that fired and we still didn't * processed, so we check if the event is still valid. */ if (fe->mask & mask & AE_READABLE) { rfired = 1; fe->rfileProc(eventLoop,fd,fe->clientData,mask); // 可读回调 } if (fe->mask & mask & AE_WRITABLE) { if (!rfired || fe->wfileProc != fe->rfileProc) fe->wfileProc(eventLoop,fd,fe->clientData,mask); // 可写回调 } processed++; }}
客户端新的连接则触发可读事件、即acceptTcpHandler函数
void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) { int cport, cfd, max = MAX_ACCEPTS_PER_CALL; // 当前redis服务器的最大连接总数 char cip[NET_IP_STR_LEN]; while(max--) { cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport); if (cfd == ANET_ERR) { if (errno != EWOULDBLOCK) serverLog(LL_WARNING, "Accepting client connection: %s", server.neterr); return; } serverLog(LL_VERBOSE,"Accepted %s:%d", cip, cport); // 接收新的连接后处理函数 acceptCommonHandler(cfd,0,cip); }}利用anetTcpAccept产生新的fd并加入到事件队列中
static void acceptCommonHandler(int fd, int flags, char *ip) { client *c = createClient(fd);// 客户端数目限定 if (listLength(server.clients) > server.maxclients) { char *err = "-ERR max number of clients reached\r\n";..}}-->client *createClient(int fd) { client *c = zmalloc(sizeof(client)); /* passing -1 as fd it is possible to create a non connected client. * This is useful since all the commands needs to be executed * in the context of a client. When commands are executed in other * contexts (for instance a Lua script) we need a non connected client. */ if (fd != -1) { anetNonBlock(NULL,fd); anetEnableTcpNoDelay(NULL,fd); if (server.tcpkeepalive) anetKeepAlive(NULL,fd,server.tcpkeepalive); // 给前面获取的客户端连接的套接字注册 AE_READABLE事件,并设置事件处理函数readQueryFromClient if (aeCreateFileEvent(server.el,fd,AE_READABLE, readQueryFromClient, c) == AE_ERR) { close(fd); zfree(c); return NULL; } }...}
这里结束后则 readQueryFromClient()便是redis处理客户端发送的命令的起始点了。
1 0
- Redis的主要脉络梳理
- 网站的脉络梳理
- CNN知识脉络梳理
- Redis的基本知识梳理
- 服务端编程技术脉络梳理
- AI自动编程脉络梳理
- 理清知识的脉络
- 中华文化的基本脉络
- JavaWeb 的发展脉络
- 图像边缘检测技术与理论发展脉络梳理
- Redis _ Redis lesson10 的 redis.conf 中的主要配置
- linux启动的脉络[zhuan]
- 转帖:linux启动的脉络
- 人工智能发展的脉络图
- 中华文明的基本脉络---唐加文
- 脉络清晰的BP神经网络
- Linux发展的历史脉络
- redis 知识梳理
- 大数据系列修炼-Scala课程34
- hql的再次探究
- OkHttpClient使用示例
- 将H264码流打包成RTP包
- u盘 单个文件超过4G怎么办?
- Redis的主要脉络梳理
- 不定方程问题-经典案例“白文买百鸡“
- 构建单链表的递归和非递归版本(C++版)
- iOS音频格式转换
- js内核判断
- Unity3D之获取某个方法执行的时间
- Struts2.0学习笔记---ognl表达式的常见用法
- Activity生命周期
- 如何利用arcgis server发布大量数据的地图