写作背景:本文基于Redis 6.0.9版本,前提至少 Redis 3.0或更高版本。
目录
1.Redis客户端处理
本文档从网络层的角度提供有关Redis如何处理客户端的信息:此处介绍了连接,超时,缓冲区和其他类似主题。
本文档中包含的信息仅适用于Redis 2.6或更高版本。
1.1.如何接受客户端连接
Redis在已配置的侦听TCP端口和Unix套接字(如果已启用)上接受客户端连接。 当接受新的客户端连接后,将执行以下操作:
- 由于Redis使用多路复用和非阻塞I/O,因此客户端套接字处于非阻塞状态。
- 设置TCP_NODELAY选项是为了确保我们的连接没有延迟。
- 创建一个可读文件事件,以便Redis能够在套接字上读取新数据后立即收集客户端查询。
初始化客户端之后,Redis会检查我们是否已处于可以同时处理的客户端数量的限制(使用maxclients配置指令进行配置,有关更多信息,请参见本文档的下一部分)。
如果由于已经接受了最大数量的客户端而无法接受当前客户端,Redis会尝试向客户端发送错误以使其了解此情况,并立即关闭连接。 即使Redis立即关闭了连接,错误消息也将能够到达客户端,因此内核将处理错误的传输。因为新的套接字输出缓冲区通常足够大以包含错误。
1.2.以什么顺序客户端被服务
该顺序由客户端套接字文件描述符号和内核报告事件的顺序共同决定,因此该顺序应视为未指定。
但是,Redis在为客户端提供服务时会做以下两件事:
- 每当确保从客户端套接字读取新内容时,它仅执行一次
read()
系统调用,以确保如果我们连接了多个客户端,并且有几个要求很高的客户端以高速率发送查询,其他客户端不会受到惩罚,也不会遇到延迟问题。 - 但是,一旦从客户端读取了新数据,将依次处理当前缓冲区中包含的所有查询。 这样可以提高局部性,并且不需要再次进行迭代来查看是否存在需要一些处理时间的客户端。
1.3.最大客户数
在Redis 2.4中,对可以同时处理的最大客户端数量有硬编码限制。
在Redis 2.6中,此限制是动态的:默认情况下,它设置为10000个客户端,除非Redis.conf中的maxclients指令另有说明。
但是,Redis向内核检查我们能够打开的最大文件描述符数量(检查软限制)。 如果该限制小于我们要处理的最大客户端数量加上32(即Redis保留供内部使用的文件描述符数量),则Redis会修改最大客户端数量以匹配我们的客户端数量,该情况下确实能够在当前操作系统的限制下处理。
如果无法使用已配置的最大客户端数,则在启动时记录该条件,如下例所示:
$ ./redis-server --maxclients 100000
[41422] 23 Jan 11:28:33.179 # Unable to set the max number of files limit to 100032 (Invalid argument), setting the max clients configuration to 10112.
当配置Redis以处理特定数量的客户端时,最好确保也相应地设置了操作系统对每个进程的最大文件描述符数量的限制。
在Linux下,可以使用以下命令在当前会话中和系统范围内设置这些限制:
- ulimit -Sn 100000 # 只有在硬限制足够大的情况下,这才起作用
- sysctl -w fs.file-max=100000
1.4.输出缓冲区限制
Redis需要为每个客户端处理可变长度的输出缓冲区,因为命令会产生大量需要传输到客户端的数据。
但是,客户端可能会发送更多命令,从而产生更多输出,从而以更快的速度提供服务,而Redis可以将现有输出发送给客户端。 如果客户端不能足够快地处理新消息,则对于Pub/Sub客户端尤其如此。
两种情况都将导致客户端输出缓冲区增加并消耗越来越多的内存。 因此,默认情况下,Redis为不同类型的客户端设置对输出缓冲区大小的限制。 达到限制后,客户端连接将关闭,该事件将记录在Redis日志文件中。
Redis使用有两种限制:
- 硬限制是一个固定的限制,当达到该限制时,Redis会尽快关闭客户端连接。
- 软限制是取决于时间的限制,例如,软限制为每10秒32兆字节,这意味着如果客户端连续10秒钟具有大于32兆字节的输出缓冲区,则连接将关闭。
不同种类的客户端具有不同的默认限制:
- 普通客户端的默认限制为0,这意味着根本没有任何限制,因为大多数普通客户端使用阻塞实现来发送单个命令,并在发送下一个命令之前等待回复被完全读取,因此始终不希望这样做。 如果是普通客户端,请关闭连接。
- 发布/订阅客户端的默认硬限制为32 MB,软限制为每60秒8 MB。
- 从节点的默认硬限制为256 MB,每60秒的软限制为64 MB。
可以在运行时使用CONFIG SET命令更改限制,也可以使用Redis配置文件redis.conf永久更改限制。 有关如何设置限制的更多信息,请参见Redis分发中的示例redis.conf。
1.5.查询缓冲区硬限制
每个客户端还受到查询缓冲区限制。 这是一个不可配置的硬限制,它将在客户端查询缓冲区(即我们用来累积来自客户端的命令的缓冲区)达到1 GB时关闭连接,并且实际上只是一个极端限制,在客户端或服务器软件错误的情况下,以避免万一服务器崩溃。
1.6.客户端超时
默认情况下,如果客户端空闲很多秒,Redis的最新版本不会关闭与客户端的连接:连接将永远保持打开状态。
但是,如果你不喜欢此行为,则可以配置超时,以便如果客户端空闲超过指定的秒数,则客户端连接将关闭。
你可以通过redis.conf或仅使用 CONFIG SET timeout <value>
来配置此限制。
请注意,超时仅适用于普通客户端,而不适用于Pub/Sub客户端,因为Pub/Sub连接是推式连接,因此处于空闲状态的客户端是常态。
即使默认情况下连接不受超时限制,设置超时还是有两个条件:
- 客户端软件中的错误可能会使Redis服务器的空闲连接饱和,从而导致任务中断的关键任务应用程序。
- 作为一种调试机制,如果客户端软件中的错误使服务器的空闲连接饱和,从而能够与服务器连接,则无法与服务器进行交互。
超时不被认为是非常精确的:Redis避免设置计时器事件或运行O(N) 算法来检查空闲客户端,因此该检查会不时地递增执行。 这意味着,如果将超时设置为10秒,则可能会关闭客户端连接,例如,如果同时连接许多客户端,则在12秒后将关闭。
1.7.客户端(CLIENT)命令
Redis client命令允许检查每个已连接客户端的状态,杀死特定的客户端,并为连接设置名称。 如果你大规模使用Redis,它是一个非常强大的调试工具。
CLIENT LIST用于获得已连接客户端及其状态的列表:
redis 127.0.0.1:6379> client list
addr=127.0.0.1:52555 fd=5 name= age=855 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
addr=127.0.0.1:52787 fd=6 name= age=6 idle=5 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping
在上面的示例会话中,两个客户端连接到Redis服务器。 一些最有趣的字段的含义如下:
- addr: 客户端地址,即客户端IP和用于与Redis服务器连接的远程端口号。
- fd: 客户端套接字文件描述符号。
- name: 客户端名称由CLIENT SETNAME设置。
- age: 连接存在的秒数。
- idle: 连接空闲的秒数。
- flags: 客户端类型(N表示普通客户端,请检查标志的完整列表)。
- omem: 客户端用于输出缓冲区的内存量。
- cmd: 最后执行的命令。
有关字段的完整列表及其含义,请参见“CLIENT LIST”文档。
获得客户端列表后,你可以使用CLIENT KILL 命令(将客户端地址指定为参数)轻松关闭与客户端的连接。
CLIENT SETNAME和CLIENT GETNAME命令可用于设置和获取连接名称。 从Redis 4.0开始,客户端名称显示在SLOWLOG输出中,因此可以更轻松地识别造成延迟问题的客户端。
1.8.TCP存活检查
最新版本的Redis(3.2或更高版本)默认情况下启用TCP keepalive(SO_KEEPALIVE
套接字选项),并将其设置为约300秒。 此选项对于检测死对等体(即使已连接的客户端也无法访问)很有用。 此外,如果客户端和服务器之间有网络设备需要查看一些流量才能打开连接,则该选项将防止意外的连接关闭事件。