Redis 客户端处理
Redis 服务器如何管理客户端连接
本文档提供了有关 Redis 如何在网络层级处理客户端的信息:连接、超时、缓冲区和其他类似主题在此处介绍。
本文档中包含的信息仅适用于**Redis 2.6 或更高版本**。
接受客户端连接
Redis 在配置的 TCP 端口和(如果启用)Unix 套接字上接受客户端连接。当接受新的客户端连接时,将执行以下操作
- 客户端套接字处于非阻塞状态,因为 Redis 使用多路复用和非阻塞 I/O。
- 设置
TCP_NODELAY
选项以确保连接没有延迟。 - 创建一个可读文件事件,以便 Redis 能够在套接字上可读新数据时立即收集客户端查询。
客户端初始化后,Redis 检查是否已达到为同时客户端数量配置的限制(使用maxclients
配置指令配置,有关更多信息,请参见本文档的下一节)。
当 Redis 无法接受新的客户端连接,因为已达到最大客户端数量时,它会尝试向客户端发送错误消息,以便使其了解此情况,并立即关闭连接。即使 Redis 立即关闭连接,错误消息也会传达给客户端,因为新的套接字输出缓冲区通常足够大,可以容纳错误,因此内核将处理错误的传输。
客户端请求按什么顺序处理?
顺序由客户端套接字文件描述符编号和内核报告事件的顺序决定,因此顺序应视为未指定。
但是,Redis 在为客户端提供服务时会执行以下两件事
- 每次从客户端套接字中读取到新内容时,它只执行一次
read()
系统调用。这样可以确保如果有多个客户端连接,并且一些客户端以高频率发送查询,其他客户端不会受到惩罚,也不会遇到延迟问题。 - 但是,一旦从客户端读取到新数据,就会按顺序处理当前缓冲区中包含的所有查询。这提高了局部性,并且不需要第二次迭代来查看是否有需要一些处理时间的客户端。
最大并发连接的客户端
在 Redis 2.4 中,对可以同时处理的客户端数量存在硬编码限制。
在 Redis 2.6 及更高版本中,此限制可以使用 redis.conf
中的 maxclients
指令进行配置。默认值为 10,000 个客户端。
但是,Redis 会检查内核,查看我们能够打开的最大文件描述符数量(检查软限制)。如果限制小于我们想要处理的客户端的最大数量加上 32(这是 Redis 为内部使用保留的文件描述符的数量),那么客户端的最大数量将更新为与它在当前操作系统限制下真正能够处理的客户端数量相匹配。
当 maxclients
设置为大于 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
输出缓冲区限制
Redis 需要为每个客户端处理一个可变长度的输出缓冲区,因为命令可能会产生大量需要传输到客户端的数据。
但是,客户端可能会发送更多命令,生成更多输出,以比 Redis 将现有输出发送到客户端的速度更快的速度提供服务。在发布/订阅客户端的情况下,尤其如此,如果客户端无法以足够快的速度处理新消息。
这两种情况都会导致客户端输出缓冲区增长并消耗越来越多的内存。出于这个原因,默认情况下,Redis 会对不同类型的客户端的输出缓冲区大小设置限制。当达到限制时,客户端连接将被关闭,并且事件将在 Redis 日志文件中记录。
Redis 使用两种限制
- 硬限制是一个固定限制,当达到此限制时,Redis 会尽快关闭客户端连接。
- 相反,软限制是一个取决于时间的限制,例如,每 10 秒 32 兆字节的软限制意味着,如果客户端的输出缓冲区连续 10 秒大于 32 兆字节,则连接将被关闭。
不同类型的客户端具有不同的默认限制
- 普通客户端的默认限制为 0,这意味着没有限制,因为大多数普通客户端使用阻塞实现,发送单个命令并等待回复完全读取后才发送下一个命令,因此在普通客户端的情况下,关闭连接总是不希望的。
- 发布/订阅客户端的默认硬限制为 32 兆字节,每 60 秒的软限制为 8 兆字节。
- 副本的默认硬限制为 256 兆字节,每 60 秒的软限制为 64 兆字节。
可以使用 CONFIG SET
命令在运行时更改限制,或者使用 Redis 配置文件 redis.conf
以永久方式更改限制。有关如何设置限制的更多信息,请参阅 Redis 发行版中的示例 redis.conf
。
查询缓冲区硬限制
每个客户端还受到查询缓冲区限制。这是一个不可配置的硬限制,当客户端查询缓冲区(即我们用来从客户端累积命令的缓冲区)达到 1 GB 时,它将关闭连接,实际上这只是一个极端的限制,以避免在客户端或服务器软件错误的情况下发生服务器崩溃。
客户端驱逐
Redis 是为了处理大量客户端连接而构建的。客户端连接往往会消耗内存,当连接很多时,聚合内存消耗可能非常高,从而导致数据驱逐或内存不足错误。这些情况可以在一定程度上使用 输出缓冲区限制 来缓解,但是 Redis 允许我们使用更强大的配置来限制所有客户端连接使用的聚合内存。
这种机制称为客户端驱逐,它本质上是一种安全机制,一旦所有客户端的聚合内存使用量超过阈值,它就会断开客户端连接。该机制首先尝试断开使用最多内存的客户端的连接。它会断开最少的连接,以将内存使用量降至 maxmemory-clients
阈值以下。
maxmemory-clients
定义了连接到 Redis 的所有客户端的聚合内存使用量最大值。聚合考虑了客户端连接使用的所有内存:查询缓冲区、输出缓冲区和其他中间缓冲区。
请注意,副本和主服务器连接不受客户端驱逐机制的影响。因此,此类连接永远不会被驱逐。
maxmemory-clients
可以永久地设置在配置文件 (redis.conf
) 中,也可以通过 CONFIG SET
命令设置。此设置可以是 0(表示没有限制),也可以是字节大小(可能带有 mb
/gb
后缀),也可以是使用 %
后缀的 maxmemory
的百分比(例如,将其设置为 10%
意味着 maxmemory
配置的 10%)。
默认设置为 0,这意味着客户端驱逐默认情况下已关闭。但是,对于任何大型生产部署,强烈建议配置一些非零的 maxmemory-clients
值。例如,值 5%
可以作为一个良好的起点。
可以将特定客户端连接标记为从客户端驱逐机制中排除。这对于控制路径连接很有用。例如,如果您有一个应用程序通过 INFO
命令监控服务器并在出现问题时提醒您,您可能希望确保此连接不被驱逐。您可以使用以下命令(来自相关客户端的连接)来实现这一点。
您可以使用以下命令来恢复它
CLIENT NO-EVICT
off
有关更多信息和示例,请参阅默认 redis.conf
文件中的 maxmemory-clients
部分。
客户端驱逐机制从 Redis 7.0 开始提供。
客户端超时
默认情况下,Redis 的最新版本不会在客户端闲置几秒钟后关闭与客户端的连接:连接将永远保持打开状态。
但是,如果您不喜欢这种行为,您可以配置超时,以便如果客户端闲置超过指定秒数,则客户端连接将被关闭。
您可以通过 redis.conf
或简单地使用 CONFIG SET timeout <value>
来配置此限制。
请注意,超时仅适用于普通客户端,不适用于发布/订阅客户端,因为发布/订阅连接是推送式连接,因此闲置的客户端是常态。
即使默认情况下连接不受超时限制,但在某些情况下设置超时是有意义的
- 任务关键型应用程序,其中客户端软件中的错误可能会导致 Redis 服务器被闲置连接饱和,从而导致服务中断。
- 作为一种调试机制,以便能够连接到服务器,如果客户端软件中的错误使服务器被闲置连接饱和,使其无法与服务器交互。
超时不应被认为非常精确:Redis 避免设置计时器事件或运行 O(N) 算法来检查闲置客户端,因此检查会不时地增量执行。这意味着,即使超时设置为 10 秒,客户端连接也可能在 12 秒后关闭,例如,如果同时连接了多个客户端。
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
](/commands/client-list) 文档。
获得客户端列表后,您可以使用 CLIENT KILL
命令关闭客户端的连接,指定客户端地址作为其参数。
命令 CLIENT SETNAME
和 CLIENT GETNAME
可用于设置和获取连接名称。从 Redis 4.0 开始,客户端名称将显示在 SLOWLOG
输出中,以帮助识别导致延迟问题的客户端。
TCP 保活
从 3.2 版本开始,Redis 默认情况下启用了 TCP 保活 (SO_KEEPALIVE
套接字选项),并设置为大约 300 秒。此选项用于检测死亡对等体(即使它们看起来已连接也无法访问的客户端)。此外,如果客户端和服务器之间存在需要查看一些流量才能保持连接打开的网络设备,则该选项将防止意外连接关闭事件。