redis 事件 客户端 服务器

1. 事件

1.1 main函数流程图

image

1.2 文件事件

image
  • 构成
  1. 套接字
  2. I/O多路复用程序
  3. 文件事件分派器
  4. 事件处理器
  • 套接字产生AE_READABLE事件(服务器读套接字,优先)
  1. 套接字可应答(客户端对套接字执行connect)
  2. 套接字可读(客户端对套接字执行write/close)
  • 套接字产生AE_WRITABLE事件(服务器写套接字)
  1. 套接字可写(客户端对套接字执行read)

1.3 时间事件

时间事件放入无序链表

  • server.c/initServer()里的server.c/serverCron()函数

更新服务器的内存占用、数据库占用情况。- zmalloc_used_memory(), zmalloc_get_allocator_info()
清理过期键值对。 - databasesCron()
关闭和清理连接失效的客户端。 - clientsArePaused()
尝试进行 AOF 或 RDB 持久化操作。- rewriteAppendOnlyFileBackground(), rdbSaveBackground()
如果服务器是主节点的话,对附属节点进行定期同步。
如果处于集群模式的话,对集群进行定期同步和连接测试。- clusterCron()
如果处于哨兵模式的话,运行哨兵检查机制。- sentinelTimer()

2. 客户端

2.1 数据结构

struct redisServer{
    list *clients;
    ...
}

struct client{
    int fd; //客户端正在使用的套接字描述符
    redisDb *db; //当前select的DB
    int flags; //角色和状态
    
    sds querybuf; //输入命令缓冲区
    int argc; //参数个数【命令名字本身也是参数!】
    robj **argv; //参数
    struct redisCommand *cmd; //命令集合
    
    int bufpos; //固定大小的输出缓冲区已使用
    char buf[PROTO_REPLY_CHUNK_BYTES]; //固定大小的输出缓冲区,16k
    list *reply; //可变大小输出缓冲区,字符串链表
    int authenticated; //0未认证通过,1认证通过
    ...
}

2.2 客户端类型

  • 伪客户端(fd = -1,不需要套接字描述符,伴随redisServer一直存在)
  1. 载入AOF文件还原数据库
  2. 执行Lua脚本里的redis命令
  • 普通客户端(fd > 0)
redis > CLIENT list
reids > CLIENT setname

3. 服务器

3.1 命令集合

struct redisCommand redisCommandTable[] = {
    {"module", //name, 命令名称
    moduleCommand, //proc,执行函数指针
    -2, //arity,参数个数,包括命令本身。-N个表示大于或等于N个。
     "admin no-script", //sflags,命令属性
     0, //flags,分析命令属性得到的二进制标识
     NULL,0,0,0,0,0,0},

    {"get",getCommand,2,
     "read-only fast @string",
     0,NULL,1,1,1,0,0,0}
     ...
}
命令属性 含义 示例
read-only 只读 get
write 要写 set,rpush,del
use-memory 占用内存(需检查内存) set
admin 管理命令 bgsave,shutdown
fast 内核调度给时间就不会延迟执行的命令,隐式调用del的均不是fast get,setnx,exists,rpush
no-script 不能在Lua脚本里使用 blpop,auth,bgsave,multi相关
random 随机命令 randomkey,srandmember
ok-loading 允许命令加载数据库 select,auth,multi相关,pub-sub相关
ok-stale 允许正在数据载入时执行 select,auth,multi相关,pub-sub相关
no-monitor 不传给monitor auth,hello
pub-sub pub-sub相关命令 subscribe等
@xxx 按类别给命令排序用的属性 @write,@hash等

3.2 【单机时】命令预备工作

  1. 检查cmd是否为null,null表示没有此命令。
  2. 检查参数。
  3. 检查auth通过没有。
  4. 写命令时,检查内存占用,有需要时回收内存。
  5. 写命令时,根据stop-writes-on-bgsave-error(默认yes),来检查上次BGSAVE是否error。
  6. 若客户端订阅了频道,只会接受客户端pub-sub相关命令。
  7. 若正在载入数据的话,to-stale命令才允许执行。
  8. 若正在阻塞执行Lua,则只允许shutdown nosave和script kill命令。
  9. 若客户端正在执行事务,则只执行此客户端的事务相关命令。
  10. 若打开了monitor功能,则把命令传给monitor。

3.3 命令后续工作

  1. 若开启了慢查询(默认slowlog-log-slower-than 10000,表示0.1秒。slowlog-max-len 128),检查并添加慢查询日志。
  2. 更新redisCommandTable[]里面的calls计数和millisenconds执行总时长。
  3. 若开启了AOF,若有需要则记录到AOF。
  4. 若正在主从复制,则传播命令。

4. client/server交互流程

4.1 client发起socket连接

src/redis-cli -h {ip} -p {port}
//unix socket用于同一主机进程间的通信
src/redis-cli -s /tmp/redis.sock
  1. cliConnect() -> redisConnect() -> 创建context,对socket执行connect。
  2. socket产生AE_READABLE事件。
  3. server接受成功后,cliAuth() 权限验证。
  4. server接受成功后,cliSelect() DB选择。
  5. server接受成功后,cliSwitchProto() 协议选择。

4.2 server接受socket连接

  1. 初始化服务器时,打开TCP监听端口。
  2. 连接应答处理器,通过epoll_ctl注册事件。ae.c/aeCreateFileEvent(),ae_epoll.c/acceptTcpHandler()
  3. client发起socket连接后,server通过epoll_wait取出事件。
  4. acceptTcpHandler()接受socket连接。
  5. server后续1:createClient()
  6. server后续2:ae.c/aeCreateFileEvent()->ae_epoll.c/aeApiAddEvent()->epoll_ctl注册事件,绑定readQueryFromClient方法。

4.3 客户端开始写入

  1. 将客户端内容格式化成redis协议。cliSendCommand()
  2. 写入context的outbuf。
  3. outbuf内容写入套接字描述符,产生AE_READABLE事件。
  4. 读取挂起。

4.4 server接收写入

  1. readQueryFromClient()读取内容到client对象命令缓冲区。
  2. redis协议解析并传给processCommand()。
  3. 命令预备工作。
  4. call(),从redisCommand命令表里找执行方法。(判断key过期,键空间失效通知等执行相关操作)。

4.5 server返回结果

  1. 写client reply缓冲区。
  2. aeCreateFileEvent()注册写事件,绑定sendReplyToClient()方法。
  3. 结果内容写入到socket,触发AE_WRITABLE事件。

4.6 Client收到结果

  1. 若客户端1秒内收到读事件,则从socket读数据 - redisBufferRead()。
  2. cliFormatReplyRaw()解析。
  3. printf()打印展示。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容