IT技术博客大学习 共学习 共进步

redis源代码分析

运维和开发 2011-08-05 13:43:22 浏览 6,182 次

我们跟踪一个普通的get命令来遍历几个关键函数,熟悉协议处理的过程。

你可以通过telnet或者redis_cli、利用lib库发送请求给redis server。前者的是一种裸协议的请求发送到服务端,而后两者会对键入的请求进行协议组装帮助更好的解析(常见的是长度放到前头,还有添加阿协议类型)。

Requests格式

*参数的个数 CRLF
$第一个参数的长度CRLF
第一个参数CRLF
...
$第N个参数的长度CRLF
第N个参数CRLF

例如在redis_cli里键入get a,经过协议组装后的请求为

2\\r\\n$3\\r\\nget\\r\\n$1\\r\\na\\r\\n

下面这个图涵盖了接收request,处理请求,调用函数,发送reply的过程。

\"\"

redis的网络事件库,我们在前面的文章已经讲过,readQueryFromClient先从fd中读取数据,先存储在c->querybuf里(networking.c 823)。接下来函数processInputBuffer来解析querybuf,上面说过如果是telnet发送的裸协议数据是没有*打头的表示参数个数的辅助信息,针对telnet的数据跳到processInlineBuffer函数,而其他则通过函数processMultibulkBuffer。
这两个函数的作用一样,解析c->querybuf的字符串,分解成多参数到c->argc和c->argv里面,argc表示参数的个数,argv是个redis_object的指针数组,每个指针指向一个redis_object, object的ptr里存储具体的内容,对于”get a“的请求转化后,argc就是2,argv就是

(gdb) p (char*)(*c->argv[0])->ptr
$28 = 0x80ea5ec \"get\"
(gdb) p (char*)(*c->argv[1])->ptr
$26 = 0x80e9fc4 \"a\"

协议解析后就执行命令。processCommand首先调用lookupCommand找到get对应的函数。在redis server 启动的时候会调用populateCommandTable函数(redis.c 830)把readonlyCommandTable数组转化成一个hash table(server.commands),lookupCommand就是一个简单的hash取值过程,通过key(get)找到相应的命令函数指针getCommand( t_string.c 437)。
getCommand比较简单,通过另一个全局的server.db这个hash table来查找key,并返回redis object,然后通过addReplyBulk函数返回结果。

下面介绍一下reply协议。
bulk replies是以$打头消息体,格式$值长度\\r\\n值\\r\\n,一般的get命令返回的结果就是这种个格式。

redis>get aaa
$3\\r\\nbbb\\r\\n

对应的的处理函数addReplyBulk

addReplyBulkLen(c,obj);
addReply(c,obj);
addReply(c,shared.crlf);

error messag是以-ERR 打头的消息体,后面跟着出错的信息,以\\r\\n结尾,针对命令出错。

redis>d
-ERR unknown command \'d\'\\r\\n

处理的函数是addReplyError

addReplyString(c,\"-ERR \",5);
addReplyString(c,s,len);
addReplyString(c,\"\\r\\n\",2);

integer reply 是以:打头,后面跟着数字和\\r\\n。

redis>incr a
:2\\r\\n

处理函数是

addReply(c,shared.colon);
addReply(c,o);
addReply(c,shared.crlf);

status reply以+打头,后面直接跟状态内容和\\r\\n

redis>ping
+PONG\\r\\n

这里要注意reply经过协议加工后,都会先保存在c->buf里,c->bufpos表示buf的长度。待到事件分离器转到写出操作(sendReplyToClient)的时候,就把c->buf的内容写入到fd里,c->sentlen表示写出长度。当c->sentlen=c->bufpos才算写完。

还有一种Multi-bulk replies,lrange、hgetall这类函数通常需要返回多个值,消息结构与请求的格式一模一样。相关的函数是setDeferredMultiBulkLength。临时数据存储在链表c->reply里,处理方式同其他的协议格式。

建议继续学习

  1. redis源代码分析 - persistence (阅读 32,105)
  2. Redis消息队列的若干实现方式 (阅读 11,927)
  3. 基于Redis构建系统的经验和教训 (阅读 10,383)
  4. 浅谈redis数据库的键值设计 (阅读 9,223)
  5. redis运维的一些知识点 (阅读 8,523)
  6. redis在大数据量下的压测表现 (阅读 8,204)
  7. Redis和Memcached的区别 (阅读 7,944)
  8. redis 运维实际经验纪录之一 (阅读 7,584)
  9. Redis作者谈Redis应用场景 (阅读 7,546)
  10. 记Redis那坑人的HGETALL (阅读 7,324)