Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.rx.net.nameserver.NameserverImpl Maven / Gradle / Ivy
package org.rx.net.nameserver;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.rx.bean.RandomList;
import org.rx.core.*;
import org.rx.exception.InvalidException;
import org.rx.net.Sockets;
import org.rx.net.dns.DnsServer;
import org.rx.net.rpc.Remoting;
import org.rx.net.rpc.RemotingContext;
import org.rx.net.transport.TcpServer;
import org.rx.net.transport.UdpClient;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import static org.rx.core.Extends.*;
@Slf4j
public class NameserverImpl implements Nameserver {
@RequiredArgsConstructor
@ToString
static class DeregisterInfo implements Serializable {
private static final long serialVersionUID = 713672672746841635L;
final String appName;
final InetAddress address;
}
static final String NAME = "nameserver";
final NameserverConfig config;
final TcpServer rs;
@Getter
final DnsServer dnsServer;
@Setter
long syncDelay = 500;
final UdpClient ss;
final Set svrEps = ConcurrentHashMap.newKeySet();
//key String -> appName, InetAddress -> instance addr
final Map> attrs = new ConcurrentHashMap<>();
int getSyncPort() {
if (config.getSyncPort() > 0) {
return config.getSyncPort();
}
return config.getRegisterPort();
}
public Map> getInstances() {
return Linq.from(rs.getClients().values()).groupByIntoMap(p -> ifNull(p.attr(APP_NAME_KEY), "NOT_REG"),
(k, p) -> getDiscoverInfos(p.select(x -> x.getRemoteEndpoint().getAddress()).toList(), Collections.emptyList()));
}
public NameserverImpl(@NonNull NameserverConfig config) {
this.config = config;
dnsServer = new DnsServer(config.getDnsPort());
dnsServer.setTtl(config.getDnsTtl());
svrEps.addAll(Linq.from(config.getReplicaEndpoints()).select(Sockets::parseEndpoint).selectMany(Sockets::newAllEndpoints).toList());
rs = Remoting.register(this, config.getRegisterPort(), false);
rs.onDisconnected.combine((s, e) -> {
String appName = e.getClient().attr(APP_NAME_KEY);
if (appName == null) {
return;
}
doDeregister(appName, e.getClient().getRemoteEndpoint().getAddress(), true, true);
});
rs.onPing.combine((s, e) -> attrs(e.getClient().getRemoteEndpoint().getAddress())
.put("ping", String.format("%dms", (NtpClock.UTC.millis() - e.getValue().getTimestamp()) * 2)));
ss = new UdpClient(getSyncPort());
ss.onReceive.combine((s, e) -> {
Object packet = e.getValue().packet;
log.info("[{}] Replica {}", getSyncPort(), packet);
if (!tryAs(packet, Map.class, p -> {
Map> sync = (Map>) p;
for (Map.Entry> entry : sync.entrySet()) {
attrs(entry.getKey()).putAll(entry.getValue());
}
}) && !tryAs(packet, DeregisterInfo.class, p -> doDeregister(p.appName, p.address, false, false))) {
syncRegister((Set) packet);
}
});
}
public synchronized void syncRegister(@NonNull Set serverEndpoints) {
if (!svrEps.addAll(serverEndpoints) && serverEndpoints.containsAll(svrEps)) {
return;
}
dnsServer.addHosts(NAME, RandomList.DEFAULT_WEIGHT, Linq.from(svrEps).select(InetSocketAddress::getAddress).toList());
raiseEventAsync(EVENT_CLIENT_SYNC, new NEventArgs<>(svrEps));
Tasks.setTimeout(() -> {
for (InetSocketAddress ssAddr : serverEndpoints) {
ss.sendAsync(Sockets.newEndpoint(ssAddr, getSyncPort()), svrEps);
}
}, syncDelay, svrEps, Constants.TIMER_REPLACE_FLAG);
}
public void syncDeregister(@NonNull DeregisterInfo deregisterInfo) {
Tasks.setTimeout(() -> {
for (InetSocketAddress ssAddr : svrEps) {
ss.sendAsync(Sockets.newEndpoint(ssAddr, getSyncPort()), deregisterInfo);
}
}, syncDelay, DeregisterInfo.class, Constants.TIMER_REPLACE_FLAG);
}
public void syncAttributes() {
Tasks.setTimeout(() -> {
for (InetSocketAddress ssAddr : svrEps) {
ss.sendAsync(Sockets.newEndpoint(ssAddr, getSyncPort()), attrs);
}
}, syncDelay, attrs, Constants.TIMER_REPLACE_FLAG);
}
@Override
public int register(@NonNull String appName, int weight, Set serverEndpoints) {
Sys.logCtx("clientSize", rs.getClients().size());
RemotingContext ctx = RemotingContext.context();
ctx.getClient().attr(APP_NAME_KEY, appName);
InetAddress addr = ctx.getClient().getRemoteEndpoint().getAddress();
Sys.logCtx("remoteAddr", addr);
doRegister(appName, weight, addr);
syncRegister(serverEndpoints);
return config.getDnsPort();
}
void doRegister(String appName, int weight, InetAddress addr) {
if (dnsServer.addHosts(appName, weight, Collections.singletonList(addr))) {
raiseEventAsync(EVENT_APP_ADDRESS_CHANGED, new AppChangedEventArgs(appName, addr, true, attrs(addr)));
}
}
@Override
public void deregister() {
RemotingContext ctx = RemotingContext.context();
String appName = ctx.getClient().attr(APP_NAME_KEY);
if (appName == null) {
throw new InvalidException("Must register first");
}
doDeregister(appName, ctx.getClient().getRemoteEndpoint().getAddress(), false, true);
}
void doDeregister(String appName, InetAddress addr, boolean isDisconnected, boolean shouldSync) {
//Multiple instances of same app and same ip, such as k8s rolling updates.
int c = Linq.from(rs.getClients().values()).count(p -> eq(p.attr(APP_NAME_KEY), appName) && p.getRemoteEndpoint().getAddress().equals(addr));
if (c == (isDisconnected ? 0 : 1)) {
log.info("deregister {}", appName);
if (dnsServer.removeHosts(appName, Collections.singletonList(addr))) {
raiseEventAsync(EVENT_APP_ADDRESS_CHANGED, new AppChangedEventArgs(appName, addr, false, attrs(addr)));
}
if (shouldSync) {
syncDeregister(new DeregisterInfo(appName, addr));
}
}
}
@Override
public void attr(String appName, String key, T value) {
attrs(appName).put(key, value);
syncAttributes();
}
@Override
public T attr(String appName, String key) {
return (T) attrs(appName).get(key);
}
Map attrs(Object key) {
return attrs.computeIfAbsent(key, k -> new ConcurrentHashMap<>());
}
@Override
public void instanceAttr(String appName, String key, T value) {
RemotingContext ctx = RemotingContext.context();
attrs(ctx.getClient().getRemoteEndpoint().getAddress()).put(key, value);
syncAttributes();
}
@Override
public T instanceAttr(String appName, String key) {
RemotingContext ctx = RemotingContext.context();
return (T) attrs(ctx.getClient().getRemoteEndpoint().getAddress()).get(key);
}
@Override
public List discover(@NonNull String appName) {
return dnsServer.getHosts(appName);
}
@Override
public List discoverAll(@NonNull String appName, boolean exceptCurrent) {
List hosts = dnsServer.getAllHosts(appName);
if (exceptCurrent) {
RemotingContext ctx = RemotingContext.context();
hosts.remove(ctx.getClient().getRemoteEndpoint().getAddress());
}
return hosts;
}
@Override
public List discover(@NonNull String appName, List instanceAttrKeys) {
List hosts = dnsServer.getHosts(appName);
return getDiscoverInfos(hosts, instanceAttrKeys);
}
@Override
public List discoverAll(@NonNull String appName, boolean exceptCurrent, List instanceAttrKeys) {
List hosts = dnsServer.getAllHosts(appName);
if (exceptCurrent) {
RemotingContext ctx = RemotingContext.context();
hosts.remove(ctx.getClient().getRemoteEndpoint().getAddress());
}
return getDiscoverInfos(hosts, instanceAttrKeys);
}
List getDiscoverInfos(List hosts, List instanceAttrKeys) {
return Linq.from(hosts).select(p -> {
Map attrs = attrs(p);
return new InstanceInfo(p, (String) attrs.get(RxConfig.ConfigNames.APP_ID),
Linq.from(!CollectionUtils.isEmpty(instanceAttrKeys) ? instanceAttrKeys : attrs.keySet()).toMap(x -> x, attrs::get));
}).toList();
}
}