1、选择合适的代码版本
如果是作为初中级入门,想通过项目更加深刻地掌握c语言语法,了解生产级项目的架构设计,则一定不能贪多。为了最大限度地学习项目的架构设计、以及减少bug,推荐选择第一个主版本的最后一个稳定小版本。
redis作为2009年就发布的基于内存的键值数据库,虽然结构简洁、功能强大,但他最新版本的代码库也一定不会简单。通过 cloc 命令可以简单的统计出redis 各个主要版本的代码行数。
1.3.6
版本 | c代码行数 |
---|---|
1.3.6 | 10892 |
2.8.4 | 64061 |
3.2.13 | 78612 |
6.2.13 | 129308 |
7.2.1 | 170691 |
作为初中级入门,10w+的代码行数是不可接受的,而1.3.6就刚刚好,1w+的代码,删繁就简,重构出最小系统的 mini-redis ,代码总量应该也可以控制在小千范围内。
2、梳理项目的核心逻辑结构
通过CODEMAP源代码阅读器,现在梳理项目的核心逻辑结构已经不再那么困难。
2.1、核心初始化流程
CODEMAP源代码阅读器分析界面
- 系统通过 main() 函数启动,分别运行 initServer() 和 aeMain() 函数,initServer() 执行服务器的初始化,aeMain() 当 !eventLoop->stop 就一直执行 aeProcessEvents() 处理事件的死循环
- 初始化 server.el 和 server.fd,然后通过 aeCreateFileEvent() 函数配置事件循环监听网络事件,当产生可读事件,则通过 acceptHandler 处理
- 产生网络连接后,首先创建 redisClient 结构体,然后又通过 aeCreateFileEvent() 函数,配置 eventLoop 和 c->fd ,当继续产生可读事件,则由 readQueryFromClient() 处理
- 处理 client 传来的数据,在 cwdTable 中查找相应的处理方法。cmdTable 中存储了 redis 支持的所有命令,以及相应的处理方法、以及在虚拟内存中的相关配置信息等
- 最后执行相应命令,并返回
2.1、get命令执行流程
CODEMAP源代码阅读器分析界面
- cmdTable 中存储了 redis 支持的所有命令,redisCommand 结构体包括了命令名、处理函数、参数数量、标志位、以及虚拟内存相关的配置
- getCommand 函数通过 getGenericCommand() 主要是执行了 lookupKey() 和 addReply() 两步
- redis 的 key、value 键值对存储在 redisDb 的 dict 元素中,dict 结构体是一个 hash 表,table 元素是 dictEntry 指针的数组,dictEntry 是一个链表,其中存储了键值对的真实数据 key 和 value 的指针,dict 相关的操作和头文件都在 dict.c 和 dict.h 中
- 当查找 key 对应的 value 时,首先通过 dictHashKey() 算出当前 key 的索引值 h,然后通过 ht->table[h] 找到链表的第一个 dictEntry,通过对比 dictEntry 的 key 和当前 key 是否完全一致,查找相应的 dictEntry
- 根据找到的 dictEntry 的具体类型,添加相应的回复
2.2、hset、hget、hdel 命令执行流程
- hset 命令调用 hsetCommand() 处理函数
- 通过 lookupKeyWrite(c->db,c->argv[1]) 查找键值对应的 hash 表, redis 的数据存储在 redisDb.dict