结合redis设计与实现的redis源码学习-19.1-客户端

Redis服务器是典型的一对多服务程序:一个服务器可以与多个客户端建立网络连接,每个客户单可以向服务器发送命令请求,而服务器则接收并处理客户端发送的命令请求,并向客户端返回命令回复。
通过使用I/O多路复用技术实现的文件处理器,Redis服务器使用单线程单进程的方式来处理命令请求,并与多个客户端进行网络通信。
对于每个与服务器进行连接的客户端,服务器使用redisClient结构来保存客户端当前的状态信息,及执行相关功能时需要用到的数据结构:

/* With multiplexing we need to take per-client state. Clients are taken in a linked list. 多路复用下,我们需要采取每个客户端的状态,保存到list中*/
typedef struct client {
    uint64_t id;            /* Client incremental unique ID. 客户端id*/
    int fd;                 /* Client socket. 套接字描述符*/
    redisDb *db;            /* Pointer to currently SELECTed DB. 指向当前使用的数据库*/
    int dictid;             /* ID of the currently SELECTed DB. 当前数据库的id*/
    robj *name;             /* As set by CLIENT SETNAME. 客户端名称*/
    sds querybuf;           /* Buffer we use to accumulate client queries. 积累客户端查询缓冲区*/
    size_t querybuf_peak;   /* Recent (100ms or more) peak of querybuf size. 最近请求的峰值*/
    int argc;               /* Num of arguments of current command. 当前命令的参数数量*/
    robj **argv;            /* Arguments of current command. 当前命令的参数*/
    struct redisCommand *cmd, *lastcmd;  /* Last command executed. 最后一个执行的命令*/
    int reqtype;            /* Request protocol type: PROTO_REQ_* 请求协议类型*/
    int multibulklen;       /* Number of multi bulk arguments left to read. 要读取多个批量参数的数量*/
    long bulklen;           /* Length of bulk argument in multi bulk request. 多批量请求中参数的长度*/
    list *reply;            /* List of reply objects to send to the client. 返回客户单的相应链表*/
    unsigned long long reply_bytes; /* Tot bytes of objects in reply list. 在回复列表中显示对象的字节数*/
    size_t sentlen;         /* Amount of bytes already sent in the current buffer or object being sent. 在当前正在发送的缓冲区或对象中已经发送的字节数*/
    time_t ctime;           /* Client creation time. 客户端创建时间*/
    time_t lastinteraction; /* Time of the last interaction, used for timeout 最后一次交互的时间*/
    time_t obuf_soft_limit_reached_time;
    int flags;              /* Client flags: CLIENT_* macros.标志值 */
    int authenticated;      /* When requirepass is non-NULL. 身份验证标志*/
    int replstate;          /* Replication state if this is a slave. 复制标志*/
    int repl_put_online_on_ack; /* Install slave write handler on ACK. 安装从属写处理器*/
    int repldbfd;           /* Replication DB file descriptor. */
    off_t repldboff;        /* Replication DB file offset. */
    off_t repldbsize;       /* Replication DB file size. */
    sds replpreamble;       /* Replication DB preamble. */
    long long reploff;      /* Replication offset if this is our master. */
    long long repl_ack_off; /* Replication ack offset, if this is a slave. */
    long long repl_ack_time;/* Replication ack time, if this is a slave. */
    long long psync_initial_offset; /* FULLRESYNC reply offset other slaves copying this slave output buffer should use. 回复其他从机复制这个从机输出缓冲区时使用*/
    char replrunid[CONFIG_RUN_ID_SIZE+1]; /* Master run id if is a master. 主机id*/
    int slave_listening_port; /* As configured with: REPLCONF listening-port */
    char slave_ip[NET_IP_STR_LEN]; /* Optionally given by REPLCONF ip-address */
    int slave_capa;         /* Slave capabilities: SLAVE_CAPA_* bitwise OR. */
    multiState mstate;      /* MULTI/EXEC state 事务状态*/
    int btype;              /* Type of blocking op if CLIENT_BLOCKED. */
    blockingState bpop;     /* blocking state */
    long long woff;         /* Last write global replication offset. 最后写的全局复制偏移量*/
    list *watched_keys;     /* Keys WATCHED for MULTI/EXEC CAS 事务中观察的key*/
    dict *pubsub_channels;  /* channels a client is interested in (SUBSCRIBE) 订阅的通道*/
    list *pubsub_patterns;  /* patterns a client is interested in (SUBSCRIBE) 订阅的模式*/
    sds peerid;             /* Cached peer ID. 缓存对等ID*/

    /* Response buffer响应缓冲区*/
    int bufpos;
    char buf[PROTO_REPLY_CHUNK_BYTES];
} client;

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

一、客户端属性

-1、比较通用的属性,这些属性很少与特定的功能相关,无论客户端执行的是什么工作,他们都要用到这个属性;
-2、和特定功能相关的属性,比如操作数据库的db属性和dictid属性等。

1、套接字描述符

根据客户单类型的不同,fd的属性可以是-1,或者大于-1的整数:
-1、伪客户端的fd属性为-1:来源于AOF文件或者Lua脚本,而不是网络,所以不需要套接字连接。
-2、普通客户端的fd值大于-1的整数:需要使用网络套接字与服务器通信,所以服务器使用fd来记录客户端套接字的描述符。

2、名字

在默认情况下,一个连接到服务器的客户端是没有名字的。
使用CLIENT setname命令可以为客户端设置一个名字来标志客户端。如果不设置,name属性指向NULL。

3、标志

flags记录了客户端的角色,以及状态。使用每个二进制位来表示不同的角色和状态。

4、输入缓冲区

querybuf用于保存客户端发送的命令请求,它的大小会根据输入内容动态地缩小或者扩大,但它的最大大小不能超过1GB,否则服务器将关闭客户端。

5、命令与命令参数

在服务器将客户端发送的命令请求保存到客户端状态的querybuf后,会对命令请求进行分析,并将得出的命令参数及个数保存到argv和argc中。

6、命令的实现函数

当服务器解析出argv和argc后,将根据argv[0]的值,在命令表中查找命令所对应的命令实现函数。当找到所对应的RedisCommand结构时,他会将客户端状态的cmd指针指向这个结构。

7、输出缓冲区

执行命令所得到的回复会保存在客户端状态的输出缓冲区中,每个客户端都有两个输出缓冲区可用,一个缓冲区大小是固定的,另一个是可变的:
-1、固定大小的缓冲区用于保存那些长度较小的回复。
-2、可变大小的缓冲区用于保存那些长度比较大的回复。
buf是一个REDIS_REPLY_CHUNK_BYTES字节唱的数组,bufpos记录了buf数组目前已经使用的数量。

8、身份验证

authenticated用于记录客户端是否通过了身份验证。为0表示未通过,为1表示通过了。
如果服务器没有开启身份验证,那么即使authenticated为0,也不会拒绝执行命令。

9、时间

ctime记录了客户端创建的时间,用来计算客户端已经连接了多少秒。
lastinteraction记录了客户端最后一次与服务器进行交互的时间,用来计算客户端的空转时间。
obuf_soft_limit_reached_time记录了输出缓冲区第一次达到软限制的时间。

二、客户端的创建与关闭

1、创建普通客户端

如果客户端通过网络连接与服务器进行连接,在连接时就会创建相应的客户端状态,并加入到clients链表中。

2、关闭普通客户端

关闭原因:
-1、客户端进程退出或者被干掉,网络连接会被关闭,从而造成客户端被关闭;
-2、如果客户端向服务器发送了带有不符合协议格式的命令请求,会被服务器关闭;
-3、客户端成为了client kill的目标,会被关闭;
-4、服务器设置了timeout选项,超时后会被关闭,chufei使用了BLPOP命令阻塞,或者正在执行订阅命令。
-5、客户端发送的命令请求大小超过了限制大小;
-6、发送给客户端的命令回复超过了输出缓冲区大小。
为了避免客户端回复过大,占用过多的资源,所以服务器会检查客户端输出缓冲区的大小,并在超出范围时,执行相应的限制操作。
限制模式:
-1、硬性限制:超出则立即关闭;
-2、软性限制:如果超过了软限制而没有超过硬限制,那么会记录下客户端到达软限制的时间,如果一定时间内一直超过软限制,就会被关闭。

4、Lua脚本伪客户端

lua_client在服务器运行时会一直存在,只有在服务器关闭时才会被关闭。

5、AOF文件伪客户端

在载入AOF文件时,会创建用于执行AOF文件包含的Redis命令的伪客户端,在载入完成后,会被关闭。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值