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
](https://redis.ac.cn/commands/client-list) 文档,了解字段及其用途的完整列表。
一旦你有了客户端列表,你可以使用 CLIENT KILL
命令关闭客户端连接,指定客户端地址作为其参数。
命令 CLIENT SETNAME
和 CLIENT GETNAME
可用于设置和获取连接名称。从 Redis 4.0 开始,客户端名称显示在 SLOWLOG
输出中,以帮助识别造成延迟问题的客户端。
TCP 保活
从 3.2 版本开始,Redis 已默认启用 TCP 保活(SO_KEEPALIVE
套接字选项),并将其设置为约 300 秒。此选项对于检测死对等方(即使看起来已连接,也无法访问的客户端)非常有用。此外,如果客户端和服务器之间有需要看到一些流量才能保持连接打开的网络设备,该选项将防止意外的连接关闭事件。