第32章 客户端超时问题排查
排查客户端超时问题
排查超时故障
客户端报“超时错误”,但不知从何入手? 本页将引导你定位最常见的原因。
检查防火墙 你可能需要调整或关闭位于客户端与 memcached 之间的防火墙。大多数防火墙会跟踪当前使用的连接(连接跟踪),一旦达到跟踪上限,现有 TCP 连接会被丢弃,从而引发超时错误。 你需要查阅防火墙厂商的文档来确认具体配置。
仔细排查常见嫌疑人
* 机器是否在使用 Swap? 如果你的操作系统定期将 memcached 换出到磁盘,你会看到随机出现的延迟尖峰。 * 机器是否过载? 如果 CPU 空闲率为 0%,负载高达 400,memcached 可能无法获得足够的 CPU 时间。你可以尝试使用 `nice` 或 `renice` 调整优先级,或者减少机器上的任务量。如果 CPU 严重过载,你可能会注意到下方的 `mc_conn_tester` 报告 `set` 命令的等待时间非常高。
使用 mc_conn_tester.pl
下载这个 Perl 脚本: https://www.memcached.org/files/mc_conn_tester.pl
```bash $ ./mc_conn_tester.pl -s memcached-host -p 11211 -c 1000 --timeout 1 Averages: (conn: 0.00081381) (set: 0.00001603) (get: 0.00040122) $ ./mc_conn_tester.pl --help Usage: mc_conn_tester.pl [options]
Options: -s --server hostname Connect to an alternate hostname. [...etc...] ```
这是一个用于直接对 memcached 实例进行快速常规测试的小型工具。它会建立连接,尝试执行几次 `set` 和几次 `get`,然后循环重复此过程。 该工具不使用任何 memcached 客户端库,而是通过 ASCII 协议执行最小化的原始命令,从而有助于排除客户端本身的 bug。
如果发生超时,你可以查看它在测试周期中走到了哪一步:
```bash Fail: (timeout: 1) (elapsed: 1.00427794) (conn: 0.00000000) (set: 0.00000000) (get: 0.00000000) Fail: (timeout: 1) (elapsed: 1.00133896) (conn: 0.00000000) (set: 0.00000000) (get: 0.00000000) Fail: (timeout: 1) (elapsed: 1.00135303) (conn: 0.00000000) (set: 0.00000000) (get: 0.00000000) Fail: (timeout: 1) (elapsed: 1.00145602) (conn: 0.00000000) (set: 0.00000000) (get: 0.00000000) ```
在上述日志行中,包含测试的总经过时间,以及每个子测试成功时的时间点。在上面的场景中,由于无法连接到 memcached,所有测试都失败了(`conn` 等均为 0)。
```bash Fail: (timeout: 1) (elapsed: 0.00121498) (conn: 0.00114512) (set: 1.00002694) (get: 0.00000000) Fail: (timeout: 1) (elapsed: 0.00368810) (conn: 0.00360799) (set: 1.00003314) (get: 0.00000000) Fail: (timeout: 1) (elapsed: 0.00128603) (conn: 0.00117397) (set: 1.00004005) (get: 0.00000000) Fail: (timeout: 1) (elapsed: 0.00115108) (conn: 0.00108099) (set: 1.00002789) (get: 0.00000000) ```
在这种情况下,测试是因为等待 `get` 完成而失败。
如果你想记录 `mc_conn_tester.pl` 运行的所有测试,请打开该文件并修改此行: 将 `my $debug = 0;` 改为 `my $debug = 1;` 这样,正常的日志行将以 `loop:` 开头,而失败的测试行则照旧以 `Fail:` 开头。
你可能正在丢包 在许多情况下,你可能因某种原因正在丢失数据包。可能是防火墙阻挡了请求且用尽了有状态跟踪槽位,或者是你的网卡或交换机正在丢包。 这种情况通常表现为:
```bash Fail: (timeout: 1) (elapsed: 1.00145602) (conn: 0.00000000) (set: 0.00000000) (get: 0.00000000) ```
即 `conn` 及其后续指标均为零,意味着测试无法连接到 memcached。你也可能看到 `conn`/`set`/`get` 中混杂着随机数值和部分完全失败的情况。
在大多数系统中,SYN 重试间隔为 3 秒,这时间太长。丢失单个 SYN 包必然导致超时。这一点很容易验证:
```bash $ ./mc_conn_tester.pl -s memcached-host -c 5000 --timeout 1 > log_one_second && ./mc_conn_tester.pl -s memcached-host -c 5000 --timeout 4 > log_three_seconds && ./mc_conn_tester.pl -s memcached-host -c 5000 --timeout 8 > log_eight_seconds ```
…每轮运行 5000 次测试(你可以自行调整此数值)。第一轮超时设为 1 秒(这是常见的客户端默认值)。第二轮设为 4 秒,允许丢失 1 个 SYN 包但仍能通过测试。最后一轮设为 8 秒,允许连续丢失 2 个 SYN 包仍成功。
如果你看到每个日志文件中 `Fail:` 行的数量随着超时时间增加而减少,那么你的网络很可能正在丢失 SYN 包。 然而,修复此类网络问题的方法超出了本文档的范围。
TIME_WAIT 桶或本地端口耗尽 如果 `mc_conn_tester.pl` 报告连接超时(`conn:` 为 0),你可能正在耗尽本地端口、防火墙状态或 TIME_WAIT 桶。如果你打开和关闭连接的速度快于套接字(socket)自然销毁的速度,就会出现这种情况。 使用 `netstat` 查看当前打开的连接数量,判断数值是否高到可能引发问题。例如执行:`netstat -n | grep -c TIME_WAIT`。 如何调整这些变量的细节不在本文档范围内,但搜索“Linux TCP network tuning TIME_WAIT”(或你使用的操作系统)通常能找到不错的结果。请查找以下变量并理解其含义后再进行调整。
注意:这些是示例值,并非推荐值!
```text net.ipv4.ip_local_port_range = 16384 65534 net.ipv4.tcp_max_tw_buckets = 262144 net.ipv4.tcp_fin_timeout = 30 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 ```
另外,请阅读关于 iptables 的资料,并查找有关管理 conntrack 状态或 conntrack 桶的信息。如果你找到了优秀的链接,请发邮件告诉我们,我们会将其列入此处。
但你的工具从未失败过! 既然工具测试正常,那很有可能是你的客户端存在 bug :( 请尝试联系客户端作者寻求帮助。如果无效,请通过 GitHub discussions 联系我们寻求支持。