博客 | NGINX

解决 NGINX 和 NGINX Plus 中的短暂端口耗尽问题

NGINX-F5-horiz-black-type-RGB 的一部分
凯文·琼斯缩略图
凯文·琼斯
2016 年 4 月 19 日发布

NGINXNGINX Plus是极其强大的HTTPTCPUDP负载均衡器。 它们能够非常高效地代理大量突发请求并维持大量并发连接。 但这些特性使得 NGINX 和 NGINX Plus 特别容易受到短暂端口耗尽的影响——由于操作系统用完了分配给建立新本地套接字的端口号,因此无法创建新连接的情况。 (短暂端口耗尽适用于这两种产品,但为了简洁起见,我们将在本博客的其余部分仅参考 NGINX Plus。)

在这篇博客中,我们回顾了 TCP 连接的组成部分以及在建立连接之前如何决定其内容。 然后,我们展示如何确定 NGINX Plus 何时受到短暂端口耗尽的影响。 最后,我们讨论了使用 Linux 内核调整和 NGINX Plus 指令来克服这些限制的策略。

网络套接字简要概述

通过 TCP 建立连接时,会在本地和远程主机上创建一个套接字。 然后连接这些套接字以创建一个套接字对,该套接字对由本地 IP 地址和端口以及远程 IP 地址和端口组成的唯一 4 元组描述。

远程 IP 地址和端口属于连接的服务器端,并且必须由客户端在发起连接之前确定。 大多数情况下,客户端会自动选择使用哪个本地 IP 地址进行连接,但有时也会由建立连接的软件来选择。 最后,从操作系统提供的定义范围中随机选择本地端口。 该端口仅在连接持续期间与客户端关联,因此被称为短暂的。 当连接终止时,临时端口可供重新使用。

识别短暂的端口耗尽

正如介绍中所提到的,NGINX Plus 本质上容易受到短暂端口耗尽及其引起的问题的影响。 当 NGINX Plus 将请求代理到上游服务器时,它是上面描述的套接字创建过程中的客户端,其默认行为是将代理请求的套接字自动绑定到本地 IP 地址和运行主机上可用的临时端口。 如果连接率很高,使得正在建立的套接字进入等待状态的速度比关闭现有打开的套接字的速度快,那么最终可用端口将被耗尽,并且无法创建新的套接字。 这会导致操作系统和 NGINX Plus 都出现错误。 以下是 NGINX Plus 错误日志中产生的错误的一个示例。

2016/03/18 09:08:37 [crit] 1888#1888: *13 connect() 至 10.2.2.77:8081 失败 (99: 无法分配请求的地址)连接到上游时,客户端: 10.2.2.42,服务器:,请求: “GET / HTTP/1.1”,上游:“http://10.2.2.77:8081/”,主机: “10.2.2.77”

港口枯竭也会导致500错误源自 NGINX Plus 而不是上游服务器。 这是 NGINX Plus 访问日志中的示例条目。

10.2.2.42 - - [18/Mar/2016:09:14:20 -0700] “获取/HTTP/1.1”500 192 “-” “curl/7.35.0”

要检查 NGINX Plus 服务器上处于TIME-WAIT状态的套接字数量,请在 Linux shell 中运行以下ss命令。 该示例显示有 143 个套接字打开,状态为TIME-WAIT 。 在示例中,我们使用wc命令列出命令输出中的行数来获取计数。

# ss -a | grep TIME-WAIT | wc -l143

调整内核以增加可用临时端口的数量

减少临时端口耗尽的一种方法是使用 Linux 内核net.ipv4.ip_local_port_range设置。 默认范围最常见的是 32768 到 61000。

如果您注意到临时端口即将用完,将范围从默认值 1024 到 65000 更改为可用临时端口数量的两倍是一种实用的方法。 有关更改内核设置的更多信息,请参阅我们的“调整 NGINX 性能”博客文章。

在 NGINX Plus 中启用 Keepalive 连接

减少临时端口耗尽的另一种方法是启用 NGINX Plus 和上游服务器之间的保持连接。 在 HTTP 的最简单实现中,客户端打开一个新连接,写入请求,读取响应,然后关闭连接以释放相关资源。

客户端读取响应后,保持连接保持打开状态,因此可以重新用于后续请求。

使用keepalive指令启用从 NGINX Plus 到上游服务器的保持连接,定义在每个工作进程的缓存中保留的到上游服务器的最大空闲保持连接数。 当超过此数量时,最近最少使用的连接将被关闭。 如果没有保持活动,则会增加更多开销,并且连接和临时端口的效率都会变得低下。

以下示例配置告诉 NGINX Plus 与名为backend上游块中定义的服务器维持至少 128 个保持活动连接。

上游后端 { 服务器 10.0.0.100:1234;
服务器 10.0.0.101:1234;
keepalive 128;
}

当启用与上游服务器的保持连接时,还必须使用proxy_http_version指令告诉 NGINX Plus 使用 HTTP 版本 1.1,并使用proxy_set_header指令删除任何名为Connection的标头。 这两个指令都可以放在httpserverlocation配置块中。

proxy_http_version 1.1; proxy_set_header 连接“”;

将连接动态绑定到定义的本地 IP 地址列表

优化内核并启用保持活动连接可以更好地控制临时端口的可用性和使用情况,但在某些情况下,这些更改不足以对抗临时端口的过度使用。 在这种情况下,我们可以采用一些 NGINX Plus 指令和参数,允许我们将一定比例的连接绑定到静态本地 IP 地址。 这实际上将可用的临时端口数量乘以配置中定义的 IP 地址数量。 为了实现这一点,我们使用proxy_bindsplit_clients指令。

在下面的示例配置中,位置块中的proxy_bind指令根据$split_ip变量的值在每次请求期间设置本地 IP 地址。 我们将这个变量动态设置为split_clients块生成的值,该块使用哈希函数来确定该值。

split_clients指令的第一个参数是一个字符串( “$remote_addr$remote_port” ),它在每次请求期间使用MurmurHash2函数进行散列。 第二个参数( $split_ip )是我们根据第一个参数的哈希值动态设置的变量。

花括号内的语句将哈希表划分为“桶”,每个桶包含一定百分比的哈希值。 这里我们将表分成 10 个大小相同的桶,但我们可以创建任意数量的桶,并且它们不必全部具有相同的大小。 (最后一个存储桶的百分比始终用星号 [ * ] 表示,而不是具体数字,因为哈希值的数量可能无法均匀地分成指定的百分比。)

可能的哈希值范围是从 0 到 4294967295,因此在我们的例子中,每个存储桶包含大约 429496700 个值(总数的 10%):第一个存储桶的值从 0 到 429496700,第二个存储桶的值从 429496701 到 858993400,依此类推。 $split_ip变量设置为与包含$remote_addr$remote_port字符串的哈希值的存储桶关联的 IP 地址。 举一个具体的例子,哈希值 150000000 落在第四个存储桶中,因此在这种情况下变量$split_ip会动态设置为 10.0.0.213。

http { 上游后端 { 服务器 10.0.0.100:1234; 服务器 10.0.0.101:1234; } 服务器 { #... 位置 / { #... proxy_pass http://backend; proxy_bind $split_ip; proxy_set_header X-Forwarded-For $remote_addr; } } split_clients "$remote_addr$remote_port" $split_ip { 10% 10.0.0.210; 10% 10.0.0.211; 10% 10.0.0.212; 10% 10.0.0.213; 10% 10.0.0.214; 10% 10.0.0.215; 10% 10.0.0.216; 10% 10.0.0.217; 10% 10.0.0.218; * 10.0.0.219; } }

结论

调整 Linux 内核的临时端口数量设置可以提供更高效的可用性和使用率。 启用 NGINX Plus 与上游服务器的保持连接可以提高它们对临时端口的消耗效率。 最后,在 NGINX Plus 配置中使用split_clientsproxy_bind指令,您可以将传出连接动态绑定到定义的本地 IP 地址列表,从而大大增加 NGINX Plus 可用的临时端口数量。

要试用 NGINX Plus,请立即开始30 天免费试用联系我们讨论您的用例。


“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”