Redis教程

Redis客户端

本文主要是介绍Redis客户端,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Redis 是一个客户端服务端的程序,服务端提供数据存储等等服务,客户端连接服务端并通过向服务端发送命令,读取或写入数据,简单来说,客户端就是某种工具,我们通过它与 Redis 服务端进行通讯并完成数据操作。

客户端并不是 Redis 的核心,Redis 的核心是它的服务端程序,服务端程序才是完成数据存、取,持久化等等我们使用频繁的各种操作的执行者。但也不是说客户端就没什么作用,客户端在整个 Redis 服务体系中也是非常重要的一环。

Redis服务器是一个典型的一对多服务器程序:一个服务器可以与多个客户端建立网络连接,每个客户端可以向服务器发送命令请求,而服务器则接受并处理客户端发送的命令请求,并向客户端返回命令回复。通过使用由I/O多路复用技术实现的文件事件处理器,Redis服务器使用单线程单进程的方式来处理命令请求,并与多个客户端进行网络通信。

服务器状态结构的clients属性是一个链表,这个链表保存了所有与服务器连接的客户端的状态结构,对客户端执行批量操作,或者查找某个指定的客户端,都可以通过遍历clients链表来完成:

struct redisServer{
    //,.,
    // 一个链表,保存了所有客户端状态
    list * clients;
}

 

 

一、客户端的基本属性

redis 中为客户端抽象的数据结构是,server.h/client 结构,我这里是 redis-4.0.x 版本,不同版本或许稍有不同,每一个 redis 客户端成功的连接上服务端之后,服务端就会创建一个 client 结构实例,并以链表的形式链接所有连接成功的客户端。

这个结构最主要作用就是存储当前客户端的大量属性,套接字、名字、标志,状态等等信息,这些信息非常的重要,当服务端为客户端服务时,很多的信息例如当前要执行的命令、参数都会从这里获取。我们一个一个来了解。

1、客户端名称

默认情况下,所有连接成功的客户端都是没有名字的,这一点你可以通过向服务发送 client list 命令验证,它会返回当前服务端成功建立的客户端以及他们的基本信息。例如:

可以看到,name 字段默认是空,如果你想让你的客户端辨识度更高,你可以向服务端发送 client setname 为你的客户端命名,这里我就不做演示了,客户端名称这个信息保存在 client 结构中的 name 字段里。

typedef struct client {
    .........
    robj *name;             /* As set by CLIENT SETNAME. */           
    .........
} client;

 

2、套接字

客户端套接字由客户端状态的 fd 属性记录

当 fd 属性值为-1 时,表示这个客户端是伪客户端。伪客户端的请求命令不是来源于网络的,而是来源于 Lua 脚本或 AOF 文件(后续详细介绍)的,所以伪客户端不需要套接字连接,它也没有套接字描述符。当我们执行的 Lua 脚本中含有 Redis 命令,或者使用 AOF 文件来还原数据库状态时,就会用到伪客户端。

当 fd 属性值是大于-1 的整数时,表示这个客户端是普通客户端。普通客户端采用相关套接字来实现与服务器的通信,因此服务器会利用 fd 属性来记录客户端套接字的描述符。

3、标志属性

标志用于描述当前 redis 客户端的一些状态或者角色,对应的到数据结构中就是一个整型字段。

typedef struct client {
    .........
    int flags;              /* Client flags: CLIENT_* macros. */
    .........
} client;

 

Redis 中定义了很多的客户端标志,客户端的标志属性 flags 用来记录客户端的角色(Role)及客户端目前所处的状态。

flags 属性的取值可以是单个标志,也可以是多个二进制或的组合标志,具体如下。

单个标志:flags=<flag>

组合标志:flags=<flag1>|<flag2>|<flag3>|…

标志使用常量来表示。Redis 所具有的所有标志都定义在 redis.h 文件中。

记录客户端角色的标志有如下几个:

  • 在利用 Redis 主从服务器实现复制时,主从服务器会相互成为对方的客户端,也就是从服务器是主服务器的客户端,同时主服务器也是从服务器的客户端。Redis 使用REDIS_MASTER 标志来表示这个客户端是主服务器,而使用 REDIS_SLAVE 标志来表示另一个客户端是从服务器。
  • Redis 使用 REDIS_LUA_CLIENT 标志来表示该客户端是一个专门用于处理 Lua 脚本的伪客户端,它主要用于执行 Lua 脚本中包含的 Redis 命令。
  • Redis 使用 REDIS_PRE_PSYNC 标志来表示该客户端是一个低于 Redis 2.8 版本的从服务器,此时,对应的主服务器不能使用 PSYNC 命令实现与从服务器的数据同步。只有当 REDIS_SLAVE 标志处于打开状态时,才能使用 REDIS_PRE_PSYNC 标志。

记录客户端当前状态的标志有如下几个:

  •  REDIS_ASKING 标志表示客户端向运行在集群模式下的服务器节点发送了 ASKING 命令。
  •  REDIS_CLOSE_ASAP 标志表示客户端的输出缓冲区过大,超出了服务器所允许的范围。当服务器在下一次执行 serverCron 函数时,会关闭这个输出缓冲区过大的客户端,以此来保证服务器的稳定性不受这个客户端影响。在关闭的时候,存储在这个缓冲区中的数据也会被删除,并且不会给客户端返回任何信息。
  •  REDIS_CLOSE_AFTER_REPLY 标志表示客户端给服务器发送的命令请求中有错误的协议内容,或者用户在客户端中执行了 CLIENT kill 命令。此时服务器会将客户端输出缓冲区中存储的所有数据内容发送给客户端,然后关闭这个客户端。
  •  REDIS_DIRTY_CAS 标志表示事务使用 WATCH 命令监视的数据库键已经被修改。
  •  REDIS_DIRTY_EXEC 标志表示事务在命令入队时出现错误。

REDIS_DIRTY_CAS 和 REDIS_DIRTY_EXEC 标志的出现都表示 Redis 事务的安全性已被破坏。只要这两个标志中的任何一个被打开,EXEC 命令都会执行失败。而只有在客户端打开了 REDIS_MULTI 标志的情况下,才能使用这两个标志。

  •  REDIS_MULTI 标志表示客户端正处于执行事务的状态中。
  •  REDIS_MONITOR 标志表示客户端正处于执行 MONITOR 命令的状态中。
  •  REDIS_FORCE_AOF 标志表示让服务器将当前正在执行的命令强制写入 AOF 文件中。在执行 PUBSUB 命令时,会使客户端打开 REDIS_FORCE_AOF 标志。
  •  REDIS_FORCE_REPL 标志表示强制让主服务器将当前正在执行的命令复制给所有与它连接的从服务器。当执行 SCRIPT LOAD 命令时,会使客户端同时开启 REDIS_FORCE_AOF 和 REDIS_FORCE_REPL 标志。如果要实现主从服务器可以正确地载入 SCRIPT LOAD 命令指定的脚本,那么服务器必须使用 REDIS_FORCE_REPL 标志,让主服务器强制将 SCRIPT LOAD 命令分发给相应的从服务器。
  •  REDIS_UNIX_SOCKET 标志表示服务器连接客户端使用的是 UNIX 套接字。
  •  REDIS_BLOCKED 标志表示客户端正处于被 BRPOP、BLPOP 等命令阻塞的状态中。
  •  REDIS_UNBLOCKED 标志表示客户端不再阻塞,它从 REDIS_BLOCKED 标志的阻塞状态中脱离出来。只有在 REDIS_BLOCKED 标志被打开的情况下,才能使用 REDIS_UNBLOCKED 标志。
  •  REDIS_MASTER_FORCE_REPLY 标志:在主从服务器进行命令交互的过程中,从服务器需要向主服务器发送 REPLICATION ACK 命令。但是,在发送此命令之前,从服务器必须开启主服务器对应的客户端的 REDIS_MASTER_FORCE_REPLY 标志;否则主服务器会拒绝执行从服务器发送的 REPLCATION ACK 命令。

当然了,上面那个 flages 的值只是举了个例子,描述了当前客户端是一个主节点的 server(当进行主从节点复制的时候,主节点会作为客户端连接从节点发送 RDB 文件给客户端),又正在执行 MONITOR 命令。前者描述了客户端角色,后者描述客户端状态。

总而言之,redis 客户端 flags 字段可以描述当前客户端的角色,也可以记录当前客户端各种状态信息,是服务端了解客户端信息的一个非常重要的字段。

4、时间属性

  •  ctime 属性:该属性记录了客户端被创建的时间。利用这个时间可以计算出这个客户端与服务器相连接的时间,单位为秒。在执行 CLIENT list 命令后,返回的 age 域记录了连接秒数
  •  lastinteraction 属性:该属性记录了客户端与服务器最后一次交互的时间。交互就是两者之间互相发送命令请求与返回结果。利用 lastinteraction 属性可以计算出客户端的空转时间,也就是在进行最后一次交互之前过去了多少时间,单位为秒。CLIENT list 命令返回的 idle 域记录了这个时间。当 idle 的值为 0 时,表示空转时间为 0 秒。
  •  obuf_soft_limit_reached_time 属性:该属性记录了客户端输出缓冲区第一次达到软性限制的时间

二、输入/输出缓冲区

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 命令查看输出缓冲区的使用情况。

image

obl 表示固定缓冲区长度,oll 代表动态缓冲区长度,omem 表示固定缓冲区和动态缓冲区总共占用了多少字节。

ps:输出缓冲区可以通过配置 client-output-buffer-limit 限制最大内存上限,同样如果滥用,一样会导致 redis 服务器内存飙升,建议尽量配置小一点的输出缓存区大小。

二、客户端三种类型

redis 客户端主要分为三种,普通客户端、发布订阅客户端、slave 客户端。普通客户端我们不用多说,也是我们用的最多的客户端。

redis 客户端可以订阅任意数量的频道,如果你订阅了某个服务器频道,那么你的客户端就也是一个发布订阅客户端,当频道中有新消息,服务器会向你推送,关于 redis 的发布订阅功能,我们后续会详细介绍。

当 redis 集群中部署了主从节点的时候,所有的从节点服务器又称为 slave 客户端,他们会向 master 定期拉取最新数据,详细的内容后续介绍。

下一篇我们分析 redis 的服务端程序实现,以及神秘的 serverCron 定时函数实现。

 

 https://www.cnblogs.com/undefined22/p/12580818.html

这篇关于Redis客户端的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!