*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 # 无法将文件的最大数量限制设置为 100032(无效参数),将最大客户端配置设置为 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
on
您可以使用以下命令将其恢复:
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
获取客户端列表后,可以使用 CLIENT KILL 命令关闭客户端连接,指定客户端地址作为参数。
CLIENT SETNAME 和 CLIENT GETNAME 命令可用于设置和获取连接名称。从 Redis 4.0 开始,客户端名称显示在 SLOWLOG 输出中,以帮助识别导致延迟问题的客户端。
*TCP keepalive
从版本 3.2 开始,Redis 默认启用 TCP keepalive(SO_KEEPALIVE
套接字选项),并设置为大约 300 秒。此选项用于检测死对等方(即使看起来已连接也无法到达的客户端)。此外,如果客户端和服务器之间的网络设备需要看到一些流量以保持连接打开,此选项将防止意外的连接关闭事件。