Redis服务器是典型的一对多服务器程序:一个服务器能够与多个客户端创建网络链接,每一个客户端能够向服务器发送命令请求,而服务器则接收并处理客户端发送的命令请求,并向客户端返回命令回复。redis
经过使用由I/O多路复用技术实现的文件事件处理器,Redis服务器使用单线程单进程的方式处理命令请求,并于多个客户端进行网络通讯。数组
每一个与服务器进行链接的客户端,服务端都为这些客户端创建相应的redisClient结构(客户端状态),保存客户端当前的状态信息,以及执行相关功能时须要用到的数据结构。安全
Redis服务器状态结构的clients属性是一个链表,这个链表保存了全部与服务器链接的客户端的状态结构,对客户端执行批量操做,或者查找某个指定的客户端,均可以经过遍历clients链完成。服务器
struct redisServer{ //一个链表,保存了全部客户端状态 list *clients; }
一、客户端状态的fd属性记录了客户端正在使用的套接字描述符网络
typedef struct redisClient{ int fd; } redisClient;
根据客户端的类型不一样fd的属性值能够是-1,或者是大于-1的整数:伪客户端fd属性为-1;普通客户端的fd属性值为大于-1的整数。数据结构
二、默认状况下一个链接到服务器的客户端是没有名称的, 可使用Client setname 命令为客户端设置一个名称。函数
三、客户端的标志属性flags记录了客户端角色(role),以及客户端状态学习
typedef struct redisClient{ int flag; } redisClient;
在主从服务器进行复制操做时,主服务器会成为从服务器的客户端,而从服务器也会成为主服务器的客户端。lua
REDIS_MASTER 客户端是一个主服务器spa
REDIS_BLOCKED 客户端正在被列表命令阻塞
REDIS_MULTI | REDIS_DIRTY_CAS 客户端正在执行事务,但事务的安全性已经被破坏
REDIS_SLAVE | REDOS_PRE_PSYNC 客户端是一个从服务器,而且版本低于REDIS2.8
REDIS_LUA_CLIENT | REDIS_FORCE_AOF | REDIS_FORCE_REPL 这是专门执行Lua脚本包含的redis命令的伪客户端 ,它强制服务器将当前执行的命令写入到AOF文件,并复制给从服务器。
四、客户端状态的输入缓冲区用户保存客户端发送的命令请求:
typedef struct redisClient { sds querybuf; } redisClient;
五、在服务器将客户端发送的命令请求保存在客户端状态的querybuf属性后,服务器将对命令请求的内容进行分析,并将得出的命令参数以及命令参数的个数分别保存到客户端状态的argv属性和argc属性:
typedef struct redisClient{ robj **argv; int argc; } redisClient;
argv属性是一个数组,数组的每一个项都是一个字符串队形,其中avgv[0]是要执行的命令,其以后的其余项都是传给命令的参数。
六、当服务器从协议内容中分析并得出argv属性和argc属性的值以后,服务器将根据项argv[0]的值,在命令表中查找命令所对应的实现函数。
命令表是一个字典表,字典的键是一个SDS结构,保存了命令的名字,字典值是命令的名字,字典值是命令所对应的redisCommand结构,这个结构保存了命令的实现函数、命令的标志、命令应该给定的参数个数、命令的总执行次数和总耗时长等统计信息。
当程序在命令表中成功找到argv[0]所对应的redisCommand结构时,它会将客户端状态的cmd指针指向这个结构:
typedef struct redisClient{ struct redisCommand *cmd; } redisClient;
以后,服务器就可使用cmd属性所指向的redisCommand结构,以及argv、argc属性中保存的命令参数信息,调用命令实现函数,执行客户端指定的命令。
七、执行命令所得的命令回复会被保存在客户端状态的输出缓冲区里面,每一个客户端有两个输出缓冲区能够用,一个缓冲区的大小是固定的,一个大小是可变的。
固定大小的缓冲区用于保存那些长度比较小的回复,可变大小的缓冲区用于保存耐饿长度比较大的回复。
客户端的固定大小缓冲区由buf和bufpos两个属性组成
typedef struct redisClient{ char buf[REDIS_REPLY_CHUNK_BYTES];//REDIS_REPLY_CHUNK_BYTES 默认16 *1024 也就是说固定缓冲区默认 16k int bufpos;//记录已使用的字节数量 } redisClient;
八、身份认证
客户端状态的authenticated属性用于记录客户端是否经过了身份验证:
typedef struct redisClient{ int authenticated; } redisClient;
authenticated属性仅在服务器启用了身份验证功能的时候使用,若是服务器没有启用的话,即便为0也不会拒绝客户端的命令请求。
九、时间
typedef struct redisClient{ time_t ctime;//建立客户端的时间 链接服务器时长(秒) time_t lastinteraction;//最后一次与服务器互动时间,计算客户端空转时间 //记录缓冲区第一次到达软性限制的时间 time_t obuf_soft_limit_reached_time; } redisClient;
若是客户端是经过网络链接于服务器进行链接的普通客户端,那么客户端使用connect函数链接到服务器时,服务器就会调用链接事件处理器,为客户端建立相应的客户端状态,并将这个新的客户端状态添加到服务器状态结构clients链表的末尾。
普通客户端能够由于多种缘由而被关闭:
客户端进程退出或者被杀死,那么客户端与服务端的网络链接被关闭。
若是客户端向服务端发送了带有不符合协议的命令请求,那么这个客户端也会被关闭。
若是客户端成为了CLIENT KILL命令的目标,那么它也会被关闭。
若是服务端设置了timeout配置项,那么客户端的空转事件超过timeout现象设置的值时,客户端被关闭。
若是客户端发送的命令请求的大小超过了输入缓冲区的限制大小(默认1G),那么客户端会被服务器关闭。
若是要发送给客户端的命令回复的大小超过了输出缓冲区的限制大小,那么这个客户端会被服务器关闭。
服务器使用两种模式来限制客户端输出缓冲区的大小:
一、硬性限制,若是输出缓冲区的大小超过了硬性限制所设置的大小,那么服务器当即关闭客户端。
二、软性限制,若是输出缓冲区的大小超过了软性限制设置的大小,但没有超过硬性设置的大小,obuf_soft_limit_reached_time属性记录下客户端达到软性限制的起始事件,以后服务器会继续监视客户端,若是输出缓冲区的大小一直超出软性限制,而且持续时间超过服务器设置的时长,那么服务器将关闭客户端。
服务端会在初始化时建立负责执行Lua脚本中包含redis命令的伪客户端,并将这个伪客户端关联在服务器状态结构的lua_client属性中:
struct redisServer{ redisClient *lua_client; }
lua_client 伪客户端在服务器运行的整个声明周期中会一直存在,只有服务器关闭时,这个客户端才会关闭。
服务器在载入AOF文件时,会建立用于执行AOF文件的Redis命令的伪客户端,并在载入完成以后,关闭伪客户端。
天天学一点,总会有收获。
说明:尊重做者知识产权,文中内容参考《Redis设计与实现》,仅在此作学习与你们分享。