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

com.alibaba.nacos.naming.core.Cluster Maven / Gradle / Ivy

There is a newer version: 3.4.0
Show 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.nacos.naming.healthcheck.HealthCheckReactor;
import com.alibaba.nacos.naming.healthcheck.HealthCheckStatus;
import com.alibaba.nacos.naming.healthcheck.HealthCheckTask;
import com.alibaba.nacos.naming.misc.Loggers;
import com.fasterxml.jackson.annotation.JsonIgnore;

import org.apache.commons.collections.CollectionUtils;
import com.alibaba.nacos.common.utils.StringUtils;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Cluster.
 *
 * @author nkorange
 * @author jifengnan 2019-04-26
 */
public class Cluster extends com.alibaba.nacos.api.naming.pojo.Cluster implements Cloneable {
    
    private static final String CLUSTER_NAME_SYNTAX = "[0-9a-zA-Z-]+";
    
    private static final long serialVersionUID = 8940123791150907510L;
    
    /**
     * a addition for same site routing, can group multiple sites into a region, like Hangzhou, Shanghai, etc.
     */
    private String sitegroup = StringUtils.EMPTY;
    
    private int defCkport = 80;
    
    private int defIpPort = -1;
    
    @JsonIgnore
    private HealthCheckTask checkTask;
    
    @JsonIgnore
    private Set persistentInstances = new HashSet<>();
    
    @JsonIgnore
    private Set ephemeralInstances = new HashSet<>();
    
    @JsonIgnore
    private Service service;
    
    @JsonIgnore
    private volatile boolean inited = false;
    
    private Map metadata = new ConcurrentHashMap<>();
    
    public Cluster() {
    }
    
    /**
     * Create a cluster.
     *
     * 

the cluster name cannot be null, and only the arabic numerals, letters and endashes are allowed. * * @param clusterName the cluster name * @param service the service to which the current cluster belongs * @throws IllegalArgumentException the service is null, or the cluster name is null, or the cluster name is * illegal * @author jifengnan 2019-04-26 * @since 1.0.1 */ public Cluster(String clusterName, Service service) { this.setName(clusterName); this.service = service; validate(); } /** * Reason why method is not camel is that the old version has released, and the method name will be as the key * serialize and deserialize for Json. So ignore checkstyle. * * @return default port */ @SuppressWarnings("checkstyle:abbreviationaswordinname") public int getDefIPPort() { // for compatibility with old entries return defIpPort == -1 ? defCkport : defIpPort; } @SuppressWarnings("checkstyle:abbreviationaswordinname") public void setDefIPPort(int defIpPort) { if (defIpPort == 0) { throw new IllegalArgumentException("defIPPort can not be 0"); } this.defIpPort = defIpPort; } /** * Get all instances. * * @return list of instance */ public List allIPs() { List allInstances = new ArrayList<>(); allInstances.addAll(persistentInstances); allInstances.addAll(ephemeralInstances); return allInstances; } /** * Get all ephemeral or consistence instances. * * @param ephemeral whether returned instances are ephemeral * @return list of special instances */ public List allIPs(boolean ephemeral) { return ephemeral ? new ArrayList<>(ephemeralInstances) : new ArrayList<>(persistentInstances); } /** * Init cluster. */ public void init() { if (inited) { return; } checkTask = new HealthCheckTask(this); HealthCheckReactor.scheduleCheck(checkTask); inited = true; } /** * Destroy cluster. */ public void destroy() { if (checkTask != null) { checkTask.setCancelled(true); } } @JsonIgnore public HealthCheckTask getHealthCheckTask() { return checkTask; } public Service getService() { return service; } /** * Replace the service for the current cluster. * *

the service shouldn't be replaced. so if the service is not empty will nothing to do. * (the service fields can be changed, but the service A shouldn't be replaced to service B). If the service of a * cluster is required to replace, actually, a new cluster is required. * * @param service the new service */ public void setService(Service service) { if (this.service != null) { return; } this.service = service; } /** * this method has been deprecated, the service name shouldn't be changed. * * @param serviceName the service name * @author jifengnan 2019-04-26 * @since 1.0.1 */ @Deprecated @Override public void setServiceName(String serviceName) { super.setServiceName(serviceName); } /** * Get the service name of the current cluster. * *

Note that the returned service name is not the name which set by {@link #setServiceName(String)}, * but the name of the service to which the current cluster belongs. * * @return the service name of the current cluster. */ @Override public String getServiceName() { if (service != null) { return service.getName(); } else { return super.getServiceName(); } } @Override public Cluster clone() throws CloneNotSupportedException { super.clone(); Cluster cluster = new Cluster(this.getName(), service); cluster.setHealthChecker(getHealthChecker().clone()); cluster.persistentInstances = new HashSet<>(); cluster.checkTask = null; cluster.metadata = new HashMap<>(metadata); return cluster; } public boolean isEmpty() { return ephemeralInstances.isEmpty() && persistentInstances.isEmpty(); } /** * Update instance list. * * @param ips instance list * @param ephemeral whether these instances are ephemeral */ public void updateIps(List ips, boolean ephemeral) { Set toUpdateInstances = ephemeral ? ephemeralInstances : persistentInstances; HashMap oldIpMap = new HashMap<>(toUpdateInstances.size()); for (Instance ip : toUpdateInstances) { oldIpMap.put(ip.getDatumKey(), ip); } List updatedIps = updatedIps(ips, oldIpMap.values()); if (updatedIps.size() > 0) { for (Instance ip : updatedIps) { Instance oldIP = oldIpMap.get(ip.getDatumKey()); // do not update the ip validation status of updated ips // because the checker has the most precise result // Only when ip is not marked, don't we update the health status of IP: if (!ip.isMarked()) { ip.setHealthy(oldIP.isHealthy()); } if (ip.isHealthy() != oldIP.isHealthy()) { // ip validation status updated Loggers.EVT_LOG.info("{} {SYNC} IP-{} {}:{}@{}", getService().getName(), (ip.isHealthy() ? "ENABLED" : "DISABLED"), ip.getIp(), ip.getPort(), getName()); } if (ip.getWeight() != oldIP.getWeight()) { // ip validation status updated Loggers.EVT_LOG.info("{} {SYNC} {IP-UPDATED} {}->{}", getService().getName(), oldIP, ip); } } } List newIPs = subtract(ips, oldIpMap.values()); if (newIPs.size() > 0) { Loggers.EVT_LOG .info("{} {SYNC} {IP-NEW} cluster: {}, new ips size: {}, content: {}", getService().getName(), getName(), newIPs.size(), newIPs); for (Instance ip : newIPs) { HealthCheckStatus.reset(ip); } } List deadIPs = subtract(oldIpMap.values(), ips); if (deadIPs.size() > 0) { Loggers.EVT_LOG .info("{} {SYNC} {IP-DEAD} cluster: {}, dead ips size: {}, content: {}", getService().getName(), getName(), deadIPs.size(), deadIPs); for (Instance ip : deadIPs) { HealthCheckStatus.remv(ip); } } toUpdateInstances = new HashSet<>(ips); if (ephemeral) { ephemeralInstances = toUpdateInstances; } else { persistentInstances = toUpdateInstances; } } private List updatedIps(Collection newInstance, Collection oldInstance) { List intersects = (List) CollectionUtils.intersection(newInstance, oldInstance); Map stringIpAddressMap = new ConcurrentHashMap<>(intersects.size()); for (Instance instance : intersects) { stringIpAddressMap.put(instance.getIp() + ":" + instance.getPort(), instance); } Map intersectMap = new ConcurrentHashMap<>(newInstance.size() + oldInstance.size()); Map updatedInstancesMap = new ConcurrentHashMap<>(newInstance.size()); Map newInstancesMap = new ConcurrentHashMap<>(newInstance.size()); for (Instance instance : oldInstance) { if (stringIpAddressMap.containsKey(instance.getIp() + ":" + instance.getPort())) { intersectMap.put(instance.toString(), 1); } } for (Instance instance : newInstance) { if (stringIpAddressMap.containsKey(instance.getIp() + ":" + instance.getPort())) { if (intersectMap.containsKey(instance.toString())) { intersectMap.put(instance.toString(), 2); } else { intersectMap.put(instance.toString(), 1); } } newInstancesMap.put(instance.toString(), instance); } for (Map.Entry entry : intersectMap.entrySet()) { String key = entry.getKey(); Integer value = entry.getValue(); if (value == 1) { if (newInstancesMap.containsKey(key)) { updatedInstancesMap.put(key, newInstancesMap.get(key)); } } } return new ArrayList<>(updatedInstancesMap.values()); } private List subtract(Collection oldIp, Collection ips) { Map ipsMap = new HashMap<>(ips.size()); for (Instance instance : ips) { ipsMap.put(instance.getIp() + ":" + instance.getPort(), instance); } List instanceResult = new ArrayList<>(); for (Instance instance : oldIp) { if (!ipsMap.containsKey(instance.getIp() + ":" + instance.getPort())) { instanceResult.add(instance); } } return instanceResult; } @Override public int hashCode() { return Objects.hash(getName()); } @Override public boolean equals(Object obj) { if (!(obj instanceof Cluster)) { return false; } return getName().equals(((Cluster) obj).getName()); } public int getDefCkport() { return defCkport; } public void setDefCkport(int defCkport) { this.defCkport = defCkport; super.setDefaultCheckPort(defCkport); } /** * Update cluster from other cluster. * * @param cluster new cluster */ public void update(Cluster cluster) { if (!getHealthChecker().equals(cluster.getHealthChecker())) { Loggers.SRV_LOG.info("[CLUSTER-UPDATE] {}:{}:, healthChecker: {} -> {}", getService().getName(), getName(), getHealthChecker().toString(), cluster.getHealthChecker().toString()); setHealthChecker(cluster.getHealthChecker()); } if (defCkport != cluster.getDefCkport()) { Loggers.SRV_LOG .info("[CLUSTER-UPDATE] {}:{}, defCkport: {} -> {}", getService().getName(), getName(), defCkport, cluster.getDefCkport()); defCkport = cluster.getDefCkport(); } if (defIpPort != cluster.getDefIPPort()) { Loggers.SRV_LOG .info("[CLUSTER-UPDATE] {}:{}, defIPPort: {} -> {}", getService().getName(), getName(), defIpPort, cluster.getDefIPPort()); defIpPort = cluster.getDefIPPort(); } if (!StringUtils.equals(sitegroup, cluster.getSitegroup())) { Loggers.SRV_LOG .info("[CLUSTER-UPDATE] {}:{}, sitegroup: {} -> {}", getService().getName(), getName(), sitegroup, cluster.getSitegroup()); sitegroup = cluster.getSitegroup(); } if (isUseIPPort4Check() != cluster.isUseIPPort4Check()) { Loggers.SRV_LOG.info("[CLUSTER-UPDATE] {}:{}, useIPPort4Check: {} -> {}", getService().getName(), getName(), isUseIPPort4Check(), cluster.isUseIPPort4Check()); setUseIPPort4Check(cluster.isUseIPPort4Check()); } metadata = cluster.getMetadata(); } public String getSitegroup() { return sitegroup; } public void setSitegroup(String sitegroup) { this.sitegroup = sitegroup; } public boolean contains(Instance ip) { return persistentInstances.contains(ip) || ephemeralInstances.contains(ip); } /** * validate the current cluster. * *

the cluster name cannot be null, and only the arabic numerals, letters and endashes are allowed. * * @throws IllegalArgumentException the service is null, or the cluster name is null, or the cluster name is * illegal */ public void validate() { Assert.notNull(getName(), "cluster name cannot be null"); Assert.notNull(service, "service cannot be null"); if (!getName().matches(CLUSTER_NAME_SYNTAX)) { throw new IllegalArgumentException( "cluster name can only have these characters: 0-9a-zA-Z-, current: " + getName()); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy