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

com.alibaba.nacos.naming.core.Cluster 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.annotation.JSONField;
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 org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.springframework.util.Assert;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @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-]+";
    /**
     * 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;

    @JSONField(serialize = false)
    private HealthCheckTask checkTask;

    @JSONField(serialize = false)
    private Set persistentInstances = new HashSet<>();

    @JSONField(serialize = false)
    private Set ephemeralInstances = new HashSet<>();

    @JSONField(serialize = false)
    private Service service;

    @JSONField(serialize = false)
    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(); } public int getDefIPPort() { // for compatibility with old entries return defIPPort == -1 ? defCkport : defIPPort; } public void setDefIPPort(int defIPPort) { if (defIPPort == 0) { throw new IllegalArgumentException("defIPPort can not be 0"); } this.defIPPort = defIPPort; } public List allIPs() { List allInstances = new ArrayList<>(); allInstances.addAll(persistentInstances); allInstances.addAll(ephemeralInstances); return allInstances; } public List allIPs(boolean ephemeral) { return ephemeral ? new ArrayList<>(ephemeralInstances) : new ArrayList<>(persistentInstances); } public void init() { if (inited) { return; } checkTask = new HealthCheckTask(this); HealthCheckReactor.scheduleCheck(checkTask); inited = true; } public void destroy() { if (checkTask != null) { checkTask.setCancelled(true); } } 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 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.toString(), ip.toString()); } } } 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.toString()); 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.toString()); for (Instance ip : deadIPs) { HealthCheckStatus.remv(ip); } } toUpdateInstances = new HashSet<>(ips); if (ephemeral) { ephemeralInstances = toUpdateInstances; } else { persistentInstances = toUpdateInstances; } } public List updatedIPs(Collection a, Collection b) { List intersects = (List) CollectionUtils.intersection(a, b); Map stringIPAddressMap = new ConcurrentHashMap<>(intersects.size()); for (Instance instance : intersects) { stringIPAddressMap.put(instance.getIp() + ":" + instance.getPort(), instance); } Map intersectMap = new ConcurrentHashMap<>(a.size() + b.size()); Map instanceMap = new ConcurrentHashMap<>(a.size()); Map instanceMap1 = new ConcurrentHashMap<>(a.size()); for (Instance instance : b) { if (stringIPAddressMap.containsKey(instance.getIp() + ":" + instance.getPort())) { intersectMap.put(instance.toString(), 1); } } for (Instance instance : a) { if (stringIPAddressMap.containsKey(instance.getIp() + ":" + instance.getPort())) { if (intersectMap.containsKey(instance.toString())) { intersectMap.put(instance.toString(), 2); } else { intersectMap.put(instance.toString(), 1); } } instanceMap1.put(instance.toString(), instance); } for (Map.Entry entry : intersectMap.entrySet()) { String key = entry.getKey(); Integer value = entry.getValue(); if (value == 1) { if (instanceMap1.containsKey(key)) { instanceMap.put(key, instanceMap1.get(key)); } } } return new ArrayList<>(instanceMap.values()); } public List subtract(Collection a, Collection b) { Map mapa = new HashMap<>(b.size()); for (Instance o : b) { mapa.put(o.getIp() + ":" + o.getPort(), o); } List result = new ArrayList<>(); for (Instance o : a) { if (!mapa.containsKey(o.getIp() + ":" + o.getPort())) { result.add(o); } } return result; } @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; } 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 - 2024 Weber Informatics LLC | Privacy Policy