"Connection reset by peer"和"Too many open files"异常在尝试扩展Vert.x服务器时出现。

5
我正在尝试测试基于Netty的Java Vert.x Web服务器在Mac OS X上的可扩展性。为此,我已将连接限制增加到100万:
sudo sysctl -w kern.maxfiles=1000200
sudo sysctl -w kern.maxfilesperproc=1000100
sudo ulimit -n 1000000

然后我使用以下命令创建了本地主机别名:
for i in `seq 200 250`; do sudo ifconfig lo0 alias 172.16.123.$i ; done

我现在能够连接到这些IP地址;然而,在建立了大约1000-2000个连接之后,我会遇到“对等方重置连接”的情况:

java.net.SocketException: Connection reset by peer
    at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method)
    at sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:712)
    at io.netty.channel.socket.nio.NioSocketChannel.doFinishConnect(NioSocketChannel.java:191)
    at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:228)
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:497)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:447)

他们后面跟着“打开的文件过多”异常:

Caused by: java.net.SocketException: Too many open files
    at sun.nio.ch.Net.socket0(Native Method)
    at sun.nio.ch.Net.socket(Net.java:393)
    at sun.nio.ch.Net.socket(Net.java:386)
    at sun.nio.ch.SocketChannelImpl.<init>(SocketChannelImpl.java:104)
    at sun.nio.ch.SelectorProviderImpl.openSocketChannel(SelectorProviderImpl.java:60)
    at java.nio.channels.SocketChannel.open(SocketChannel.java:145)
    at io.netty.channel.socket.nio.NioSocketChannel.newSocket(NioSocketChannel.java:48)

有趣的是,当我使用netstat列出所有连接时,我发现有很多连接处于FIN_WAIT_1状态:
tcp4       0    213  172.16.123.230.55618   172.16.123.230.8877    FIN_WAIT_1 
tcp4       0    213  172.16.123.229.55624   172.16.123.229.8877    FIN_WAIT_1 
tcp4       0    213  172.16.123.228.55617   172.16.123.228.8877    FIN_WAIT_1 
tcp4       0    213  172.16.123.227.55616   172.16.123.227.8877    FIN_WAIT_1 
tcp4       0    213  172.16.123.226.55612   172.16.123.226.8877    FIN_WAIT_1 

其他结果:

$ netstat -a | wc -l:
14282
$ lsof | wc -l
6922

服务器代码(简化版):

HttpServer server = vertx.createHttpServer();
server.websocketHandler(ws -> {
    connections++;
    ws.exceptionHandler(t -> t.printStackTrace());
    ws.closeHandler(h -> {
        connections--;
    });

    if (ws.path().equals("/app")) {
        ws.dataHandler(data -> {
            // ...
        });
    } else {
        ws.reject();
    }
}).listen(8877);

客户端代码(简化版):

Handler<Void> connectHandler = new Handler<Void>() {
    @Override
    public void handle(Void e) {
        HttpClient client = vertx.createHttpClient().setHost(nextHost()).setPort(8877);

        client.connectWebsocket("/app", ws -> {
            connections++;
            ws.exceptionHandler(t -> t.printStackTrace());
            ws.closeHandler(h -> {
                connections--;
            });

            // ...
        });

        if (connections < 1000000) {
            vertx.runOnContext(this);
        }
    }
};
connectHandler.handle(null);

两者都在同一台计算机上启动:

vertx run Class -cp classes:... -instances 8

有人知道问题可能是什么吗?我对这样的设置还没有经验,也许我只是犯了一个简单的错误。增加接受后备量并没有帮助。

更多信息:

更新:

添加后

limit maxfiles 1000100 2000200

到 /etc/launchd.conf 和

kern.maxfiles=2000400
kern.maxfilesperproc=1000200

将内容添加到 /etc/sysctl.conf 文件并重新启动计算机后,情况已经有所改善。但是,现在我会得到“打开文件太多”的异常,即使只是读取普通文件也会出现,尽管没有超过10000个打开的文件:

Caused by: sun.nio.fs.UnixException: Too many open files
        at sun.nio.fs.UnixNativeDispatcher.getcwd(Native Method)
        at sun.nio.fs.UnixFileSystem.<init>(UnixFileSystem.java:67)
        at sun.nio.fs.BsdFileSystem.<init>(BsdFileSystem.java:41)
        at sun.nio.fs.MacOSXFileSystem.<init>(MacOSXFileSystem.java:44)
        at sun.nio.fs.MacOSXFileSystemProvider.newFileSystem(MacOSXFileSystemProvider.java:45)
        at sun.nio.fs.MacOSXFileSystemProvider.newFileSystem(MacOSXFileSystemProvider.java:38)
        at sun.nio.fs.UnixFileSystemProvider.<init>(UnixFileSystemProvider.java:56)
        at sun.nio.fs.BsdFileSystemProvider.<init>(BsdFileSystemProvider.java:36)
        at sun.nio.fs.MacOSXFileSystemProvider.<init>(MacOSXFileSystemProvider.java:40)

更新2::

我也尝试了其他几个选项,但似乎有一个限制,即恰好10180个连接是不能超过的。除非有人有另外的想法,否则我可能只能切换到Linux。

更新3::

添加JVM选项“-XX:-MaxFDLimit”后,我现在可以打开16331个连接,直到出现以下异常:

java.net.BindException: Can't assign requested address
        at sun.nio.ch.Net.connect0(Native Method)
        at sun.nio.ch.Net.connect(Net.java:435)
        at sun.nio.ch.Net.connect(Net.java:427)
        at sun.nio.ch.SocketChannelImpl.connect(SocketChannelImpl.java:643)
        at io.netty.channel.socket.nio.NioSocketChannel.doConnect(NioSocketChannel.java:176)

这可能是因为默认端口范围(16383个端口)的原因:
net.inet.ip.portrange.first: 49152
net.inet.ip.portrange.last: 65535

然而,由于我使用了50个本地主机别名,我本来希望能够打开16383 * 50 = 819150个连接。或者这是一个已知的限制,不能在不同的别名中使用相同的端口?更新4:我尝试使用networksetup创建虚拟接口,但结果仍然相同。
for i in `seq 200 250`; do sudo networksetup -createnetworkservice lo$i lo0 ; sudo networksetup -setmanual lo$i 172.16.123.$i 255.240.0.0 ; done

也许你已经用完了可用的“临时端口”? - swKK
@swKK 我不这么认为,因为我连接到本地主机还是所有50个别名都没有关系。此外,端口范围包含16383个端口。无论如何,我会增加它并检查是否有任何区别。 - Joel
很遗憾,它没有帮助增加短暂端口范围。 - Joel
10180个同时连接对于一个典型的应用程序/机器来说是很多的。您确定需要保持所有这些连接吗?对于100万个以上的同时连接,通常会开始使用一些应用程序优化的TCP堆栈和/或负载均衡硬件/DNS与多个服务器的组合。 - Jason C
Netty即使使用默认设置,也可以轻松处理10k个连接。 - Nino Walker
显示剩余2条评论
1个回答

1

非常感谢这个提示!现在我可以打开16331个连接,然后我会得到“java.net.BindException:无法分配请求的地址”异常。这可能是由于短暂端口范围限制(当前为16383),但我不太明白为什么,因为我使用50个别名来规避这个问题。我将更新问题。 - Joel

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