From 87f3dd64b3e577d2fef8d98e1a4aaf191c4b124c Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Tue, 25 Sep 2018 19:51:33 +0200 Subject: [PATCH] Allow SshProxyServer to share AsynchronousChannelGroup For testing purposes it is good to have the ability to share a single thread group, as ach of them implies a dedicated thread. This patch exposes an alternative constructor and allows multiple SshProxyServers to share that group -- lowering resources needed significantly, both in terms of threads, memory and file descriptors used. Change-Id: I237ab0790e9a70c26288a116c59f5a541f236a74 Signed-off-by: Robert Varga --- .../netconf/ssh/SshProxyServer.java | 100 +++++++++++++----- .../test/tool/NetconfDeviceSimulator.java | 10 +- 2 files changed, 81 insertions(+), 29 deletions(-) diff --git a/netconf/netconf-ssh/src/main/java/org/opendaylight/netconf/ssh/SshProxyServer.java b/netconf/netconf-ssh/src/main/java/org/opendaylight/netconf/ssh/SshProxyServer.java index f638a2bfbf..4a5ea777bb 100644 --- a/netconf/netconf-ssh/src/main/java/org/opendaylight/netconf/ssh/SshProxyServer.java +++ b/netconf/netconf-ssh/src/main/java/org/opendaylight/netconf/ssh/SshProxyServer.java @@ -8,6 +8,9 @@ package org.opendaylight.netconf.ssh; +import static java.util.Objects.requireNonNull; + +import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import io.netty.channel.EventLoopGroup; import java.io.IOException; @@ -43,15 +46,30 @@ public class SshProxyServer implements AutoCloseable { private final EventLoopGroup clientGroup; private final IoServiceFactoryFactory nioServiceWithPoolFactoryFactory; - public SshProxyServer(final ScheduledExecutorService minaTimerExecutor, - final EventLoopGroup clientGroup, final ExecutorService nioExecutor) { + private SshProxyServer(final ScheduledExecutorService minaTimerExecutor, final EventLoopGroup clientGroup, + final IoServiceFactoryFactory serviceFactory) { this.minaTimerExecutor = minaTimerExecutor; this.clientGroup = clientGroup; - this.nioServiceWithPoolFactoryFactory = - new NioServiceWithPoolFactory.NioServiceWithPoolFactoryFactory(nioExecutor); + this.nioServiceWithPoolFactoryFactory = serviceFactory; this.sshServer = SshServer.setUpDefaultServer(); } + public SshProxyServer(final ScheduledExecutorService minaTimerExecutor, + final EventLoopGroup clientGroup, final ExecutorService nioExecutor) { + this(minaTimerExecutor, clientGroup, new NioServiceWithPoolFactoryFactory(nioExecutor)); + } + + /** + * Create a server with a shared {@link AsynchronousChannelGroup}. Unlike the other constructor, this does + * not create a dedicated thread group, which is useful when you need to start a large number of servers and do + * not want to have a thread group (and hence an anonyous thread) for each of them. + */ + @VisibleForTesting + public SshProxyServer(final ScheduledExecutorService minaTimerExecutor, final EventLoopGroup clientGroup, + final AsynchronousChannelGroup group) { + this(minaTimerExecutor, clientGroup, new SharedNioServiceFactoryFactory(group)); + } + public void bind(final SshProxyServerConfiguration sshProxyServerConfiguration) throws IOException { sshServer.setHost(sshProxyServerConfiguration.getBindingAddress().getHostString()); sshServer.setPort(sshProxyServerConfiguration.getBindingAddress().getPort()); @@ -91,59 +109,85 @@ public class SshProxyServer implements AutoCloseable { } } - /** - * Based on Nio2ServiceFactory with one addition: injectable executor. - */ - private static final class NioServiceWithPoolFactory extends AbstractCloseable implements IoServiceFactory { - + private abstract static class AbstractNioServiceFactory extends AbstractCloseable implements IoServiceFactory { private final FactoryManager manager; private final AsynchronousChannelGroup group; - NioServiceWithPoolFactory(final FactoryManager manager, final ExecutorService executor) { - this.manager = manager; - try { - group = AsynchronousChannelGroup.withThreadPool(executor); - } catch (final IOException e) { - throw new RuntimeSshException(e); - } + AbstractNioServiceFactory(final FactoryManager manager, final AsynchronousChannelGroup group) { + this.manager = requireNonNull(manager); + this.group = requireNonNull(group); + } + + final AsynchronousChannelGroup group() { + return group; } @Override - public IoConnector createConnector(final IoHandler handler) { + public final IoConnector createConnector(final IoHandler handler) { return new Nio2Connector(manager, handler, group); } @Override - public IoAcceptor createAcceptor(final IoHandler handler) { + public final IoAcceptor createAcceptor(final IoHandler handler) { return new Nio2Acceptor(manager, handler, group); } + } + + /** + * Based on Nio2ServiceFactory with one addition: injectable executor. + */ + private static final class NioServiceWithPoolFactory extends AbstractNioServiceFactory { + NioServiceWithPoolFactory(final FactoryManager manager, final AsynchronousChannelGroup group) { + super(manager, group); + } @SuppressWarnings("checkstyle:IllegalCatch") @Override protected void doCloseImmediately() { try { - group.shutdownNow(); - group.awaitTermination(5, TimeUnit.SECONDS); + group().shutdownNow(); + group().awaitTermination(5, TimeUnit.SECONDS); } catch (final Exception e) { log.debug("Exception caught while closing channel group", e); } finally { super.doCloseImmediately(); } } + } - private static final class NioServiceWithPoolFactoryFactory extends Nio2ServiceFactoryFactory { + private static final class NioServiceWithPoolFactoryFactory extends Nio2ServiceFactoryFactory { + private final ExecutorService nioExecutor; - private final ExecutorService nioExecutor; + NioServiceWithPoolFactoryFactory(final ExecutorService nioExecutor) { + this.nioExecutor = nioExecutor; + } - private NioServiceWithPoolFactoryFactory(final ExecutorService nioExecutor) { - this.nioExecutor = nioExecutor; + @Override + public IoServiceFactory create(final FactoryManager manager) { + try { + return new NioServiceWithPoolFactory(manager, AsynchronousChannelGroup.withThreadPool(nioExecutor)); + } catch (final IOException e) { + throw new RuntimeSshException("Failed to create channel group", e); } + } + } - @Override - public IoServiceFactory create(final FactoryManager manager) { - return new NioServiceWithPoolFactory(manager, nioExecutor); - } + private static final class SharedNioServiceFactory extends AbstractNioServiceFactory { + SharedNioServiceFactory(final FactoryManager manager, final AsynchronousChannelGroup group) { + super(manager, group); } } + private static final class SharedNioServiceFactoryFactory extends Nio2ServiceFactoryFactory { + private final AsynchronousChannelGroup group; + + SharedNioServiceFactoryFactory(final AsynchronousChannelGroup group) { + this.group = requireNonNull(group); + } + + @Override + public IoServiceFactory create(final FactoryManager manager) { + return new SharedNioServiceFactory(manager, group); + } + } } diff --git a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/NetconfDeviceSimulator.java b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/NetconfDeviceSimulator.java index dea8fa8199..5409e242d1 100644 --- a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/NetconfDeviceSimulator.java +++ b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/NetconfDeviceSimulator.java @@ -28,6 +28,7 @@ import java.net.BindException; import java.net.Inet4Address; import java.net.InetSocketAddress; import java.net.UnknownHostException; +import java.nio.channels.AsynchronousChannelGroup; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; @@ -191,6 +192,13 @@ public class NetconfDeviceSimulator implements Closeable { // Generate key to temp folder final KeyPairProvider keyPairProvider = getPemGeneratorHostKeyProvider(); + final AsynchronousChannelGroup group; + try { + group = AsynchronousChannelGroup.withThreadPool(nioExecutor); + } catch (IOException e) { + throw new IllegalStateException("Failed to create group", e); + } + for (int i = 0; i < configuration.getDeviceCount(); i++) { if (currentPort > 65535) { LOG.warn("Port cannot be greater than 65535, stopping further attempts."); @@ -206,7 +214,7 @@ public class NetconfDeviceSimulator implements Closeable { server = dispatcher.createLocalServer(tcpLocalAddress); try { final SshProxyServer sshServer = new SshProxyServer( - minaTimerExecutor, nettyThreadgroup, nioExecutor); + minaTimerExecutor, nettyThreadgroup, group); sshServer.bind(getSshConfiguration(bindingAddress, tcpLocalAddress, keyPairProvider)); sshWrappers.add(sshServer); } catch (final BindException e) { -- 2.36.6