diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/InstanceController.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/InstanceController.java index 89e8c2c3e..187ce429f 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/InstanceController.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/InstanceController.java @@ -26,6 +26,7 @@ import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.core.utils.WebUtils; import com.alibaba.nacos.naming.core.Instance; import com.alibaba.nacos.naming.core.InstanceOperatorClientImpl; +import com.alibaba.nacos.naming.core.InstancePatchObject; import com.alibaba.nacos.naming.core.Service; import com.alibaba.nacos.naming.core.ServiceManager; import com.alibaba.nacos.naming.healthcheck.RsInfo; @@ -33,9 +34,9 @@ import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.misc.SwitchDomain; import com.alibaba.nacos.naming.misc.SwitchEntry; import com.alibaba.nacos.naming.misc.UtilsAndCommons; -import com.alibaba.nacos.naming.pojo.Subscriber; import com.alibaba.nacos.naming.pojo.InstanceOperationContext; import com.alibaba.nacos.naming.pojo.InstanceOperationInfo; +import com.alibaba.nacos.naming.pojo.Subscriber; import com.alibaba.nacos.naming.push.ClientInfo; import com.alibaba.nacos.naming.push.DataSource; import com.alibaba.nacos.naming.push.PushService; @@ -173,8 +174,7 @@ public class InstanceController { @PutMapping @Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE) public String update(HttpServletRequest request) throws Exception { - String namespaceId = WebUtils - .optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); + String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME); NamingUtils.checkServiceNameFormat(serviceName); instanceService.updateInstance(namespaceId, serviceName, parseInstance(request)); @@ -311,7 +311,6 @@ public class InstanceController { @PatchMapping @Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE) public String patch(HttpServletRequest request) throws Exception { - String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME); NamingUtils.checkServiceNameFormat(serviceName); String ip = WebUtils.required(request, "ip"); @@ -320,35 +319,29 @@ public class InstanceController { if (StringUtils.isBlank(cluster)) { cluster = WebUtils.optional(request, "cluster", UtilsAndCommons.DEFAULT_CLUSTER_NAME); } - - Instance instance = serviceManager.getInstance(namespaceId, serviceName, cluster, ip, Integer.parseInt(port)); - if (instance == null) { - throw new IllegalArgumentException("instance not found"); - } - + InstancePatchObject patchObject = new InstancePatchObject(cluster, ip, Integer.parseInt(port)); String metadata = WebUtils.optional(request, "metadata", StringUtils.EMPTY); if (StringUtils.isNotBlank(metadata)) { - instance.setMetadata(UtilsAndCommons.parseMetadata(metadata)); + patchObject.setMetadata(UtilsAndCommons.parseMetadata(metadata)); } String app = WebUtils.optional(request, "app", StringUtils.EMPTY); if (StringUtils.isNotBlank(app)) { - instance.setApp(app); + patchObject.setApp(app); } String weight = WebUtils.optional(request, "weight", StringUtils.EMPTY); if (StringUtils.isNotBlank(weight)) { - instance.setWeight(Double.parseDouble(weight)); + patchObject.setWeight(Double.parseDouble(weight)); } String healthy = WebUtils.optional(request, "healthy", StringUtils.EMPTY); if (StringUtils.isNotBlank(healthy)) { - instance.setHealthy(BooleanUtils.toBoolean(healthy)); + patchObject.setHealthy(BooleanUtils.toBoolean(healthy)); } String enabledString = WebUtils.optional(request, "enabled", StringUtils.EMPTY); if (StringUtils.isNotBlank(enabledString)) { - instance.setEnabled(BooleanUtils.toBoolean(enabledString)); + patchObject.setEnabled(BooleanUtils.toBoolean(enabledString)); } - instance.setLastBeat(System.currentTimeMillis()); - instance.validate(); - serviceManager.updateInstance(namespaceId, serviceName, instance); + String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); + instanceService.patchInstance(namespaceId, serviceName, patchObject); return "ok"; } @@ -403,36 +396,18 @@ public class InstanceController { String ip = WebUtils.required(request, "ip"); int port = Integer.parseInt(WebUtils.required(request, "port")); - Service service = serviceManager.getService(namespaceId, serviceName); - if (service == null) { - throw new NacosException(NacosException.NOT_FOUND, "no service " + serviceName + " found!"); - } - - List clusters = new ArrayList<>(); - clusters.add(cluster); - - List ips = service.allIPs(clusters); - if (ips == null || ips.isEmpty()) { - throw new NacosException(NacosException.NOT_FOUND, - "no ips found for cluster " + cluster + " in service " + serviceName); - } - - for (Instance instance : ips) { - if (instance.getIp().equals(ip) && instance.getPort() == port) { - ObjectNode result = JacksonUtils.createEmptyJsonNode(); - result.put("service", serviceName); - result.put("ip", ip); - result.put("port", port); - result.put("clusterName", cluster); - result.put("weight", instance.getWeight()); - result.put("healthy", instance.isHealthy()); - result.put("instanceId", instance.getInstanceId()); - result.set("metadata", JacksonUtils.transferToJsonNode(instance.getMetadata())); - return result; - } - } - - throw new NacosException(NacosException.NOT_FOUND, "no matched ip found!"); + com.alibaba.nacos.api.naming.pojo.Instance instance = instanceService + .getInstance(namespaceId, serviceName, cluster, ip, port); + ObjectNode result = JacksonUtils.createEmptyJsonNode(); + result.put("service", serviceName); + result.put("ip", ip); + result.put("port", port); + result.put("clusterName", cluster); + result.put("weight", instance.getWeight()); + result.put("healthy", instance.isHealthy()); + result.put("instanceId", instance.getInstanceId()); + result.set("metadata", JacksonUtils.transferToJsonNode(instance.getMetadata())); + return result; } /** @@ -503,21 +478,15 @@ public class InstanceController { serviceName = key; } NamingUtils.checkServiceNameFormat(serviceName); - Service service = serviceManager.getService(namespaceId, serviceName); - if (service == null) { - throw new NacosException(NacosException.NOT_FOUND, "service: " + serviceName + " not found."); - } - - List ips = service.allIPs(); + List ips = instanceService + .listAllInstances(namespaceId, serviceName); ObjectNode result = JacksonUtils.createEmptyJsonNode(); ArrayNode ipArray = JacksonUtils.createEmptyArrayNode(); - - for (Instance ip : ips) { - ipArray.add(ip.toIpAddr() + "_" + ip.isHealthy()); + for (com.alibaba.nacos.api.naming.pojo.Instance ip : ips) { + ipArray.add(ip.toInetAddr() + "_" + ip.isHealthy()); } - result.replace("ips", ipArray); return result; } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/InstanceOperator.java b/naming/src/main/java/com/alibaba/nacos/naming/core/InstanceOperator.java index 2cb54963f..286fb8272 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/InstanceOperator.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/InstanceOperator.java @@ -22,6 +22,8 @@ import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.naming.healthcheck.RsInfo; import com.alibaba.nacos.naming.pojo.Subscriber; +import java.util.List; + /** * Instance operator. * @@ -53,6 +55,8 @@ public interface InstanceOperator { * Update instance information. Due to the basic information can't be changed, so this update should only update * metadata. * + *

Update API will replace the whole metadata with new input instance. + * * @param namespaceId namespace * @param serviceName grouped service name group@@service * @param instance instance @@ -60,6 +64,19 @@ public interface InstanceOperator { */ void updateInstance(String namespaceId, String serviceName, Instance instance) throws NacosException; + /** + * Patch update instance information. Due to the basic information can't be changed, so this update should only + * update metadata. + * + *

Patch update will only update variables in requests, the others will keep original value. + * + * @param namespaceId namespace + * @param serviceName grouped service name group@@service + * @param patchObject objects need to be patch + * @throws NacosException nacos exception when update failed + */ + void patchInstance(String namespaceId, String serviceName, InstancePatchObject patchObject) throws NacosException; + /** * Get all instance of input service. * @@ -74,6 +91,20 @@ public interface InstanceOperator { ServiceInfo listInstance(String namespaceId, String serviceName, Subscriber subscriber, String cluster, boolean healthOnly) throws Exception; + /** + * Get instance detail information. + * + * @param namespaceId namespace + * @param serviceName grouped service name group@@service + * @param cluster cluster of instance + * @param ip ip of instance + * @param port port of instance + * @return instance info + * @throws NacosException nacos exception during query + */ + Instance getInstance(String namespaceId, String serviceName, String cluster, String ip, int port) + throws NacosException; + /** * Handle beat request. * @@ -100,4 +131,14 @@ public interface InstanceOperator { * @return heart beat interval */ long getHeartBeatInterval(String namespaceId, String serviceName, String ip, int port, String cluster); + + /** + * List all instances whatever status they are. + * + * @param namespaceId namespace + * @param serviceName grouped service name group@@service + * @return all instances + * @throws NacosException nacos exception during query + */ + List listAllInstances(String namespaceId, String serviceName) throws NacosException; } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/InstanceOperatorClientImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/core/InstanceOperatorClientImpl.java index eeb0453ab..c8e6abe8c 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/InstanceOperatorClientImpl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/InstanceOperatorClientImpl.java @@ -42,6 +42,8 @@ import com.alibaba.nacos.naming.misc.SwitchDomain; import com.alibaba.nacos.naming.pojo.Subscriber; import com.alibaba.nacos.naming.utils.ServiceUtil; +import java.util.HashMap; +import java.util.List; import java.util.Optional; /** @@ -66,7 +68,8 @@ public class InstanceOperatorClientImpl implements InstanceOperator { public InstanceOperatorClientImpl(ClientManagerDelegate clientManager, ClientOperationService clientOperationService, ServiceStorage serviceStorage, - NamingMetadataOperateService metadataOperateService, NamingMetadataManager metadataManager, SwitchDomain switchDomain) { + NamingMetadataOperateService metadataOperateService, NamingMetadataManager metadataManager, + SwitchDomain switchDomain) { this.clientManager = clientManager; this.clientOperationService = clientOperationService; this.serviceStorage = serviceStorage; @@ -82,9 +85,7 @@ public class InstanceOperatorClientImpl implements InstanceOperator { public void registerInstance(String namespaceId, String serviceName, Instance instance) { String clientId = instance.toInetAddr(); createIpPortClientIfAbsent(clientId, instance.isEphemeral()); - String groupName = NamingUtils.getGroupName(serviceName); - String serviceNameNoGrouped = NamingUtils.getServiceName(serviceName); - Service service = Service.newService(namespaceId, groupName, serviceNameNoGrouped, instance.isEphemeral()); + Service service = getService(namespaceId, serviceName, instance.isEphemeral()); clientOperationService.registerInstance(service, instance, clientId); } @@ -95,17 +96,13 @@ public class InstanceOperatorClientImpl implements InstanceOperator { Loggers.SRV_LOG.warn("remove instance from non-exist client: {}", clientId); return; } - String groupName = NamingUtils.getGroupName(serviceName); - String serviceNameNoGrouped = NamingUtils.getServiceName(serviceName); - Service service = Service.newService(namespaceId, groupName, serviceNameNoGrouped, instance.isEphemeral()); + Service service = getService(namespaceId, serviceName, instance.isEphemeral()); clientOperationService.deregisterInstance(service, instance, clientId); } @Override public void updateInstance(String namespaceId, String serviceName, Instance instance) throws NacosException { - String groupName = NamingUtils.getGroupName(serviceName); - String serviceNameNoGrouped = NamingUtils.getServiceName(serviceName); - Service service = Service.newService(namespaceId, groupName, serviceNameNoGrouped, instance.isEphemeral()); + Service service = getService(namespaceId, serviceName, instance.isEphemeral()); if (!ServiceManager.getInstance().containSingleton(service)) { throw new NacosException(NacosException.INVALID_PARAM, "service not found, namespace: " + namespaceId + ", service: " + service); @@ -121,12 +118,43 @@ public class InstanceOperatorClientImpl implements InstanceOperator { return result; } + @Override + public void patchInstance(String namespaceId, String serviceName, InstancePatchObject patchObject) + throws NacosException { + Service service = getService(namespaceId, serviceName, true); + Instance instance = getInstance(namespaceId, serviceName, patchObject.getCluster(), patchObject.getIp(), + patchObject.getPort()); + String instanceId = instance.toInetAddr(); + Optional instanceMetadata = metadataManager.getInstanceMetadata(service, instanceId); + InstanceMetadata newMetadata = instanceMetadata.map(this::cloneMetadata).orElseGet(InstanceMetadata::new); + mergeMetadata(newMetadata, patchObject); + metadataOperateService.updateInstanceMetadata(service, instanceId, newMetadata); + } + + private InstanceMetadata cloneMetadata(InstanceMetadata instanceMetadata) { + InstanceMetadata result = new InstanceMetadata(); + result.setExtendData(new HashMap<>(instanceMetadata.getExtendData())); + result.setWeight(instanceMetadata.getWeight()); + result.setEnabled(instanceMetadata.isEnabled()); + return result; + } + + private void mergeMetadata(InstanceMetadata newMetadata, InstancePatchObject patchObject) { + if (null != patchObject.getMetadata()) { + newMetadata.setExtendData(new HashMap<>(patchObject.getMetadata())); + } + if (null != patchObject.getEnabled()) { + newMetadata.setEnabled(patchObject.getEnabled()); + } + if (null != patchObject.getWeight()) { + newMetadata.setWeight(patchObject.getWeight()); + } + } + @Override public ServiceInfo listInstance(String namespaceId, String serviceName, Subscriber subscriber, String cluster, boolean healthOnly) { - String groupName = NamingUtils.getGroupName(serviceName); - String serviceNameNoGrouped = NamingUtils.getServiceName(serviceName); - Service service = Service.newService(namespaceId, groupName, serviceNameNoGrouped); + Service service = getService(namespaceId, serviceName, true); if (null != subscriber) { createIpPortClientIfAbsent(subscriber.getAddrStr(), true); clientOperationService.subscribeService(service, subscriber, subscriber.getAddrStr()); @@ -138,13 +166,28 @@ public class InstanceOperatorClientImpl implements InstanceOperator { return result; } + @Override + public Instance getInstance(String namespaceId, String serviceName, String cluster, String ip, int port) + throws NacosException { + Service service = getService(namespaceId, serviceName, true); + ServiceInfo serviceInfo = serviceStorage.getData(service); + if (serviceInfo.getHosts().isEmpty()) { + throw new NacosException(NacosException.NOT_FOUND, + "no ips found for cluster " + cluster + " in service " + serviceName); + } + for (Instance each : serviceInfo.getHosts()) { + if (cluster.equals(each.getClusterName()) && ip.equals(each.getIp()) && port == each.getPort()) { + return each; + } + } + throw new NacosException(NacosException.NOT_FOUND, "no matched ip found!"); + } + @Override public int handleBeat(String namespaceId, String serviceName, String ip, int port, String cluster, RsInfo clientBeat) throws NacosException { - String groupName = NamingUtils.getGroupName(serviceName); - String serviceNameNoGrouped = NamingUtils.getServiceName(serviceName); + Service service = getService(namespaceId, serviceName, true); String clientId = ip + ":" + port; - Service service = Service.newService(namespaceId, groupName, serviceNameNoGrouped); IpPortBasedClient client = (IpPortBasedClient) clientManager.getClient(clientId); if (null == client || !client.getAllPublishedService().contains(service)) { if (null == clientBeat) { @@ -181,11 +224,10 @@ public class InstanceOperatorClientImpl implements InstanceOperator { @Override public long getHeartBeatInterval(String namespaceId, String serviceName, String ip, int port, String cluster) { - String groupName = NamingUtils.getGroupName(serviceName); - String serviceNameNoGrouped = NamingUtils.getServiceName(serviceName); - Service service = Service.newService(namespaceId, groupName, serviceNameNoGrouped); + Service service = getService(namespaceId, serviceName, true); Optional metadata = metadataManager.getInstanceMetadata(service, ip); - if (metadata.isPresent() && metadata.get().getExtendData().containsKey(PreservedMetadataKeys.HEART_BEAT_INTERVAL)) { + if (metadata.isPresent() && metadata.get().getExtendData() + .containsKey(PreservedMetadataKeys.HEART_BEAT_INTERVAL)) { return ConvertUtils.toLong(metadata.get().getExtendData().get(PreservedMetadataKeys.HEART_BEAT_INTERVAL)); } String clientId = ip + ":" + port; @@ -197,9 +239,21 @@ public class InstanceOperatorClientImpl implements InstanceOperator { return switchDomain.getClientBeatInterval(); } + @Override + public List listAllInstances(String namespaceId, String serviceName) throws NacosException { + Service service = getService(namespaceId, serviceName, true); + return serviceStorage.getData(service).getHosts(); + } + private void createIpPortClientIfAbsent(String clientId, boolean ephemeral) { if (!clientManager.allClientId().contains(clientId)) { clientManager.clientConnected(new IpPortBasedClient(clientId, ephemeral)); } } + + private Service getService(String namespaceId, String serviceName, boolean ephemeral) { + String groupName = NamingUtils.getGroupName(serviceName); + String serviceNameNoGrouped = NamingUtils.getServiceName(serviceName); + return Service.newService(namespaceId, groupName, serviceNameNoGrouped, ephemeral); + } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/InstanceOperatorServiceImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/core/InstanceOperatorServiceImpl.java index 936ce85c8..e5120d449 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/InstanceOperatorServiceImpl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/InstanceOperatorServiceImpl.java @@ -106,6 +106,35 @@ public class InstanceOperatorServiceImpl implements InstanceOperator { serviceManager.updateInstance(namespaceId, serviceName, coreInstance); } + @Override + public void patchInstance(String namespaceId, String serviceName, InstancePatchObject patchObject) + throws NacosException { + com.alibaba.nacos.naming.core.Instance instance = serviceManager + .getInstance(namespaceId, serviceName, patchObject.getCluster(), patchObject.getIp(), + patchObject.getPort()); + if (instance == null) { + throw new NacosException(NacosException.INVALID_PARAM, "instance not found"); + } + if (null != patchObject.getMetadata()) { + instance.setMetadata(patchObject.getMetadata()); + } + if (null != patchObject.getApp()) { + instance.setApp(patchObject.getApp()); + } + if (null != patchObject.getEnabled()) { + instance.setEnabled(patchObject.getEnabled()); + } + if (null != patchObject.getHealthy()) { + instance.setHealthy(patchObject.getHealthy()); + } + if (null != patchObject.getApp()) { + instance.setApp(patchObject.getApp()); + } + instance.setLastBeat(System.currentTimeMillis()); + instance.validate(); + serviceManager.updateInstance(namespaceId, serviceName, instance); + } + @Override public ServiceInfo listInstance(String namespaceId, String serviceName, Subscriber subscriber, String cluster, boolean healthOnly) throws Exception { @@ -202,6 +231,28 @@ public class InstanceOperatorServiceImpl implements InstanceOperator { return result; } + @Override + public Instance getInstance(String namespaceId, String serviceName, String cluster, String ip, int port) + throws NacosException { + Service service = serviceManager.getService(namespaceId, serviceName); + if (service == null) { + throw new NacosException(NacosException.NOT_FOUND, "no service " + serviceName + " found!"); + } + List clusters = new ArrayList<>(); + clusters.add(cluster); + List ips = service.allIPs(clusters); + if (ips == null || ips.isEmpty()) { + throw new NacosException(NacosException.NOT_FOUND, + "no ips found for cluster " + cluster + " in service " + serviceName); + } + for (com.alibaba.nacos.naming.core.Instance each : ips) { + if (each.getIp().equals(ip) && each.getPort() == port) { + return each; + } + } + throw new NacosException(NacosException.NOT_FOUND, "no matched ip found!"); + } + private void checkIfDisabled(Service service) throws Exception { if (!service.getEnabled()) { throw new Exception("service is disabled now."); @@ -260,4 +311,13 @@ public class InstanceOperatorServiceImpl implements InstanceOperator { } return switchDomain.getClientBeatInterval(); } + + @Override + public List listAllInstances(String namespaceId, String serviceName) throws NacosException { + Service service = serviceManager.getService(namespaceId, serviceName); + if (service == null) { + throw new NacosException(NacosException.NOT_FOUND, "service: " + serviceName + " not found."); + } + return service.allIPs(); + } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/InstancePatchObject.java b/naming/src/main/java/com/alibaba/nacos/naming/core/InstancePatchObject.java new file mode 100644 index 000000000..5b4aa4ed9 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/InstancePatchObject.java @@ -0,0 +1,106 @@ +/* + * Copyright 1999-2020 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 javax.servlet.http.HttpServletRequest; +import java.util.Map; + +/** + * Patch object for instance update. To save which variables will be update by {@link + * com.alibaba.nacos.naming.controllers.InstanceController#patch(HttpServletRequest)} API + * + * @author xiweng.yy + */ +public class InstancePatchObject { + + private final String cluster; + + private final String ip; + + private final int port; + + private Map metadata; + + private Double weight; + + private Boolean healthy; + + private Boolean enabled; + + public InstancePatchObject(String cluster, String ip, int port) { + this.cluster = cluster; + this.ip = ip; + this.port = port; + } + + /** + * Will be deprecated in 2.x. + */ + private String app; + + public String getCluster() { + return cluster; + } + + public String getIp() { + return ip; + } + + public int getPort() { + return port; + } + + public Map getMetadata() { + return metadata; + } + + public void setMetadata(Map metadata) { + this.metadata = metadata; + } + + public Double getWeight() { + return weight; + } + + public void setWeight(Double weight) { + this.weight = weight; + } + + public Boolean getHealthy() { + return healthy; + } + + public void setHealthy(Boolean healthy) { + this.healthy = healthy; + } + + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } +}