Как Nettys FileDescriptor используется в OS X - PullRequest
2 голосов
/ 12 февраля 2020

в проекте PLC4X мы используем Netty для клиентов для подключения к ПЛК, которые действуют как сервер. Иногда, либо по ошибке пользователя, либо по ошибке PL C, соединения не принимаются, но отклоняются. Если мы попытаемся установить соединение как можно скорее несколько раз, мы увидим сообщение об ошибке 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% сбоев происходит не из-за «отклонения», а из-за «слишком большого количества открытых файлов». Это ulimit вещь, так как Netty (на macOS) просто нужно несколько fd для использования? Или я что-то слил?

Спасибо за разъяснения!

1 Ответ

2 голосов
/ 12 февраля 2020

Я нашел решение, вроде себя. В исходной реализации есть 2 проблемы (возможно, даже 3), которые на самом деле не связаны с Ma c OS X:

  • connect и addListener должны быть связаны
  • workerGroup.shutdownGracefully() запускается в другом потоке, поэтому основной (вызываемый) поток уже завершает
  • Не ожидается, что рабочийГруппа действительно завершается.

Это вместе может привести к ситуациям, как кажется, где новые группы создаются быстрее, чем закрываются старые группы. Таким образом, я изменил реализацию на

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);
}

, что устраняет вышеуказанные проблемы. Таким образом, вызов заканчивается только после его надлежащей очистки.

Мои тесты теперь показывают постоянное количество дескрипторов открытых файлов.

...