redis 系列19 客户端
一. 概述
redis服务器是可以与多个客户端建立网络连接,每个客户端可以向服务器发送命令请求,而服务器则接收并处理客户端发送的命令请求,并向客户端返回命令回复。通过使用i/o多路复用技术实现的文件事件处理器,redis服务器使用单进程单线程的方式来处理命令请求,并与多个客户端进行网络通信。
1.1 伪客户端和普通客户端
对于每个与服务器进行连接的客户端,服务器都为这些客户端建立了相应的redis.h/redisclient结构(客户端状态),这个结构保存了客户端当前的状态信息。根据客户端类型的不同,分为伪客户端和普通客户端。伪客户端处理的命令请求来源于aof文件或者lua脚本,而不是网络,这种客户端不需要套接字连接。普通客户端使用套接字与服务器进行通信,下面脚本显示了全部的普通客户端:
127.0.0.1:6379> client list id=2 addr=127.0.0.1:45090 fd=8 name= age=95 idle=0 flags=n db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client id=3 addr=127.0.0.1:45092 fd=9 name= age=3 idle=3 flags=n db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=command
上面脚本,当fd为-1时,就是一个伪客户端。普通客户端的fd都大于0。name代表客户端名称,默认为空,可以用client setname命令来设置一个客户端的名字,例如给当前客户端设置名称,以下脚本:
127.0.0.1:6379> client setname client1 ok 127.0.0.1:6379> client list id=2 addr=127.0.0.1:45090 fd=8 name=client1 age=414 idle=0 flags=n db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0
events=r cmd=client id=3 addr=127.0.0.1:45092 fd=9 name= age=322 idle=322 flags=n db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0
events=r cmd=command
1.2 flags标志
flags记录了客户端的角色(role),以及客户端目前所处的状态,flags属性的值可以是单个标志,也可以是多个标志的二进制。客户端 flag 可以由以下部分组成:
属性值 |
描述 |
o |
客户端是 monitor 模式下的附属节点(slave) |
s |
客户端是一般模式下(normal)的附属节点 |
m |
客户端是主节点(master) |
x |
客户端正在执行事务 |
b |
客户端正在等待阻塞事件 |
i |
客户端正在等待 vm i/o 操作(已废弃 |
c |
在将回复完整地写出之后,关闭链接 |
u |
客户端未被阻塞(unblocked) |
n |
未设置任何 flag |
r |
客户端套接字(在事件 loop 中)是可读的(readable) |
w |
客户端套接字(在事件 loop 中)是可写的(writeable) |
1.3 输入缓冲区qbuf 与输出缓冲区 obl
(1)客户端状态的输入缓冲区用于保存客户端发送的命令请求,如set key value, 输入缓冲区的大小会根据输入内容动态地缩小或扩大,但它的最大空间不超过1gb,否则服务器将关闭这个客户端。
(2)当服务端执行命令回复,会被保存在客户端状态的输出缓冲区中,每个客户端都有两个输出缓冲区可用,一个缓冲区大小是固定的,另一个缓冲区的大小是可变的。固定大小的缓冲区用于保存回复信息长度比较小的,比如ok,简短的字符串值,整数值,错误回复等。可变大小的缓冲区用于保存长度比较大的,比如一个非常长的字符串值,或一个由很多项组成的列表,或一个包含了很多元素的集合等。 固定大小的缓冲区默认为16kb。可变大小缓冲区不能超过服务器设置的硬性限制值,本篇最后client-output-buffer-limit时有讲。 omem表示输出缓存的内存占用量。
1.4 身份验证
客户端状态的authenticated属性用于记录客户端是否通过了身份验证,当redisclinet结构下的authenticated属性为值0时,代表客户端未通过身份验证,安装redis服务后,默认是没有密码,也就是不需要身份验证,通过设置config set requirepass xxx 来设置密码。通过auth xx来验证密码是否正确。下面脚本是设置密码和验证密码是否正确:
127.0.0.1:6379> config set requirepass 123456 ok 127.0.0.1:6379> auth 123456 ok
当没有通过验证时,使用ping命令显示信息如下, 退出客户端,重启登录使用-a 参数输入密码,再次使用ping命令,验证成功,如下脚本:
127.0.0.1:6379> ping (error) noauth authentication required. [root@xuegod64 ~]# redis-cli -a 123456 127.0.0.1:6379> ping pong
1.5 时间
客户端还有几个和时间有关的属性,一是age属性,它记录了创建客户端的时间,这个时间可以用来计算客户端与服务器已经连接了多少秒。二是idle空转时间,即客户端与服务器最后一次进行互动以来,已经过去了多少秒。
1.6 客户端的创建与关闭
如果客户端是通过网络连接与服务器进行连接的普通客户端,那么在客户端使用connect函数连接到服务器时,服务器就会调用连接事件处理器(上篇简单讲到的文件事件处理器),为客户端创建相应的客户端状态,并将这个新状态添加到服务器状态结构reddisserver结构的clients数组链表的末尾。对于关闭普通客户端,人为可以使用clinet kill命令,来关闭,如下脚本所示:
127.0.0.1:6379> client list id=4 addr=127.0.0.1:45094 fd=8 name= age=6141 idle=0 flags=n db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client id=6 addr=127.0.0.1:45098 fd=9 name= age=5966 idle=22 flags=n db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=client 127.0.0.1:6379> client kill id 6 (integer) 1 127.0.0.1:6379> client list id=4 addr=127.0.0.1:45094 fd=8 name= age=6179 idle=0 flags=n db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client
如果用户为服务器设置了timeout选项,那么当客户端的空转时间(idle)超过timeout选项设置的值时,客户端将关闭。如果输入缓冲区占用空间超过1gb时,客户端将关闭。
对于输出缓冲区的大小,达到限制时,客户端也将关闭。为了避免客户端的回复过大,占用过多的服务器资源,服务器会时刻检查客户端的输出缓冲区的大小,并在缓冲区的大小超出范围时,执行相应的限制操作。服务器使用两种模式来限制客户端输出缓冲区的大小:
(1) 硬性限制(hard limit):如果输出缓冲区的大小超过了硬性限制所设置的大小,那么服务器立即关闭客户端。
(2) 软性限制(soft limit): 如果输出缓冲区的大小超出了软性限制所设置的大小,并且占用的时长大于> 设置的总时长,那么服务器将关闭客户端。
使用client-output-buffer-limit选项可以为普通客户端,从服务器客户端,执行发布订阅功能的客户端分别设置不同的软性限制和硬性限制,默认设置如下:
1) "client-output-buffer-limit" 2) "normal 0 0 0 slave 268435456 67108864 60 pubsub 33554432 8388608 60"
normal是普通客户端:软性限制和硬性限制都没有限制。
slave是从服务器客户端:硬性限制是268435456, 软性限制是67108864 并且时长为60秒。
pubsub是发布订阅功能的客户端: 硬性限制是33554432, 软性限制是8388608并且时长为60秒。
1.7 伪客户端的创建与关闭
对于lua脚本的伪客户端,在服务器初始化时创建,这个伪客户端在服务器运行的整个生命期中会一直存在,只有服务器关闭时,这个客户端才会被关闭。
对于aof文件的伪客户端,服务器在载入aof文件时,会创建伪客户端,并在载入完成之后,关闭这个伪客户端。