nats.java icon indicating copy to clipboard operation
nats.java copied to clipboard

Add better DNS resolution if possible

Open sasbury opened this issue 5 years ago • 4 comments

[Idea|Proposal] So currently, nats uses the net package to create a TCP connection to the (NATS) server. The current implementation basically takes the first IP from the domain name and uses it to create a TCP connection. For users that can’t implement different loadbalancing techniques through the DNS, using the first IP from the record is kind of a bummer and kind of forces the servers to have multiple domains (as only the first IP is used).

What would be really cool, is that before or while creating the TCP connection to the server, the dns resolution returns all the IPs and uses them as nats server endpoints to connect, enabling reconnecting to all the IPs in the record.

Let me know what you guys think!

sasbury avatar Mar 10 '19 21:03 sasbury

You can use io.nats.client.impl.DataPort and override connect. But io.nats.client.impl.NatsConnection is package class, so .........

goomario avatar Oct 18 '19 04:10 goomario

I have a solution.

package io.nats.client.impl;

import XXXX.registry.RegistryClient;
import XXXX.registry.RegistryException;
import XXXX.registry.ServiceMeta;
import io.nats.client.Options;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * Use io.nats.client.impl package to access package class io.nats.client.impl.NatsConnection
 */
public final class NatsServiceDiscoveryDataPort extends SocketDataPort {
    private static final Map<Options, RegistryClient> clients = new HashMap<>();

    /**
     * Register registry client
     *
     * @param options        registry client target options
     * @param registryClient registry client instance
     */
    public static synchronized void registerRegistryClient(Options options, RegistryClient registryClient) {
        if (Objects.nonNull(clients.putIfAbsent(options, registryClient))) {
            throw new IllegalArgumentException("Exist registry client " + options.getConnectionName());
        }
    }

    /**
     * Unregister registry client
     *
     * @param options registry client target options
     */
    public static synchronized void unregisterRegistryClient(Options options) {
        clients.remove(options);
    }

    @Override
    public void connect(String serverURI, NatsConnection conn, long timeoutNanos) throws IOException {
        RegistryClient registryClient = clients.get(conn.getOptions());
        if (registryClient == null) {
            throw new IllegalStateException("Not found registry client for nats connection");
        }
        try {
            URI uri = new URI(serverURI);
            Optional<ServiceMeta> serviceMeta = registryClient.getDiscovery().getService(uri.getHost()); // add your balance logic code to here
            if (serviceMeta.isPresent()) {
                serverURI = uri.getScheme() + "://" + serviceMeta.get().getHost() + ":" + serviceMeta.get().getPort();
            } else {
                throw new RuntimeException("Not found nats service " + uri.getHost());
            }
        } catch (RegistryException | URISyntaxException e) {
            throw new RuntimeException(e);
        }
        super.connect(serverURI, conn, timeoutNanos);
    }
}

goomario avatar Oct 18 '19 11:10 goomario

thanks felix, i have to do some maintenance on the library next week and will look at adding this.

sasbury avatar Oct 18 '19 22:10 sasbury

ugh, forgot to look for 2.6.6, i will try to get this in my brain next time

sasbury avatar Oct 25 '19 18:10 sasbury

This is supported by the users ability to override the list of servers to connect to.

scottf avatar Sep 20 '22 14:09 scottf