Redis 客户端处理

Redis 服务器如何管理客户端连接

本文档提供了关于 Redis 如何在网络层级处理客户端的信息:包括连接、超时、缓冲区以及其他类似主题。

本文档中的信息仅适用于 Redis 2.6 或更高版本

接受客户端连接

Redis 在配置的 TCP 端口上接受客户端连接,如果启用了 Unix 套接字,也在 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 将现有输出发送给客户端的速度。对于 Pub/Sub 客户端尤其如此,当客户端无法足够快地处理新消息时会发生这种情况。

这两种情况都会导致客户端输出缓冲区不断增长并消耗越来越多的内存。因此,Redis 默认对不同类型客户端的输出缓冲区大小设置了限制。达到限制后,客户端连接将被关闭,并且该事件会被记录在 Redis 日志文件中。

Redis 使用两种类型的限制

  • 硬限制是一个固定限制,一旦达到此限制,Redis 将尽快关闭客户端连接。
  • 软限制是一个取决于时间的限制,例如,每 10 秒 32 兆字节的软限制意味着如果客户端的输出缓冲区连续 10 秒超过 32 兆字节,则连接将被关闭。

不同类型的客户端有不同的默认限制

  • 普通客户端的默认限制为 0,即没有限制,因为大多数普通客户端使用阻塞实现,发送单个命令并等待完全读取回复后再发送下一个命令,因此对于普通客户端而言,关闭连接通常是不合适的。
  • Pub/Sub 客户端的默认硬限制为 32 兆字节,软限制为每 60 秒 8 兆字节。
  • 副本的默认硬限制为 256 兆字节,软限制为每 60 秒 64 兆字节。

可以通过 CONFIG SET 命令在运行时更改此限制,或通过 Redis 配置文件 redis.conf 进行永久设置。有关如何设置限制的更多信息,请参阅 Redis 分发包中的示例 redis.conf

查询缓冲区硬限制

每个客户端还受到查询缓冲区限制。这是一个不可配置的硬限制,当客户端查询缓冲区(即我们用来累积客户端命令的缓冲区)达到 1GB 时,将关闭连接。这实际上只是一个极端的限制,用于避免在客户端或服务器软件存在 bug 时导致服务器崩溃。

客户端逐出

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> 配置此限制。

请注意,此超时仅适用于普通客户端,并且不适用于 Pub/Sub 客户端,因为 Pub/Sub 连接是推送式连接,因此客户端空闲是正常的。

即使默认情况下连接不受超时影响,但在以下两种情况下设置超时是有意义的

  • 任务关键型应用中,客户端软件的 bug 可能会使 Redis 服务器被空闲连接饱和,导致服务中断。
  • 作为一种调试机制,如果客户端软件的 bug 使服务器被空闲连接饱和,导致无法与服务器交互,则能够连接到服务器。

超时不应被视为非常精确: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 SETNAMECLIENT GETNAME 命令可用于设置和获取连接名称。从 Redis 4.0 开始,客户端名称显示在 SLOWLOG 输出中,有助于识别导致延迟问题的客户端。

TCP keepalive

从 3.2 版本开始,Redis 默认启用 TCP keepalive (SO_KEEPALIVE 套接字选项),并设置为约 300 秒。此选项有助于检测死对等体(即使看起来已连接也无法访问的客户端)。此外,如果客户端和服务器之间存在需要看到一些流量以保持连接打开的网络设备,此选项将防止意外的连接关闭事件。

评价此页面
返回顶部 ↑