com.alibaba.nacos.naming.core.VirtualClusterDomain Maven / Gradle / Ivy
The newest version!
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.naming.core;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.nacos.naming.healthcheck.ClientBeatCheckTask;
import com.alibaba.nacos.naming.healthcheck.ClientBeatProcessor;
import com.alibaba.nacos.naming.healthcheck.HealthCheckReactor;
import com.alibaba.nacos.naming.healthcheck.RsInfo;
import com.alibaba.nacos.naming.misc.Loggers;
import com.alibaba.nacos.naming.misc.NetUtils;
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
import com.alibaba.nacos.naming.push.PushService;
import com.alibaba.nacos.naming.raft.RaftCore;
import com.alibaba.nacos.naming.raft.RaftListener;
import com.alibaba.nacos.naming.selector.NoneSelector;
import com.alibaba.nacos.naming.selector.Selector;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.ListUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author nkorange
*/
public class VirtualClusterDomain implements Domain, RaftListener {
private static final String DOMAIN_NAME_SYNTAX = "[0-9a-zA-Z\\.:_-]+";
public static final int MINIMUM_IP_DELETE_TIMEOUT = 60 * 1000;
@JSONField(serialize = false)
private ClientBeatProcessor clientBeatProcessor = new ClientBeatProcessor();
@JSONField(serialize = false)
private ClientBeatCheckTask clientBeatCheckTask = new ClientBeatCheckTask(this);
private String name;
private String token;
private List owners = new ArrayList<>();
private Boolean resetWeight = false;
private Boolean enableHealthCheck = true;
private Boolean enabled = true;
private Boolean enableClientBeat = false;
private Selector selector = new NoneSelector();
private String namespaceId;
/**
* IP will be deleted if it has not send beat for some time, default timeout is 30 seconds.
*/
private long ipDeleteTimeout = 30 * 1000;
private volatile long lastModifiedMillis = 0L;
private boolean useSpecifiedURL = false;
private float protectThreshold = 0.0F;
private volatile String checksum;
private Map clusterMap = new HashMap();
private Map metadata = new ConcurrentHashMap<>();
public long getIpDeleteTimeout() {
return ipDeleteTimeout;
}
public void setIpDeleteTimeout(long ipDeleteTimeout) {
this.ipDeleteTimeout = ipDeleteTimeout;
}
public void processClientBeat(final RsInfo rsInfo) {
clientBeatProcessor.setDomain(this);
clientBeatProcessor.setRsInfo(rsInfo);
HealthCheckReactor.scheduleNow(clientBeatProcessor);
}
public Boolean getEnableClientBeat() {
return enableClientBeat;
}
public void setEnableClientBeat(Boolean enableClientBeat) {
this.enableClientBeat = enableClientBeat;
}
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public Boolean getEnableHealthCheck() {
return enableHealthCheck;
}
public void setEnableHealthCheck(Boolean enableHealthCheck) {
this.enableHealthCheck = enableHealthCheck;
}
public long getLastModifiedMillis() {
return lastModifiedMillis;
}
public void setLastModifiedMillis(long lastModifiedMillis) {
this.lastModifiedMillis = lastModifiedMillis;
}
public Boolean getResetWeight() {
return resetWeight;
}
public void setResetWeight(Boolean resetWeight) {
this.resetWeight = resetWeight;
}
public Map getMetadata() {
return metadata;
}
public void setMetadata(Map metadata) {
this.metadata = metadata;
}
public Selector getSelector() {
return selector;
}
public void setSelector(Selector selector) {
this.selector = selector;
}
public VirtualClusterDomain() {
}
@Override
public boolean interests(String key) {
return StringUtils.equals(key, UtilsAndCommons.IPADDRESS_DATA_ID_PRE + namespaceId + UtilsAndCommons.SERVICE_GROUP_CONNECTOR + name);
}
@Override
public boolean matchUnlistenKey(String key) {
return StringUtils.equals(key, UtilsAndCommons.IPADDRESS_DATA_ID_PRE + namespaceId + UtilsAndCommons.SERVICE_GROUP_CONNECTOR + name);
}
@Override
public void onChange(String key, String value) throws Exception {
if (StringUtils.isEmpty(value)) {
Loggers.SRV_LOG.warn("[NACOS-DOM] received empty iplist config for dom: {}", name);
return;
}
Loggers.RAFT.info("[NACOS-RAFT] datum is changed, key: {}, value: {}", key, value);
List ips = JSON.parseObject(value, new TypeReference>() {
});
for (IpAddress ip : ips) {
if (ip.getWeight() > 10000.0D) {
ip.setWeight(10000.0D);
}
if (ip.getWeight() < 0.01D && ip.getWeight() > 0.0D) {
ip.setWeight(0.01D);
}
}
updateIPs(ips);
recalculateChecksum();
}
@Override
public void onDelete(String key, String value) throws Exception {
// ignore
}
public void updateIPs(List ips) {
if (CollectionUtils.isEmpty(ips) && allIPs().size() > 1) {
return;
}
Map> ipMap = new HashMap>(clusterMap.size());
for (String clusterName : clusterMap.keySet()) {
ipMap.put(clusterName, new ArrayList());
}
for (IpAddress ip : ips) {
try {
if (ip == null) {
Loggers.SRV_LOG.error("[NACOS-DOM] received malformed ip: null");
continue;
}
if (StringUtils.isEmpty(ip.getClusterName())) {
ip.setClusterName(UtilsAndCommons.DEFAULT_CLUSTER_NAME);
}
// put wild ip into DEFAULT cluster
if (!clusterMap.containsKey(ip.getClusterName())) {
Loggers.SRV_LOG.warn("cluster of IP not found: {}", ip.toJSON());
continue;
}
List clusterIPs = ipMap.get(ip.getClusterName());
if (clusterIPs == null) {
clusterIPs = new LinkedList();
ipMap.put(ip.getClusterName(), clusterIPs);
}
clusterIPs.add(ip);
} catch (Exception e) {
Loggers.SRV_LOG.error("[NACOS-DOM] failed to process ip: " + ip, e);
}
}
for (Map.Entry> entry : ipMap.entrySet()) {
//make every ip mine
List entryIPs = entry.getValue();
clusterMap.get(entry.getKey()).updateIPs(entryIPs);
}
setLastModifiedMillis(System.currentTimeMillis());
PushService.domChanged(namespaceId, name);
StringBuilder stringBuilder = new StringBuilder();
for (IpAddress ipAddress : allIPs()) {
stringBuilder.append(ipAddress.toIPAddr()).append("_").append(ipAddress.isValid()).append(",");
}
Loggers.EVT_LOG.info("[IP-UPDATED] dom: {}, ips: {}", getName(), stringBuilder.toString());
}
@Override
public void init() {
RaftCore.listen(this);
HealthCheckReactor.scheduleCheck(clientBeatCheckTask);
for (Map.Entry entry : clusterMap.entrySet()) {
entry.getValue().init();
}
}
@Override
public void destroy() throws Exception {
for (Map.Entry entry : clusterMap.entrySet()) {
entry.getValue().destroy();
}
if (RaftCore.isLeader(NetUtils.localServer())) {
RaftCore.signalDelete(UtilsAndCommons.getIPListStoreKey(this));
}
HealthCheckReactor.cancelCheck(clientBeatCheckTask);
RaftCore.unlisten(UtilsAndCommons.getIPListStoreKey(this));
}
@Override
public List allIPs() {
List allIPs = new ArrayList();
for (Map.Entry entry : clusterMap.entrySet()) {
allIPs.addAll(entry.getValue().allIPs());
}
return allIPs;
}
public List allIPs(String tenant, String app) {
List allIPs = new ArrayList();
for (Map.Entry entry : clusterMap.entrySet()) {
if (StringUtils.isEmpty(app)) {
allIPs.addAll(entry.getValue().allIPs(tenant));
} else {
allIPs.addAll(entry.getValue().allIPs(tenant, app));
}
}
return allIPs;
}
public List allIPs(List clusters) {
List allIPs = new ArrayList();
for (String cluster : clusters) {
Cluster clusterObj = clusterMap.get(cluster);
if (clusterObj == null) {
throw new IllegalArgumentException("can not find cluster: " + cluster + ", dom:" + getName());
}
allIPs.addAll(clusterObj.allIPs());
}
return allIPs;
}
@Override
public List srvIPs(String clientIP) {
return srvIPs(clientIP, Collections.EMPTY_LIST);
}
public List srvIPs(String clientIP, List clusters) {
List ips;
if (CollectionUtils.isEmpty(clusters)) {
clusters = new ArrayList<>();
clusters.addAll(clusterMap.keySet());
}
return allIPs(clusters);
}
public static VirtualClusterDomain fromJSON(String json) {
try {
VirtualClusterDomain vDom = JSON.parseObject(json, VirtualClusterDomain.class);
for (Cluster cluster : vDom.clusterMap.values()) {
cluster.setDom(vDom);
}
return vDom;
} catch (Exception e) {
Loggers.SRV_LOG.error("[NACOS-DOM] parse cluster json content: {}, error: {}", json, e);
return null;
}
}
@Override
public String toJSON() {
return JSON.toJSONString(this);
}
@JSONField(serialize = false)
public String getDomString() {
Map