Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how to set apache mina sshd io thread count? #518

Open
coderZoe opened this issue Jun 17, 2024 · 2 comments
Open

how to set apache mina sshd io thread count? #518

coderZoe opened this issue Jun 17, 2024 · 2 comments

Comments

@coderZoe
Copy link

Version

sshd-core 2.12.0

Bug description

Hello,

I am using sshd-core to interact with some hardware devices. My code is structured as follows:

public abstract class BaseSshSessionSender extends AbstractSessionSender {
    protected SshClient client;

    protected ClientSession session;
    protected ClientChannel channel;
    protected ScheduledExecutorService scheduledExecutorService;

    public BaseSshSessionSender(Device device, SenderMetaData metaData, ScheduledExecutorService scheduledExecutorService) {
        super(device, metaData);
        this.scheduledExecutorService = scheduledExecutorService;
    }
    
    protected void doConnect() throws Exception {
        this.client = SshClient.setUpDefaultClient();
        this.client.start();
        this.client.setGlobalRequestHandlers(Arrays.asList(KeepAliveHandler.INSTANCE, NoMoreSessionsHandler.INSTANCE));

        ConnectFuture connectFuture = client.connect(metaData.getUserName(), metaData.getIp(), metaData.getPort())
                .verify(metaData.getConnectTimeout());
        if (connectFuture.isConnected()) {
            this.session = connectFuture.getSession();
        } else {
            this.status = SenderStatus.DISCONNECT;
            throw new ConnectException(connectFuture.getException().getMessage());
        }

        this.session.addPasswordIdentity(metaData.getPassword());
        AuthFuture authFuture = this.session.auth().verify(metaData.getAuthTimeout());
        if (!authFuture.isSuccess()) {
            throw new AuthException("auth fail:" + authFuture.getException().getMessage());
        }
        this.channel = createChannel();
        this.status = SenderStatus.CONNECTING;
	}
    
    public ClientChannel createChannel() throws Exception {
        ClientChannel channel = this.session.createChannel(ClientChannel.CHANNEL_SHELL);
        channel.setStreaming(ClientChannel.Streaming.Async);
        channel.open().verify();
        channel.getAsyncOut().read(new ByteArrayBuffer(BUFFER_PIPE_SIZE)).addListener(new ChannelResponseListener(this));
        return channel;
    }
}

For each device, I create a BaseSshSessionSender object to communicate, maintaining a long connection with the device. However, I've noticed that creating such a device connection spawns cpu*2 threads. For instance, with 100 devices on an 8-core CPU, maintaining these connections requires 1600 threads, which is a substantial overhead. Given that my communication with these devices is infrequent, I do not need so many threads.

I am looking for a way to specify the number of I/O threads during connection setup, similar to how one might use bootstrap.group(new NioEventLoopGroup(1)) in Netty. Does sshd-core support this configuration? If so, how can I set it up?

Thank you for your assistance.

Actual behavior

each device connection spawns cpu*2 threads

Expected behavior

specify the number of I/O threads during connection setup

Relevant log output

No response

Other information

No response

@coderZoe
Copy link
Author

well,I have discovered another issue: there is a memory leak when reconnecting the client. Here is an example:

I register a listener for each session to monitor if the session disconnects. Once it disconnects, I attempt to reconnect proactively.

this.session.addSessionListener(new SessionListener() {
    @Override
    public void sessionClosed(Session session) {
        synchronized (BaseSshSessionSender.this){
            if(BaseSshSessionSender.this.status != SenderStatus.DISCONNECT && BaseSshSessionSender.this.status != SenderStatus.DEATH){
                BaseSshSessionSender.this.status = SenderStatus.DISCONNECT;
                BaseSshSessionSender.this.reConnect(true);
            }
        }
    }
});
protected void reConnect(boolean immediately){
    this.scheduledExecutorService.schedule(this::connect, immediately?0L:metaData.getReconnectInterval().getSeconds(), TimeUnit.SECONDS);
}

public void connect() {
    try {
        if(this.status != SenderStatus.DEATH){
            this.doConnect();
        }
    } catch (Exception e) {
        this.status = SenderStatus.DISCONNECT;
        this.reConnect(false);
    }
}

Assuming each client currently uses cpu*2 threads, a reconnection will use a new client, which creates another cpu*2 threads. However, the previously created cpu*2 threads for the client are not reclaimed. This means each reconnection increases the number of threads by cpu*2, causing the total number of threads to keep growing.

I tested this with Jprofiler, and found that at service startup, there were around 1200 sshd threads, but after about 5 minutes, there were nearly 2000 threads (because I intentionally connected some clients to unreachable networks, triggering reconnections). This situation clearly leads to a memory leak. How can this be resolved?

Thank you for your assistance.

@JinHeap
Copy link
Contributor

JinHeap commented Jul 2, 2024

SshClient.setUpDefaultClient() will bulid new client, then every time call doConnect it is new client. But as example should stop old client. maybe you can move client init (setUpDefaultClient) to BaseSshSessionSender instance init, but you should also call stop.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants