2012年11月3日星期六

Redis源码走读

同为内存K-V数据库,Redis具有数据持久化等功能,比memcached强大。下面通过走读Redis代码,了解Redis大体框架。

首先从Redis服务器端的main函数开始,main调用initServerConfig对redisServer结构类型的server全局变量进行默认初始化,填充默认端口(6379)、DB数量(16)等字段。initServerConfig接着调用populateCommandTable对全局变量redisCommandTable保存的Redis命令进行分类,分类后的命令由全局变量commandTable存放,lookupCommand函数用于命令查找。

之后main函数调用loadServerConfig,使用redis.conf中的配置重新填充server中的字段。接着main调用initServer,大部分服务器初始化工作由该函数完成。initServer进行以下函数调用:


  • 调用listCreate创建存放不同状态的客户端的链表,填充server中clients、clients_to_close、slaves、monitors等字段
  • 调用createShareObjects创建共享对象,服务器内部实现用到的特定字符串、整数,Redis将其包装成共享对象,存放在share全局变量中,以供其他数据结构共用。如封装了换行符 '\r\n' 的字符串对象,响应消息、出错消息均要使用;strings、sets、lists等结构均要使用整数对象作ID、引用计数等
  • 调用aeCreateEventLoop,该函数调用aeApiCreate,aeApiCreate调用epoll_create,创建epoll实例
  • 调用zmalloc给DB分配内存,之后分别对各个DB分配dict
  • 调用anetTcpServer,其底层调用socket、setsockopt、bind、listen等系统接口,完成端口监听
  • 调用aeCreateTimeEvent,将serverCron加到时间事件队列,serverCron为Redis服务器完成断连超时客户端等定时任务
  • 调用aeCreateFileEvent,将acceptTcpHandler加入IO事件队列,acceptTcpHandler调用anetTcpAccept,anetTcpAccept 调用anetGenericAccept,anetGenericAccept 执行while(1)循环调用系统接口accept,等待客户端的连接


回到main函数,main调用aeMain,aeMain首先调用beforesleep,如果配置了AOF,beforesleep调用flushAppendOnlyFile将内存数据刷入磁盘,接着aeMain调用aeProcessEvents处理事件事件和IO事件。

以上为Redis服务器启动过程的代码实现,下面我们来看Redis服务器处理指令的代码实现。

在服务器初始化initServer函数中,注册了acceptTcpHandler IO事件函数,并循环调用accept等待客户端接入。当有客户端接入时,acceptTcpHandler下的acceptCommonHandler函数被调用,acceptCommonHandler调用createClient,createClient创建一个redisClient对象c,选定一个DB,将accept返回的文件描述符记录到c的fd字段,并将c加到server.client链表,调用aeCreateFileEvent将readQueryFromClient添加到IO事件队列,该函数用于接受客户端指令。

当客户端向服务器发送指令,readQueryFromClient函数被调用,其调用processInputBuffer对指令进行解析,processInputBuffer调用processCommand对指令作有效性检查,最后processCommand调用cmd->proc对执行进行处理,相对与set指令,cmd->proc指示的就是setCommand函数。

Have fun!

没有评论:

发表评论