Redis 是一个客户端服务端的程序,服务端提供数据存储等等服务,客户端链接服务端并经过向服务端发送命令,读取或写入数据,简单来讲,客户端就是某种工具,咱们经过它与 Redis 服务端进行通信并完成数据操做。java
客户端并非 Redis 的核心,Redis 的核心是它的服务端程序,服务端程序才是完成数据存、取,持久化等等咱们使用频繁的各类操做的执行者。但也不是说客户端就没什么做用,客户端在整个 Redis 服务体系中也是很是重要的一环。本篇先来看看 Redis 客户端的一些特性以及实现原理。git
redis 中为客户端抽象的数据结构是,server.h/client 结构,我这里是 redis-4.0.x 版本,不一样版本或许稍有不一样,每个 redis 客户端成功的链接上服务端以后,服务端就会建立一个 client 结构实例,并以链表的形式连接全部链接成功的客户端。程序员
这个结构最主要做用就是存储当前客户端的大量属性,套接字、名字、标志,状态等等信息,这些信息很是的重要,当服务端为客户端服务时,不少的信息例如当前要执行的命令、参数都会从这里获取。咱们一个一个来了解。github
一、客户端名称redis
默认状况下,全部链接成功的客户端都是没有名字的,这一点你能够经过向服务发送 client list 命令验证,它会返回当前服务端成功创建的客户端以及他们的基本信息。例如:缓存
能够看到,name 字段默认是空,若是你想让你的客户端辨识度更高,你能够向服务端发送 client setname 为你的客户端命名,这里我就不作演示了,客户端名称这个信息保存在 client 结构中的 name 字段里。服务器
typedef struct client { ......... robj *name; /* As set by CLIENT SETNAME. */ ......... } client;
二、标志微信
标志用于描述当前 redis 客户端的一些状态或者角色,对应的到数据结构中就是一个整型字段。数据结构
typedef struct client { ......... int flags; /* Client flags: CLIENT_* macros. */ ......... } client;
Redis 中定义了不少的客户端标志,函数
一个整型的 flags 字段,能够经过二进制或(|) 的方式同时存储过个状态,好比:
flags = 0000 0110 = CLIENT_MASTER | CLIENT_MONITOR
固然了,上面那个 flages 的值只是举了个例子,描述了当前客户端是一个主节点的 server(当进行主从节点复制的时候,主节点会做为客户端链接从节点发送 RDB 文件给客户端),又正在执行 MONITOR 命令。前者描述了客户端角色,后者描述客户端状态。
总而言之,redis 客户端 flags 字段能够描述当前客户端的角色,也能够记录当前客户端各类状态信息,是服务端了解客户端信息的一个很是重要的字段。
三、输入/输出缓冲区
redis 服务端收到客户端发来的命令请求须要不少步骤来处理和调用相关命令的实现,并最终将数据返回给客户端,那么输入缓冲区其实就是一小块内存,用于存储客户端发送过来的命令,包括参数,这块内存空间默认不能超过 1GB,不然 redis 服务端就会强制关闭与该客户端的链接。
typedef struct client { ......... sds querybuf; /* Buffer we use to accumulate client queries. */ ......... } client;
querybuf 就是客户端缓冲区,它是一个 SDS 类型的字段,那么说明这是一个能够动态扩充输入缓冲区。
固然咱们也能够经过 client list 看看当前客户端的的 querybuf 分配和使用状况。
其中 qbuf 和 qbuf-free 用于描述客户端输入缓冲区状态。我这里的这个没有写入过大的命令,因此这里的 querybuf 只分配了 32768 个字节。
ps:尽可能不要使用过大的 KEY,这样会致使客户端 querybuf 占用过多内存,这样会致使 redis 服务端程序占用太高内存,若是超过 maxmemory 限制,会触发 KEY 的 LRU 淘汰或程序异常。
除此以外,redis 客户端还有一个输出缓冲区,用于缓存服务端响应的回复。
输出缓冲区有两种,一种是固定大小的,用于存储服务端简单的响应,例如:OK,错误信息等。还有一种是非固定长度的缓冲区,它的长度是可动态扩展的,用于存储一些较长的响应内容。
typedef struct client { ......... /* Response buffer */ int bufpos; char buf[PROTO_REPLY_CHUNK_BYTES]; ......... } client;
PROTO_REPLY_CHUNK_BYTES 等于 16*1024,也就是默认固定输出缓冲区只有 16K,bufpos 记录当前固定缓冲区已经使用的字节数。
typedef struct client { ......... list *reply; /* List of reply objects to send to the client. */ ......... } client;
动态缓冲区用链表实现,能够为咱们返回较大的 key,例如一些 set、list 集合等等。咱们能够经过 client list 命令查看输出缓冲区的使用状况。
obl 表示固定缓冲区长度,oll 表明动态缓冲区长度,omem 表示固定缓冲区和动态缓冲区总共占用了多少字节。
ps:输出缓冲区能够经过配置 client-output-buffer-limit 限制最大内存上限,一样若是滥用,同样会致使 redis 服务器内存飙升,建议尽可能配置小一点的输出缓存区大小。
redis 客户端主要分为三种,普通客户端、发布订阅客户端、slave 客户端。普通客户端咱们不用多说,也是咱们用的最多的客户端。
redis 客户端能够订阅任意数量的频道,若是你订阅了某个服务器频道,那么你的客户端就也是一个发布订阅客户端,当频道中有新消息,服务器会向你推送,关于 redis 的发布订阅功能,咱们后续会详细介绍。
当 redis 集群中部署了主从节点的时候,全部的从节点服务器又称为 slave 客户端,他们会向 master 按期拉取最新数据,详细的内容后续介绍。
下一篇咱们分析 redis 的服务端程序实现,以及神秘的 serverCron 定时函数实现。