《Redis设计与实现》读书笔记(十八) ——Redis客户端属性设计与原理

《Redis设计与实现》读书笔记(十八) ——Redis客户端属性设计与原理

(原创内容,转载请注明来源,谢谢)

一、概述

redis服务器是一对多服务器,多个客户端可以与一个服务器建立连接,并且分别发送请求,服务器接收请求并分别回复。通过使用I/O多路复用技术实现的文件事件处理器,redis服务器使用单线程单进程的方式来处理请求,并与多个客户端建立网络通信。

1、单个客户端状态结构存储

每个与服务器建立连接的客户端,服务器都为这些客户端建立相应的redis.h/redisClient结构,用于分别存储每个客户端的状态,这个结构保存客户端当前的信息,以及执行相关功能时候需要用到的数据结构,主要包括:

1)客户端信息

客户端套接字描述符,客户端名字,客户端标志值,客户端身份验证标志,客户端创建时间、最后一次与服务器通信时间、客户端输出缓冲区以及缓冲区大小超出服务器软性限制的时间。

2)客户端状态

客户端当前使用的数据库指针及数据库号码,客户端当前要执行的命令、命令参数、参数个数、实现命令的指针。

3)客户端用到的数据结构

客户端复制状态信息及复制所需的数据结构,客户端执行brpop、blpop等阻塞列表命令时用到的数据结构,客户端事务及watch用到的数据结构,客户端执行发布订阅功能用到的数据结构。

2、所有客户端存储

redis通过redisServer结构的clients属性,将所有的客户端结构进行存储,存储是采用链表的方式。如下:

         struct redisServer{
         //…其他内容
         list *clients;
         }

clients属性,以链表的形式,存储了所有的客户端状态,里面的每个元素都代表一个客户端,包含该客户端的诸如客户端信息、状态、使用的数据结构等全部信息。如下图所示:

二、客户端属性

客户端属性分为通用的和特定操作下的属性,特定操作如切换数据库、执行事务、watch等。这里重点学习通用属性。

主要用到的类型如下:

         typedef struct redisClient{
         //..其他内容
         int fd;
         robj *name;
         int flags;
         sds querybuf;
         robj **argv;
         int argc;
         struct redisCommand*cmd;
         char buf[REDIS_REPLY_TRUNK_BYTES];
         int bufpos;
         list *reply;
         int authenticated;
         time_t ctime;
         time_tlastinteraction;
         time_t obuf_soft_limit_reached_time;
         }redisClient;

1、套接字描述符

在redisClient里面,用int的方式记录当前使用的套接字描述符,属性名称是fd。

fd的值是-1或者大于-1的整数。当客户端是来自AOF文件或者Lua脚本,需要生成的临时的伪客户端,则值是-1,表示其不需要网络;正常的客户端值是大于-1的整数,这个值是redis客户端和redis服务器进行通信的描述符。

2、名字

客户端的名字属性名称是name,类型是redis的字符串对象。

默认情况下客户端没有名字,可以通过client setname设置名字,通过client list命令查看当前所有客户端的信息,如下是设置名字之后的:

如果没有设置名字,name是指向null的指针,设置之后则是redis字符串对象。

3、标志

客户端的标志记录客户端的角色,和当前客户端所处的状态,用int类型存储,属性名是flags。标志可以是单个的也可以是多个的二进制“或”。如flags = flag1 或者flags = flag1 | flag2 |flag3。

每个标志是一个常量,主要的如下:

1)主从复制期间,主服务器会成为从服务器的客户端,REDIS_MASTER表示客户端是主服务器,REDIS_SLAVE表示客户端是从服务器。

2)REDIS_PRE_PSYNC表示客户端是redis版本低于2.8的从服务器,主服务器不能用psync命令才进行复制。

3)REDIS_LUA_CLIENT表示lua脚本创建的伪客户端。

4)还有其他标志,最重要的是REDIS_UNBLOCKED,这标志标记redis已经从阻塞状态脱离出来,只有这样的客户端才会接收到服务器发送的信息。

5)REDIS_FORCE_AOF,这个标志也很特殊,默认情况下只有“写”相关的命令才会计入aof文件,但是存在个别命令,虽然不会影响到数据库的数据,但是会产生副作用,则需要用此标记,强制将结果写入aof。例如pubsub命令,会给所有订阅者发送信息;script load命令,加载脚本。

flags例子如下:

4、输入缓冲区

输入缓冲区保存客户端发送的命令请求,采用redis的sds类型存储,名称是querybuf。sds是简单动态字符串,是redis用来存储字符串对象值的结构。

输入缓冲区会根据输入的内容动态扩大或减小,但是不能超过1gb,否则redis会自动关闭该客户端。

输入命令set key value,querybuf的结果(缩略)如下:

5、命令与命令参数

当发送请求道querybuf后,redis会对命令进行分析,将命令参数保存到**argv,命令个数保存到argc。

argv是一个数组,每一项都是一个redis字符串对象,其中argv[0]是要执行的命令,后面的下标是传入的参数。argc则是记录数组argv的长度。

输入命令set key value,argc和argv的结果如下:

6、命令实现函数

分析完querybuf,并生成argc和argv后,redis将根据argv[0]的内容,确定相应的要实现的函数。

redis保存着一个数据字典,该字典存储着每个命令与其对应的redisCommand结构,键是sds结构的命令,值是对应的redisCommand结构。该字典也成为redis的命令表。

客户端的cmd属性,即分析完argv[0]后,从上述redis命令表中找到对应的结构,并用指针指向该结构。命令表的键不区分大小写,因此redis的命令也是不区分大小写的。

7、输出缓冲区

执行命令得到的回复,会保存在输出缓冲区中,每个客户端都有两个缓冲区,一个是固定大小的,另一个可变大小的。

固定大小的缓冲区由buf和bufpos两个属性组成,buf是一个数组,用于存储固定大小的返回值,默认大小是16kb;bufpos记录目前buf已经使用的字节数。固定大小缓冲区通常用于记录长度比较短的元素,如OK、短的字符串返回值、整数、错误回复等。

可变大小的缓冲区由链表组成,属性名是reply,链表内是一个或多个字符串对象组成。当回复的长度太长,则用可变大小缓冲区。另外,如果一开始用固定大小缓冲区,当记录过程中大小超过16kb,则会转换成可变大小缓冲区。

8、身份验证

redis用int记录是否验证身份,属性名是authenticated,0表示未通过验证,1表示已经通过验证。

当未验证身份,除了auth以外的命令,都会被redis服务器拒绝。

9、时间

redis共有3个属性记录客户端的时间相关的内容:ctime、lastinteraction、obuf_soft_limit_reached_time,类型都是time_t。

ctime记录客户端与redis服务器创建连接的时间,clientlist命令可以查看客户端连接的秒数,用age表示,单位是秒。

lastinteraction表示客户端与redis服务器最后一次交互的时间,可以是服务器发送信息给客户端的时间,也可以是客户端发送信息给服务器的时间。这个时间主要用来判断空转时间,即连接后没有操作的时间,client list可以查看空转时间,用idle表示,单位是秒。

obuf_soft_limit_reached_time表示输出缓冲区第一次达到软性限制的时间。

——written by linhxx 2017.09.08

原文发布于微信公众号 - 决胜机器学习(phpthinker)

原文发表时间:2017-09-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏IMWeb前端团队

Nodejs进阶:如何玩转子进程(child_process)

本文作者:IMWeb 陈映平 原文出处:IMWeb社区 未经同意,禁止转载 模块概览 在node中,child_process这个模块非常重要。掌握了...

3158
来自专栏c#开发者

oracle 常用命令大汇总

oracle 常用命令大汇总(第一篇)     第一章:日志管理     1.forcing log switches     sql> alter ...

2557
来自专栏我有一个梦想

设计模式学习笔记-命令模式

1. 概述   将一个请求封装为一个对象(即我们创建的Command对象),从而使你可用不同的请求对客户进行参数化; 对请求排队或记录请求日志,以及支持可撤销的...

18810
来自专栏同步博客

HTTP协议详解以及URL具体访问过程

  即超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议,所有的WWW文件都必须遵守这个标准...

2103
来自专栏偏前端工程师的驿站

页面分部分加载呈现收集(不断更新中)

前言                                      因项目首页内容多,每次点击都会有一段画面空白的时间,使用感不好,于是找找看有没有...

1759
来自专栏张善友的专栏

反向代理(Reverse Proxy)及 IIS 7 应用请求路由模块

反向代理(Reverse Proxy)方式是指以代理服务器来接受外部的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给外部的请求连接...

2208
来自专栏安富莱嵌入式技术分享

【RL-TCPnet网络教程】第20章 RL-TCPnet之BSD Socket客户端

本章节为大家讲解RL-TCPnet的BSD Socket,学习本章节前,务必要优先学习第18章的Socket基础知识。有了这些基础知识之后,再搞本章节会有事半功...

812
来自专栏Timhbw博客

学习提升之HTTP状态码详解

2016-11-1410:23:53 发表评论 315℃热度 1.URL 2.URL中常见的协议 3.TCP/IP协议簇 4.TCP/IP参考模型 ...

2896
来自专栏运维小白

10.3 top命令

监控系统状态 top 命令,查看进程使用资源情况 top -c 显示详细的进程信息 top -bn1 静态显示所有进程 q 键退出,数字1显示所有核cpu,大写...

1695
来自专栏散尽浮华

linux运维中的命令梳理(四)

----------管理命令---------- ps命令:查看进程 要对系统中进程进行监测控制,查看状态,内存,CPU的使用情况,使用命令:/bin/ps (...

3978

扫码关注云+社区