OS X 上的 Nettys FileDescriptor 使用情况如何
How is Nettys FileDescriptor usage on OS X
在 PLC4X 项目中,我们使用 Netty 让客户端连接到充当服务器的 PLC。有时,由于用户错误或 PLC 错误,连接未被接受而是被拒绝。如果我们多次重试尽快建立连接,我们 运行 进入错误消息 Too many open files
。
我尝试清理代码中的所有内容,因此我假设没有可能泄漏的文件描述符:
try {
final NioEventLoopGroup workerGroup = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(workerGroup);
bootstrap.channel(NioSocketChannel.class);
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
bootstrap.option(ChannelOption.TCP_NODELAY, true);
// TODO we should use an explicit (configurable?) timeout here
// bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000);
bootstrap.handler(channelHandler);
// Start the client.
final ChannelFuture f = bootstrap.connect(address, port);
f.addListener(new GenericFutureListener<Future<? super Void>>() {
@Override public void operationComplete(Future<? super Void> future) throws Exception {
if (!future.isSuccess()) {
logger.info("Unable to connect, shutting down worker thread.");
workerGroup.shutdownGracefully();
}
}
});
// Wait for sync
f.sync();
f.awaitUninterruptibly(); // jf: unsure if we need that
// Wait till the session is finished initializing.
return f.channel();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new PlcConnectionException("Error creating channel.", e);
} catch (Exception e) {
throw new PlcConnectionException("Error creating channel.", e);
}
根据我的理解,监听器应该始终关闭组并释放所有使用的描述符。
但实际上,当 运行 在 macOS Catalina 上使用它时,我看到大约 1% 的失败不是由于 "rejection" 而是由 "Too many open files" 引起的。
这是 ulimit
的事情吗,因为 Netty(在 macOS 上)只需要使用多个 fd?还是我漏了什么?
感谢您的澄清!
我自己找到了解决方案。
原始实现中有 2 个问题(甚至可能是 3 个),与 Mac OS X:
没有真正的关系
- connect 和 addListener 应该链接起来
workerGroup.shutdownGracefully()
在另一个线程中触发,因此主(调用)线程已经完成
- 不等workerGroup真的结束了
这一起可能会导致看起来的情况,新组的产生速度比旧组的关闭速度快。
因此,我将实现更改为
try {
final NioEventLoopGroup workerGroup = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(workerGroup);
bootstrap.channel(NioSocketChannel.class);
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
bootstrap.option(ChannelOption.TCP_NODELAY, true);
// TODO we should use an explicit (configurable?) timeout here
// bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000);
bootstrap.handler(channelHandler);
// Start the client.
logger.trace("Starting connection attempt on tcp layer to {}:{}", address.getHostAddress(), port);
final ChannelFuture f = bootstrap.connect(address, port);
// Wait for sync
try {
f.sync();
} catch (Exception e) {
// Shutdown worker group here and wait for it
logger.info("Unable to connect, shutting down worker thread.");
workerGroup.shutdownGracefully().awaitUninterruptibly();
logger.debug("Worker Group is shutdown successfully.");
throw new PlcConnectionException("Unable to Connect on TCP Layer to " + address.getHostAddress() + ":" + port, e);
}
// Wait till the session is finished initializing.
return f.channel();
}
catch (Exception e) {
throw new PlcConnectionException("Error creating channel.", e);
}
解决了上述问题。因此,调用只有在正确清理后才会结束。
我的测试现在显示打开文件描述符的数量不变。
在 PLC4X 项目中,我们使用 Netty 让客户端连接到充当服务器的 PLC。有时,由于用户错误或 PLC 错误,连接未被接受而是被拒绝。如果我们多次重试尽快建立连接,我们 运行 进入错误消息 Too many open files
。
我尝试清理代码中的所有内容,因此我假设没有可能泄漏的文件描述符:
try {
final NioEventLoopGroup workerGroup = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(workerGroup);
bootstrap.channel(NioSocketChannel.class);
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
bootstrap.option(ChannelOption.TCP_NODELAY, true);
// TODO we should use an explicit (configurable?) timeout here
// bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000);
bootstrap.handler(channelHandler);
// Start the client.
final ChannelFuture f = bootstrap.connect(address, port);
f.addListener(new GenericFutureListener<Future<? super Void>>() {
@Override public void operationComplete(Future<? super Void> future) throws Exception {
if (!future.isSuccess()) {
logger.info("Unable to connect, shutting down worker thread.");
workerGroup.shutdownGracefully();
}
}
});
// Wait for sync
f.sync();
f.awaitUninterruptibly(); // jf: unsure if we need that
// Wait till the session is finished initializing.
return f.channel();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new PlcConnectionException("Error creating channel.", e);
} catch (Exception e) {
throw new PlcConnectionException("Error creating channel.", e);
}
根据我的理解,监听器应该始终关闭组并释放所有使用的描述符。
但实际上,当 运行 在 macOS Catalina 上使用它时,我看到大约 1% 的失败不是由于 "rejection" 而是由 "Too many open files" 引起的。
这是 ulimit
的事情吗,因为 Netty(在 macOS 上)只需要使用多个 fd?还是我漏了什么?
感谢您的澄清!
我自己找到了解决方案。 原始实现中有 2 个问题(甚至可能是 3 个),与 Mac OS X:
没有真正的关系- connect 和 addListener 应该链接起来
workerGroup.shutdownGracefully()
在另一个线程中触发,因此主(调用)线程已经完成- 不等workerGroup真的结束了
这一起可能会导致看起来的情况,新组的产生速度比旧组的关闭速度快。 因此,我将实现更改为
try {
final NioEventLoopGroup workerGroup = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(workerGroup);
bootstrap.channel(NioSocketChannel.class);
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
bootstrap.option(ChannelOption.TCP_NODELAY, true);
// TODO we should use an explicit (configurable?) timeout here
// bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000);
bootstrap.handler(channelHandler);
// Start the client.
logger.trace("Starting connection attempt on tcp layer to {}:{}", address.getHostAddress(), port);
final ChannelFuture f = bootstrap.connect(address, port);
// Wait for sync
try {
f.sync();
} catch (Exception e) {
// Shutdown worker group here and wait for it
logger.info("Unable to connect, shutting down worker thread.");
workerGroup.shutdownGracefully().awaitUninterruptibly();
logger.debug("Worker Group is shutdown successfully.");
throw new PlcConnectionException("Unable to Connect on TCP Layer to " + address.getHostAddress() + ":" + port, e);
}
// Wait till the session is finished initializing.
return f.channel();
}
catch (Exception e) {
throw new PlcConnectionException("Error creating channel.", e);
}
解决了上述问题。因此,调用只有在正确清理后才会结束。
我的测试现在显示打开文件描述符的数量不变。