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

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

Open
d9e7381f opened this issue Nov 20, 2023 · 2 comments
Labels
feature request A request for a new feature

Comments

@d9e7381f
Copy link

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 tomaswolf added the feature request A request for a new feature label Dec 14, 2023
@tomaswolf
Copy link
Member

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
Copy link
Author

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

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

No branches or pull requests

2 participants