apache / mina-sshd

Apache MINA sshd is a comprehensive Java library for client- and server-side SSH.
https://mina.apache.org/sshd-project/
Apache License 2.0
892 stars 357 forks source link

use client to connect to the cluster server, and the connection will only be established to one instance. #435

Open d9e7381f opened 11 months ago

d9e7381f commented 11 months ago

Version

2.6.0

Bug description

When the mina ssh client uses the domain name to request the cluster ssh server, the ssh client does not achieve load balancing.

Actual behavior

When I use mina ssh client to connect to mina ssh server, I use the domain name for access. When the domain name is a.com, the corresponding IPs of a.com are 10.0.0.1, 10.0.0.2, and 10.0.0.3, but I found that the ssh client will always only connect to the mina ssh server at 10.0.0.1, while other services are idle. . Even after the server of 10.0.0.1 hangs up, the ssh client is still requesting the instance of 10.0.0.1

Expected behavior

When using the mina ssh client to request a clustered ssh server, the ssh client balances access to each ssh server instance each time a connection is established.

Relevant log output

No response

Other information

No response

tomaswolf commented 10 months ago

Yes, like plain Java HTTP, Apache MINA sshd only uses the first IP address. In part this is hard-coded in Java's InetSocketAddress(String hostname, int port), which resolves the host name using InetAddress.getByName(hostname), which does return InetAddress.getAllByName(host)[0];.

Apache MINA sshd would need to resolve host names explicitly, and then try all returned addresses explicitly, sequentially with small time-outs or in parallel until one worked. This in itself is not trivial to do, and cancelling such a connection attempt might get tricky, especially if multiple addresses are attempted concurrently.

For the Nio2 transport, it would definitely have to be implemented directly in Apache MINA sshd. Perhaps the MINA or Netty transports have something built-in, but at least in Netty's io.netty.bootstrap.Bootstrap, which Apache MINA sshd uses to connect, it looks to me as if it also tries only one address.

Looks like quite a bit of non-trivial work, and hard to test. But it certainly is a valid feature request.

d9e7381f commented 10 months ago

@tomaswolf In my case, i will keep retrying to establish a connection to the server until it succeeds. so I overwrite some methods to do that, but I'm not sure if it will cause bad effect.

import org.apache.sshd.client.SshClient;

public class MySshClient extends SshClient {

  public static SshClient setUpDefaultClient() {
    ClientBuilder builder = ClientBuilder.builder();
    builder.factory(MySshClient::new);
    return builder.build();
  }

  protected InetSocketAddress pickSocketAddress(String host, int port) throws UnknownHostException {
    ThreadLocalRandom random = ThreadLocalRandom.current();
    final InetAddress[] inetAddresses = InetAddress.getAllByName(host);
    final InetAddress pickInetAddress = inetAddresses[random.nextInt(inetAddresses.length)];
    return new InetSocketAddress(pickInetAddress, port);
  }

  @Override
  protected ConnectFuture doConnect(
      HostConfigEntry hostConfig,
      List<HostConfigEntry> jumps,
      AttributeRepository context,
      SocketAddress localAddress)
      throws IOException {
    // some code
    String username = hostConfig.getUsername();
    if (GenericUtils.isNotEmpty(jumps)) {
      InetSocketAddress targetAddress =
          pickSocketAddress(hostConfig.getHostName(), hostConfig.getPort());
      ConnectFuture connectFuture = new DefaultConnectFuture(username + "@" + targetAddress, null);
      HostConfigEntry jump = jumps.remove(0);
      ConnectFuture f1 = doConnect(jump, jumps, context, null);
      // some code
      return connectFuture;
    } else {
      return doConnect(
          hostConfig.getUsername(),
          pickSocketAddress(host, port),
          context,
          localAddress,
          keys,
          hostConfig.isIdentitiesOnly());
    }
  }
}