[ISSUE #5054] Support naming instance list with healthy protection using service.protectionThreshold. (#5055)

* [ISSUE #5054] Support naming instance list with healthy protection using service.protectionThreshold.

* [ISSUE #5054] Fix code formatting.
This commit is contained in:
Pixy Yuan 2021-03-09 11:11:40 +08:00 committed by GitHub
parent 0c35f056b0
commit 5c8559b8ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 74 additions and 29 deletions

View File

@ -57,6 +57,8 @@ public class ServiceInfo {
private volatile boolean allIPs = false; private volatile boolean allIPs = false;
private volatile boolean reachProtectionThreshold = false;
public ServiceInfo() { public ServiceInfo() {
} }
@ -283,6 +285,14 @@ public class ServiceInfo {
return str1 == null ? str2 == null : str1.equals(str2); return str1 == null ? str2 == null : str1.equals(str2);
} }
public boolean isReachProtectionThreshold() {
return reachProtectionThreshold;
}
public void setReachProtectionThreshold(boolean reachProtectionThreshold) {
this.reachProtectionThreshold = reachProtectionThreshold;
}
private static final String EMPTY = ""; private static final String EMPTY = "";
private static final String ALL_IPS = "000--00-ALL_IPS--00--000"; private static final String ALL_IPS = "000--00-ALL_IPS--00--000";

View File

@ -33,6 +33,7 @@ import com.alibaba.nacos.naming.core.v2.index.ServiceStorage;
import com.alibaba.nacos.naming.core.v2.metadata.InstanceMetadata; import com.alibaba.nacos.naming.core.v2.metadata.InstanceMetadata;
import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager;
import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataOperateService; import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataOperateService;
import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata;
import com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo; import com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo;
import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.core.v2.pojo.Service;
import com.alibaba.nacos.naming.core.v2.service.ClientOperationService; import com.alibaba.nacos.naming.core.v2.service.ClientOperationService;
@ -168,7 +169,8 @@ public class InstanceOperatorClientImpl implements InstanceOperator {
clientOperationService.subscribeService(service, subscriber, clientId); clientOperationService.subscribeService(service, subscriber, clientId);
} }
ServiceInfo serviceInfo = serviceStorage.getData(service); ServiceInfo serviceInfo = serviceStorage.getData(service);
ServiceInfo result = ServiceUtil.selectInstances(serviceInfo, cluster, healthOnly, true); ServiceMetadata serviceMetadata = metadataManager.getServiceMetadata(service).orElse(null);
ServiceInfo result = ServiceUtil.selectInstances(serviceInfo, serviceMetadata, cluster, healthOnly, true);
// adapt for v1.x sdk // adapt for v1.x sdk
result.setName(NamingUtils.getGroupedName(result.getName(), result.getGroupName())); result.setName(NamingUtils.getGroupedName(result.getName(), result.getGroupName()));
return result; return result;

View File

@ -193,16 +193,22 @@ public class InstanceOperatorServiceImpl implements InstanceOperator {
return result; return result;
} }
long total = 0;
Map<Boolean, List<com.alibaba.nacos.naming.core.Instance>> ipMap = new HashMap<>(2); Map<Boolean, List<com.alibaba.nacos.naming.core.Instance>> ipMap = new HashMap<>(2);
ipMap.put(Boolean.TRUE, new ArrayList<>()); ipMap.put(Boolean.TRUE, new ArrayList<>());
ipMap.put(Boolean.FALSE, new ArrayList<>()); ipMap.put(Boolean.FALSE, new ArrayList<>());
for (com.alibaba.nacos.naming.core.Instance ip : srvedIPs) { for (com.alibaba.nacos.naming.core.Instance ip : srvedIPs) {
// remove disabled instance:
if (!ip.isEnabled()) {
continue;
}
ipMap.get(ip.isHealthy()).add(ip); ipMap.get(ip.isHealthy()).add(ip);
total += 1;
} }
double threshold = service.getProtectThreshold(); double threshold = service.getProtectThreshold();
if ((float) ipMap.get(Boolean.TRUE).size() / srvedIPs.size() <= threshold) { if ((float) ipMap.get(Boolean.TRUE).size() / total <= threshold) {
Loggers.SRV_LOG.warn("protect threshold reached, return all ips, service: {}", serviceName); Loggers.SRV_LOG.warn("protect threshold reached, return all ips, service: {}", serviceName);
@ -210,23 +216,9 @@ public class InstanceOperatorServiceImpl implements InstanceOperator {
ipMap.get(Boolean.FALSE).clear(); ipMap.get(Boolean.FALSE).clear();
} }
List<Instance> hosts = new LinkedList<>(); List<Instance> hosts = new LinkedList<>(ipMap.get(Boolean.TRUE));
if (!healthOnly) {
for (Map.Entry<Boolean, List<com.alibaba.nacos.naming.core.Instance>> entry : ipMap.entrySet()) { hosts.addAll(ipMap.get(Boolean.FALSE));
List<com.alibaba.nacos.naming.core.Instance> ips = entry.getValue();
if (healthOnly && !entry.getKey()) {
continue;
}
for (com.alibaba.nacos.naming.core.Instance instance : ips) {
// remove disabled instance:
if (!instance.isEnabled()) {
continue;
}
hosts.add(instance);
}
} }
result.setHosts(hosts); result.setHosts(hosts);

View File

@ -25,6 +25,8 @@ import com.alibaba.nacos.auth.annotation.Secured;
import com.alibaba.nacos.auth.common.ActionTypes; import com.alibaba.nacos.auth.common.ActionTypes;
import com.alibaba.nacos.core.remote.RequestHandler; import com.alibaba.nacos.core.remote.RequestHandler;
import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; import com.alibaba.nacos.naming.core.v2.index.ServiceStorage;
import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager;
import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata;
import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.core.v2.pojo.Service;
import com.alibaba.nacos.naming.utils.ServiceUtil; import com.alibaba.nacos.naming.utils.ServiceUtil;
import com.alibaba.nacos.naming.web.NamingResourceParser; import com.alibaba.nacos.naming.web.NamingResourceParser;
@ -40,8 +42,12 @@ public class ServiceQueryRequestHandler extends RequestHandler<ServiceQueryReque
private final ServiceStorage serviceStorage; private final ServiceStorage serviceStorage;
public ServiceQueryRequestHandler(ServiceStorage serviceStorage) { private final NamingMetadataManager metadataManager;
public ServiceQueryRequestHandler(ServiceStorage serviceStorage,
NamingMetadataManager metadataManager) {
this.serviceStorage = serviceStorage; this.serviceStorage = serviceStorage;
this.metadataManager = metadataManager;
} }
@Override @Override
@ -54,7 +60,8 @@ public class ServiceQueryRequestHandler extends RequestHandler<ServiceQueryReque
String cluster = null == request.getCluster() ? "" : request.getCluster(); String cluster = null == request.getCluster() ? "" : request.getCluster();
boolean healthyOnly = request.isHealthyOnly(); boolean healthyOnly = request.isHealthyOnly();
ServiceInfo result = serviceStorage.getData(service); ServiceInfo result = serviceStorage.getData(service);
result = ServiceUtil.selectInstances(result, cluster, healthyOnly, true); ServiceMetadata serviceMetadata = metadataManager.getServiceMetadata(service).orElse(null);
result = ServiceUtil.selectInstances(result, serviceMetadata, cluster, healthyOnly, true);
return QueryServiceResponse.buildSuccessResponse(result); return QueryServiceResponse.buildSuccessResponse(result);
} }
} }

View File

@ -22,6 +22,8 @@ import com.alibaba.nacos.api.selector.SelectorType;
import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.JacksonUtils;
import com.alibaba.nacos.naming.core.Instance; import com.alibaba.nacos.naming.core.Instance;
import com.alibaba.nacos.naming.core.Service; import com.alibaba.nacos.naming.core.Service;
import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata;
import com.alibaba.nacos.naming.misc.Loggers;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -238,6 +240,20 @@ public class ServiceUtil {
*/ */
public static ServiceInfo selectInstances(ServiceInfo serviceInfo, String cluster, boolean healthyOnly, public static ServiceInfo selectInstances(ServiceInfo serviceInfo, String cluster, boolean healthyOnly,
boolean enableOnly) { boolean enableOnly) {
return selectInstances(serviceInfo, null, cluster, healthyOnly, enableOnly);
}
/**
* Select instance of service info.
*
* @param serviceInfo original service info
* @param cluster cluster of instances
* @param healthyOnly whether only select instance which healthy
* @param enableOnly whether only select instance which enabled
* @return new service info
*/
public static ServiceInfo selectInstances(ServiceInfo serviceInfo, ServiceMetadata serviceMetadata, String cluster,
boolean healthyOnly, boolean enableOnly) {
ServiceInfo result = new ServiceInfo(); ServiceInfo result = new ServiceInfo();
result.setName(serviceInfo.getName()); result.setName(serviceInfo.getName());
result.setGroupName(serviceInfo.getGroupName()); result.setGroupName(serviceInfo.getGroupName());
@ -246,12 +262,34 @@ public class ServiceUtil {
result.setClusters(cluster); result.setClusters(cluster);
Set<String> clusterSets = com.alibaba.nacos.common.utils.StringUtils.isNotBlank(cluster) ? new HashSet<>( Set<String> clusterSets = com.alibaba.nacos.common.utils.StringUtils.isNotBlank(cluster) ? new HashSet<>(
Arrays.asList(cluster.split(","))) : new HashSet<>(); Arrays.asList(cluster.split(","))) : new HashSet<>();
List<com.alibaba.nacos.api.naming.pojo.Instance> filteredInstance = new LinkedList<>(); float threshold = 0F;
for (com.alibaba.nacos.api.naming.pojo.Instance each : serviceInfo.getHosts()) { // TODO: filter ips using selector
if (checkCluster(clusterSets, each) && checkHealthy(healthyOnly, each) && checkEnabled(enableOnly, each)) { if (serviceMetadata != null) {
filteredInstance.add(each); threshold = serviceMetadata.getProtectThreshold();
}
if (threshold < 0) {
threshold = 0F;
}
long total = 0L;
Map<Boolean, List<com.alibaba.nacos.api.naming.pojo.Instance>> ipMap = new HashMap<>(2);
ipMap.put(Boolean.TRUE, new LinkedList<>());
ipMap.put(Boolean.FALSE, new LinkedList<>());
for (com.alibaba.nacos.api.naming.pojo.Instance ip : serviceInfo.getHosts()) {
if (checkCluster(clusterSets, ip) && checkEnabled(enableOnly, ip)) {
ipMap.get(ip.isHealthy()).add(ip);
total += 1;
} }
} }
if ((float) ipMap.get(Boolean.TRUE).size() / total <= threshold) {
Loggers.SRV_LOG.warn("protect threshold reached, return all ips, service: {}", serviceInfo.getName());
serviceInfo.setReachProtectionThreshold(true);
ipMap.get(Boolean.TRUE).addAll(ipMap.get(Boolean.FALSE));
ipMap.get(Boolean.FALSE).clear();
}
List<com.alibaba.nacos.api.naming.pojo.Instance> filteredInstance = ipMap.get(Boolean.TRUE);
if (!healthyOnly) {
filteredInstance.addAll(ipMap.get(Boolean.FALSE));
}
result.setHosts(filteredInstance); result.setHosts(filteredInstance);
return result; return result;
} }
@ -263,10 +301,6 @@ public class ServiceUtil {
return clusterSets.contains(instance.getClusterName()); return clusterSets.contains(instance.getClusterName());
} }
private static boolean checkHealthy(boolean healthyOnly, com.alibaba.nacos.api.naming.pojo.Instance instance) {
return !healthyOnly || instance.isHealthy();
}
private static boolean checkEnabled(boolean enableOnly, com.alibaba.nacos.api.naming.pojo.Instance instance) { private static boolean checkEnabled(boolean enableOnly, com.alibaba.nacos.api.naming.pojo.Instance instance) {
return !enableOnly || instance.isEnabled(); return !enableOnly || instance.isEnabled();
} }