All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.rx.net.nameserver.NameserverClient Maven / Gradle / Ivy

package org.rx.net.nameserver;

import lombok.Getter;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.rx.core.Constants;
import org.rx.bean.BiTuple;
import org.rx.bean.RandomList;
import org.rx.core.*;
import org.rx.exception.InvalidException;
import org.rx.net.Sockets;
import org.rx.net.rpc.Remoting;
import org.rx.net.rpc.RpcClientConfig;
import org.rx.util.function.Action;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;

import static org.rx.core.App.*;
import static org.rx.core.Extends.*;

@Slf4j
public final class NameserverClient extends Disposable {
    static final int DEFAULT_RETRY_PERIOD = 1000;
    static final int DEFAULT_RETRY = 2;
    static final List>> LISTS = new CopyOnWriteArrayList<>();
    static final ResetEventWait syncRoot = new ResetEventWait();

    static void reInject() {
        Tasks.setTimeout(() -> {
            Linq> q = Linq.from(LISTS).selectMany(RandomList::aliveList).where(p -> p.right != null);
            if (!q.any()) {
                log.warn("At least one dns server that required");
                return;
            }

            List ns = q.select(p -> Sockets.newEndpoint(p.left, p.right)).distinct().toList();
            Sockets.injectNameService(ns);
            log.info("inject ns {}", toJsonString(ns));
            syncRoot.set();
        }, Constants.DEFAULT_INTERVAL, NameserverClient.class, TimeoutFlag.REPLACE);
    }

    public final Delegate onAppAddressChanged = Delegate.create();
    @Getter
    final String appName;
    final RandomList> hold = new RandomList<>();
    final Map> delayTasks = new ConcurrentHashMap<>();
    final Set svrEps = ConcurrentHashMap.newKeySet();

    public Set registerEndpoints() {
        return Linq.from(hold).select(p -> p.left).toSet();
    }

    public Set discoveryEndpoints() {
        return Linq.from(hold).where(p -> p.right != null).select(p -> Sockets.newEndpoint(p.left, p.right)).toSet();
    }

    public NameserverClient(String appName) {
        this.appName = appName;
        LISTS.add(hold);
    }

    @Override
    protected void freeObjects() {
        LISTS.remove(hold);
        for (BiTuple tuple : hold) {
            tryClose(tuple.middle);
        }
    }

    public void wait4Inject() throws TimeoutException {
        wait4Inject(30 * 1000);
    }

    public void wait4Inject(long timeout) throws TimeoutException {
        syncRoot.waitOne(timeout);
        syncRoot.set();
    }

    public CompletableFuture registerAsync(@NonNull String... registerEndpoints) {
        if (registerEndpoints.length == 0) {
            throw new InvalidException("At least one server that required");
        }

        return registerAsync(Linq.from(registerEndpoints).select(Sockets::parseEndpoint).toSet());
    }

    public CompletableFuture registerAsync(@NonNull Set registerEndpoints) {
        if (registerEndpoints.isEmpty()) {
            throw new InvalidException("At least one server that required");
        }
        svrEps.addAll(Linq.from(registerEndpoints).selectMany(Sockets::allEndpoints).toSet());

        return Tasks.runAsync(() -> {
            for (InetSocketAddress regEp : svrEps) {
                synchronized (hold) {
                    if (Linq.from(hold).any(p -> eq(p.left, regEp))) {
                        continue;
                    }

                    BiTuple tuple = BiTuple.of(regEp, null, null);
                    hold.add(tuple);
                    Action doReg = () -> {
                        try {
                            tuple.right = tuple.middle.register(appName, svrEps);
                            tuple.middle.instanceAttr(appName, RxConfig.ConfigNames.APP_ID, RxConfig.INSTANCE.getId());
                            reInject();
                        } catch (Throwable e) {
                            delayTasks.computeIfAbsent(appName, k -> Tasks.setTimeout(() -> {
                                tuple.right = tuple.middle.register(appName, svrEps);
                                tuple.middle.instanceAttr(appName, RxConfig.ConfigNames.APP_ID, RxConfig.INSTANCE.getId());
                                delayTasks.remove(appName); //优先
                                reInject();
                                asyncContinue(false);
                            }, DEFAULT_RETRY_PERIOD, null, TimeoutFlag.PERIOD));
                        }
                    };
                    tuple.middle = Remoting.create(Nameserver.class, RpcClientConfig.statefulMode(regEp, 0),
                            (ns, rc) -> {
                                rc.onConnected.combine((s, e) -> {
                                    hold.setWeight(tuple, RandomList.DEFAULT_WEIGHT);
                                    reInject();
                                });
                                rc.onDisconnected.combine((s, e) -> {
                                    hold.setWeight(tuple, 0);
                                    reInject();
                                });
                                rc.onReconnecting.combine((s, e) -> {
                                    if (svrEps.addAll(Linq.from(registerEndpoints).selectMany(Sockets::allEndpoints).toSet())) {
                                        registerAsync(svrEps);
                                    }
                                });
                                rc.onReconnected.combine((s, e) -> {
                                    tuple.right = null;
                                    doReg.invoke();
                                });
                                ns.>>attachEvent(Nameserver.EVENT_CLIENT_SYNC, (s, e) -> {
                                    log.info("sync server endpoints: {}", toJsonString(e.getValue()));
                                    if (e.getValue().isEmpty()) {
                                        return;
                                    }

                                    registerAsync(e.getValue());
                                }, false);
                                //onAppAddressChanged for arg#1 not work
                                ns.attachEvent(Nameserver.EVENT_APP_ADDRESS_CHANGED, (s, e) -> {
                                    log.info("app address changed: {} -> {}", e.getAppName(), e.getAddress());
                                    onAppAddressChanged.invoke(s, e);
                                }, false);
                            });
                    doReg.invoke();
                }
            }
        });
    }

    public CompletableFuture deregisterAsync() {
        return Tasks.runAsync(() -> {
            for (BiTuple tuple : hold) {
                sneakyInvoke(() -> tuple.middle.deregister(), DEFAULT_RETRY);
            }
        });
    }

    public List discover(@NonNull String appName) {
        return hold.next().middle.discover(appName);
    }

    public List discoverAll(@NonNull String appName, boolean exceptCurrent) {
        return hold.next().middle.discoverAll(appName, exceptCurrent);
    }

    public List discover(@NonNull String appName, List instanceAttrKeys) {
        return hold.next().middle.discover(appName, instanceAttrKeys);
    }

    public List discoverAll(@NonNull String appName, boolean exceptCurrent, List instanceAttrKeys) {
        return hold.next().middle.discoverAll(appName, exceptCurrent, instanceAttrKeys);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy