无法打开超过1023个套接字

4
我正在开发一些模拟网络设备的代码。我需要运行数千个模拟"代理",每个代理都需要连接到一个服务。问题在于,在打开1023个连接后,连接开始超时,整个系统崩溃了。
主要的代码是用Go语言编写的,但我编写了一个非常简单的Python脚本来重现这个问题。
有些不寻常的是,我们需要在创建套接字时设置本地地址。这是因为代理连接到的设备希望显式IP与我们所说的IP相匹配。为了实现这一点,我配置了10000个虚拟接口(eth0:1到eth0:10000),并为它们分配了私有网络中的唯一IP地址。
Python脚本只是这样的(仅运行到2000个连接):
import socket

i = 0
for b in range(10, 30):
    for d in range(1, 100):
        i += 1
        ip = "1.%d.1.%d" % (b, d)
        print("Conn %i   %s" % (i, ip))
        s = socket.create_connection(("1.6.1.1", 5060), 10, (ip, 5060))

如果我删除socket.create_connection的最后一个参数(源地址),那么我可以获得所有2000个连接。

使用本地地址不同的是,必须在建立连接之前进行绑定,因此在strace下运行此程序的输出如下:

Conn 1023   1.20.1.33
bind(3, {sa_family=AF_NETLINK, pid=0, groups=00000000}, 12) = 0
bind(3, {sa_family=AF_INET, sin_port=htons(5060), sin_addr=inet_addr("1.20.1.33")}, 16) = 0
connect(3, {sa_family=AF_INET, sin_port=htons(5060), sin_addr=inet_addr("1.6.1.1")}, 16) = -1 EINPROGRESS (Operation now in progress)

如果我不使用本地地址运行,则AF_INET绑定会消失,它可以工作。
因此,似乎必须有一些限制可以进行绑定。我已经浏览了关于Linux上TCP调整的各种链接,并尝试了tcp_tw_reuse/recycle和减少fin_timeout等操作,以及其他一些我无法记住的操作。
这是在Ubuntu Linux上运行的(11.04,内核2.6.38(64位)。它是VMWare ESX集群上的虚拟机。
就在发布这篇文章之前,我尝试了运行第二个Python脚本实例,其中一个附加在1.30.1.1处。第一个脚本通过了1023个连接,但第二个脚本甚至无法完成第一个脚本,这表明问题与大量虚拟接口有关。某些内部数据结构可能受到限制吗?某些最大内存设置在某个地方?
有谁能想到Linux中可能会导致这种情况的限制吗?
更新:
今天早上我决定尝试一个实验。我修改了Python脚本,将“main”接口IP用作源IP,并在10000+范围内使用临时端口。现在脚本看起来像这样:
import socket

i = 0
for i in range(1, 2000):
    print("Conn %i" % i)
    s = socket.create_connection(("1.6.1.1", 5060), 10, ("1.1.1.30", i + 10000))

这个脚本很好用,所以我认为问题与大量别名IP地址有关。


这不是ulimit的问题。如果是ulimit的问题,会出现“打开文件太多”的错误。我遇到的是连接超时问题(套接字处于SYN_SENT状态)。我的ulimit设置为50,000。 - Hisnessness
在失败的 bind 上,你的 strace 返回了非零值吗? - Karl Bielefeldt
你是否使用select()(如果是,则切换到poll())? - ninjalj
@KarlBielefeldt,绑定操作不会失败。在绑定之后进行的连接操作会返回EINPROGRESS(它总是这样)。@ninjali:我所依赖的底层套接字实现使用了poll:bind(3, {sa_family=AF_INET, sin_port=htons(5060), sin_addr=inet_addr("1.40.1.33")}, 16) = 0 connect(3, {sa_family=AF_INET, sin_port=htons(5060), sin_addr=inet_addr("1.6.1.1")}, 16) = -1 EINPROGRESS (Operation now in progress) poll([{fd=3, events=POLLOUT}], 1, 10000) = 0 (Timeout) - Hisnessness
3个回答

2

真是个“DOH”时刻。我一直在用netstat观察服务器,由于没有看到大量连接,所以我认为没有问题。但最终我变聪明了,检查了/var/log/kernel,发现了这个:

Mar  8 11:03:52 TestServer01 kernel: ipv4: Neighbour table overflow.

这引导我发布这篇文章:http://www.serveradminblog.com/2011/02/neighbour-table-overflow-sysctl-conf-tunning/,该文章解释了如何增加限制。立即增加thresh3值就可以解决问题。

0
你可能想要查看与 net.ipv4 相关的 sysctl 设置。
这些设置包括诸如 maxconntrack 和其他相关设置,您可能希望进行调整。

我已经搜索过了,但似乎找不到任何相关的东西。 - Hisnessness

0

你确定问题不是在服务器端连接没有关闭套接字吗?即服务器进程的 lsof -n -p 显示了什么?服务器进程的 plimit -p 显示了什么?服务器端可能被卡住,无法接受更多的连接,而客户端则得到 EINPROGRESS 的结果。

检查连接两端的打开文件数限制 - 1024 太接近 ulimit 级别,这不可能是巧合。


我一直在服务器上观察 netstat -t -n,就我所见,并没有任何影响。 - Hisnessness

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接