org.rx.net.nameserver.NameserverClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rxlib Show documentation
Show all versions of rxlib Show documentation
A set of utilities for Java
package org.rx.net.nameserver;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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.Set;
import java.util.concurrent.*;
import static org.rx.core.Extends.*;
import static org.rx.core.Sys.toJsonString;
@Slf4j
public final class NameserverClient extends Disposable {
@RequiredArgsConstructor
static class NsInfo {
final InetSocketAddress registerEndpoint;
Nameserver ns;
Future> healthTask;
volatile Integer dnsPort;
}
static final int DEFAULT_RETRY_PERIOD = 1000;
static final int DEFAULT_RETRY = 2;
static final int DEFAULT_HEALTH_PERIOD = 120 * 1000;
static final List> group = new CopyOnWriteArrayList<>();
static final ResetEventWait syncRoot = new ResetEventWait();
static void reInject() {
Tasks.setTimeout(() -> {
Linq q = Linq.from(group).selectMany(RandomList::aliveList).where(p -> p.dnsPort != null);
if (!q.any()) {
log.warn("At least one dns server that required");
return;
}
List ns = q.select(p -> Sockets.newEndpoint(p.registerEndpoint, p.dnsPort)).distinct().toList();
Sockets.injectNameService(ns);
log.info("inject ns {}", toJsonString(ns));
syncRoot.set();
}, Constants.DEFAULT_INTERVAL, NameserverClient.class, Constants.TIMER_REPLACE_FLAG);
}
public final Delegate onAppAddressChanged = Delegate.create();
@Getter
final String appName;
final RandomList holder = new RandomList<>();
final Set svrEps = ConcurrentHashMap.newKeySet();
public Set registerEndpoints() {
return Linq.from(holder).select(p -> p.registerEndpoint).toSet();
}
public Set discoveryEndpoints() {
return Linq.from(holder).where(p -> p.dnsPort != null).select(p -> Sockets.newEndpoint(p.registerEndpoint, p.dnsPort)).toSet();
}
public NameserverClient(String appName) {
this.appName = appName;
group.add(holder);
}
@Override
protected void freeObjects() {
group.remove(holder);
for (NsInfo tuple : holder) {
tryClose(tuple.ns);
}
}
public void waitInject() throws TimeoutException {
waitInject(30 * 1000);
}
public void waitInject(long timeout) throws TimeoutException {
if (!syncRoot.waitOne(timeout)) {
throw new TimeoutException("Inject timeout");
}
syncRoot.reset();
}
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::newAllEndpoints).toSet());
return Tasks.runAsync(() -> each(svrEps, regEp -> {
synchronized (holder) {
if (Linq.from(holder).any(p -> eq(p.registerEndpoint, regEp))) {
return;
}
NsInfo tuple = new NsInfo(regEp);
holder.add(tuple);
Action handshake = () -> {
try {
Integer lastDp = tuple.dnsPort;
if (eq(tuple.dnsPort = tuple.ns.register(appName, svrEps), lastDp)) {
log.debug("login ns ok {} -> {}", regEp, tuple.dnsPort);
return;
}
log.info("login ns {} -> {} PREV={}", regEp, tuple.dnsPort, lastDp);
tuple.ns.instanceAttr(appName, RxConfig.ConfigNames.APP_ID, RxConfig.INSTANCE.getId());
reInject();
} catch (Throwable e) {
log.debug("login error", e);
Tasks.setTimeout(() -> {
tuple.dnsPort = tuple.ns.register(appName, svrEps);
tuple.ns.instanceAttr(appName, RxConfig.ConfigNames.APP_ID, RxConfig.INSTANCE.getId());
reInject();
circuitContinue(false);
}, DEFAULT_RETRY_PERIOD, appName, TimeoutFlag.SINGLE.flags(TimeoutFlag.PERIOD));
}
};
RpcClientConfig config = RpcClientConfig.statefulMode(regEp, 0);
config.setInitHandler((ns, rc) -> {
rc.onConnected.combine((s, e) -> {
holder.setWeight(tuple, RandomList.DEFAULT_WEIGHT);
reInject();
});
rc.onDisconnected.combine((s, e) -> {
holder.setWeight(tuple, 0);
reInject();
});
rc.onReconnecting.combine((s, e) -> {
if (svrEps.addAll(Linq.from(registerEndpoints).selectMany(Sockets::newAllEndpoints).toSet())) {
registerAsync(svrEps);
}
});
rc.onReconnected.combine((s, e) -> {
tuple.dnsPort = null;
handshake.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);
});
tuple.ns = Remoting.createFacade(Nameserver.class, config);
tuple.healthTask = Tasks.schedulePeriod(handshake, DEFAULT_HEALTH_PERIOD);
handshake.invoke();
}
}));
}
public CompletableFuture> deregisterAsync() {
return Tasks.runAsync(() -> each(holder, p -> retry(() -> p.ns.deregister(), DEFAULT_RETRY, quietlyRecover())));
}
public List discover(@NonNull String appName) {
return holder.next().ns.discover(appName);
}
public List discoverAll(@NonNull String appName, boolean exceptCurrent) {
return holder.next().ns.discoverAll(appName, exceptCurrent);
}
public List discover(@NonNull String appName, List instanceAttrKeys) {
return holder.next().ns.discover(appName, instanceAttrKeys);
}
public List discoverAll(@NonNull String appName, boolean exceptCurrent, List instanceAttrKeys) {
return holder.next().ns.discoverAll(appName, exceptCurrent, instanceAttrKeys);
}
}