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将现有输出发送给客户端更快的速度产生更多的输出。这在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

查询缓冲区硬限制

每个客户端也受到查询缓冲区限制的约束。这是一个不可配置的硬性限制,当客户端查询缓冲区(即我们用来累积来自客户端的命令的缓冲区)达到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 来配置这个限制。

请注意,超时仅适用于普通客户端,不适用于发布/订阅客户端,因为发布/订阅连接是一种推送式连接,因此客户端空闲是常态。

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

  • 关键任务应用程序,其中客户端软件中的错误可能会使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 SETNAMECLIENT GETNAME 可用于设置和获取连接名称。从 Redis 4.0 开始,客户端名称显示在 SLOWLOG 输出中,以帮助识别导致延迟问题的客户端。

TCP 保持连接

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

RATE THIS PAGE
Back to top ↑