Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ISSUE #3904] feature - operate instance's metadata alonely #3912

Merged
merged 22 commits into from
Oct 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,25 @@ public static String getGroupName(final String serviceNameWithGroup) {
}
return serviceNameWithGroup.split(Constants.SERVICE_INFO_SPLITER)[0];
}

/**
* check combineServiceName format. the serviceName can't be blank. some relational logic in {@link
* com.alibaba.nacos.naming.web.DistroFilter#doFilter}, it will handle combineServiceName in some case, you should
* know it.
* <pre>
* serviceName = "@@"; the length = 0; illegal
* serviceName = "group@@"; the length = 1; illegal
* serviceName = "@@serviceName"; the length = 2; legal
* serviceName = "group@@serviceName"; the length = 2; legal
* </pre>
*
* @param combineServiceName such as: groupName@@serviceName
*/
public static void checkServiceNameFormat(String combineServiceName) {
String[] split = combineServiceName.split(Constants.SERVICE_INFO_SPLITER);
if (split.length <= 1) {
throw new IllegalArgumentException(
"Param 'serviceName' is illegal, it should be format as 'groupName@@serviceName'");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,11 @@ public static void putIfValNoEmpty(Map target, Object key, Object value) {
/**
* ComputeIfAbsent lazy load.
*
* @param target target Map data.
* @param key map key.
* @param target target Map data.
* @param key map key.
* @param mappingFunction funtion which is need to be executed.
* @param param1 function's parameter value1.
* @param param2 function's parameter value1.
* @param param1 function's parameter value1.
* @param param2 function's parameter value1.
* @return
*/
@NotThreadSafe
Expand All @@ -153,5 +153,4 @@ public static Object computeIfAbsent(Map target, Object key, BiFunction mappingF
}
return val;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,16 @@
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.InstanceOperationContext;
import com.alibaba.nacos.naming.pojo.InstanceOperationInfo;
import com.alibaba.nacos.naming.push.ClientInfo;
import com.alibaba.nacos.naming.push.DataSource;
import com.alibaba.nacos.naming.push.PushService;
import com.alibaba.nacos.naming.web.CanDistro;
import com.alibaba.nacos.naming.web.DistroFilter;
import com.alibaba.nacos.naming.web.NamingResourceParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
Expand All @@ -64,6 +65,13 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import static com.alibaba.nacos.naming.misc.UtilsAndCommons.DEFAULT_CLUSTER_NAME;
import static com.alibaba.nacos.naming.misc.UtilsAndCommons.EPHEMERAL;
import static com.alibaba.nacos.naming.misc.UtilsAndCommons.PERSIST;
import static com.alibaba.nacos.naming.misc.UtilsAndCommons.UPDATE_INSTANCE_METADATA_ACTION_REMOVE;
import static com.alibaba.nacos.naming.misc.UtilsAndCommons.UPDATE_INSTANCE_METADATA_ACTION_UPDATE;

/**
* Instance operation controller.
Expand Down Expand Up @@ -119,7 +127,7 @@ public String register(HttpServletRequest request) throws Exception {
final String namespaceId = WebUtils
.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
final String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
checkServiceNameFormat(serviceName);
NamingUtils.checkServiceNameFormat(serviceName);

final Instance instance = parseInstance(request);

Expand All @@ -141,7 +149,7 @@ public String deregister(HttpServletRequest request) throws Exception {
Instance instance = getIpAddress(request);
String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
checkServiceNameFormat(serviceName);
NamingUtils.checkServiceNameFormat(serviceName);

Service service = serviceManager.getService(namespaceId, serviceName);
if (service == null) {
Expand All @@ -167,7 +175,7 @@ public String update(HttpServletRequest request) throws Exception {
final String namespaceId = WebUtils
.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
final String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
checkServiceNameFormat(serviceName);
NamingUtils.checkServiceNameFormat(serviceName);
final Instance instance = parseInstance(request);

String agent = WebUtils.getUserAgent(request);
Expand All @@ -183,6 +191,125 @@ public String update(HttpServletRequest request) throws Exception {
return "ok";
}

/**
* Batch update instance's metadata. old key exist = update, old key not exist = add.
*
* @param request http request
* @return success updated instances. such as '{"updated":["2.2.2.2:8080:unknown:xxxx-cluster:ephemeral"}'.
* @throws Exception any error during update
horizonzy marked this conversation as resolved.
Show resolved Hide resolved
* @since 1.4.0
*/
@CanDistro
@PutMapping(value = "/metadata/batch")
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
public ObjectNode batchUpdateInstanceMatadata(HttpServletRequest request) throws Exception {
horizonzy marked this conversation as resolved.
Show resolved Hide resolved
final String namespaceId = WebUtils
.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
horizonzy marked this conversation as resolved.
Show resolved Hide resolved

String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);

String consistencyType = WebUtils.optional(request, "consistencyType", StringUtils.EMPTY);

String instances = WebUtils.optional(request, "instances", StringUtils.EMPTY);

List<Instance> targetInstances = parseBatchInstances(instances);

String metadata = WebUtils.required(request, "metadata");
Map<String, String> targetMetadata = UtilsAndCommons.parseMetadata(metadata);

List<Instance> operatedInstances = batchOperateMetadata(namespaceId,
buildOperationInfo(serviceName, consistencyType, targetInstances), targetMetadata,
UPDATE_INSTANCE_METADATA_ACTION_UPDATE);

ObjectNode result = JacksonUtils.createEmptyJsonNode();
ArrayNode ipArray = JacksonUtils.createEmptyArrayNode();

for (Instance ip : operatedInstances) {
ipArray.add(ip.getDatumKey() + ":" + (ip.isEphemeral() ? EPHEMERAL : PERSIST));
}

result.replace("updated", ipArray);
return result;
}

/**
* Batch delete instance's metadata. old key exist = delete, old key not exist = not operate
*
* @param request http request
* @return success updated instances. such as '{"updated":["2.2.2.2:8080:unknown:xxxx-cluster:ephemeral"}'.
* @throws Exception any error during update
horizonzy marked this conversation as resolved.
Show resolved Hide resolved
* @since 1.4.0
*/
@CanDistro
@DeleteMapping("/metadata/batch")
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
public ObjectNode batchDeleteInstanceMatadata(HttpServletRequest request) throws Exception {
final String namespaceId = WebUtils
.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);

String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);

String consistencyType = WebUtils.optional(request, "consistencyType", StringUtils.EMPTY);

String instances = WebUtils.optional(request, "instances", StringUtils.EMPTY);

List<Instance> targetInstances = parseBatchInstances(instances);

String metadata = WebUtils.required(request, "metadata");
Map<String, String> targetMetadata = UtilsAndCommons.parseMetadata(metadata);

List<Instance> operatedInstances = batchOperateMetadata(namespaceId,
buildOperationInfo(serviceName, consistencyType, targetInstances), targetMetadata,
UPDATE_INSTANCE_METADATA_ACTION_REMOVE);

ObjectNode result = JacksonUtils.createEmptyJsonNode();
ArrayNode ipArray = JacksonUtils.createEmptyArrayNode();

for (Instance ip : operatedInstances) {
ipArray.add(ip.getDatumKey() + ":" + (ip.isEphemeral() ? EPHEMERAL : PERSIST));
}

result.replace("updated", ipArray);
return result;
}

private InstanceOperationInfo buildOperationInfo(String serviceName, String consistencyType,
List<Instance> instances) {
if (!CollectionUtils.isEmpty(instances)) {
for (Instance instance : instances) {
if (StringUtils.isBlank(instance.getClusterName())) {
instance.setClusterName(DEFAULT_CLUSTER_NAME);
}
}
}
return new InstanceOperationInfo(serviceName, consistencyType, instances);
}

private List<Instance> parseBatchInstances(String instances) {
try {
return JacksonUtils.toObj(instances, new TypeReference<List<Instance>>() {
});
} catch (Exception e) {
Loggers.SRV_LOG.warn("UPDATE-METADATA: Param 'instances' is illegal, ignore this operation", e);
}
return null;
}

private List<Instance> batchOperateMetadata(String namespace, InstanceOperationInfo instanceOperationInfo,
Map<String, String> metadata, String action) {
Function<InstanceOperationContext, List<Instance>> operateFunction = instanceOperationContext -> {
try {
return serviceManager.updateMetadata(instanceOperationContext.getNamespace(),
instanceOperationContext.getServiceName(), instanceOperationContext.getEphemeral(), action,
instanceOperationContext.getAll(), instanceOperationContext.getInstances(), metadata);
} catch (NacosException e) {
Loggers.SRV_LOG.warn("UPDATE-METADATA: updateMetadata failed", e);
}
return new ArrayList<>();
};
return serviceManager.batchOperate(namespace, instanceOperationInfo, operateFunction);
}

/**
* Patch instance.
*
Expand All @@ -196,7 +323,7 @@ public String update(HttpServletRequest request) throws Exception {
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);
checkServiceNameFormat(serviceName);
NamingUtils.checkServiceNameFormat(serviceName);
String ip = WebUtils.required(request, "ip");
String port = WebUtils.required(request, "port");
String cluster = WebUtils.optional(request, CommonParams.CLUSTER_NAME, StringUtils.EMPTY);
Expand Down Expand Up @@ -248,7 +375,7 @@ public ObjectNode list(HttpServletRequest request) throws Exception {

String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
checkServiceNameFormat(serviceName);
NamingUtils.checkServiceNameFormat(serviceName);

String agent = WebUtils.getUserAgent(request);
String clusters = WebUtils.optional(request, "clusters", StringUtils.EMPTY);
Expand Down Expand Up @@ -280,7 +407,7 @@ public ObjectNode detail(HttpServletRequest request) throws Exception {

String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
checkServiceNameFormat(serviceName);
NamingUtils.checkServiceNameFormat(serviceName);
String cluster = WebUtils.optional(request, CommonParams.CLUSTER_NAME, UtilsAndCommons.DEFAULT_CLUSTER_NAME);
String ip = WebUtils.required(request, "ip");
int port = Integer.parseInt(WebUtils.required(request, "port"));
Expand Down Expand Up @@ -353,7 +480,7 @@ public ObjectNode beat(HttpServletRequest request) throws Exception {
}
String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
checkServiceNameFormat(serviceName);
NamingUtils.checkServiceNameFormat(serviceName);
Loggers.SRV_LOG.debug("[CLIENT-BEAT] full arguments: beat: {}, serviceName: {}", clientBeat, serviceName);
Instance instance = serviceManager.getInstance(namespaceId, serviceName, clusterName, ip, port);

Expand Down Expand Up @@ -421,7 +548,7 @@ public ObjectNode listWithHealthStatus(@RequestParam String key) throws NacosExc
namespaceId = Constants.DEFAULT_NAMESPACE_ID;
serviceName = key;
}
checkServiceNameFormat(serviceName);
NamingUtils.checkServiceNameFormat(serviceName);
Service service = serviceManager.getService(namespaceId, serviceName);

if (service == null) {
Expand All @@ -441,26 +568,6 @@ public ObjectNode listWithHealthStatus(@RequestParam String key) throws NacosExc
return result;
}

/**
* check combineServiceName format. the serviceName can't be blank. some relational logic in {@link
* DistroFilter#doFilter}, it will handle combineServiceName in some case, you should know it.
* <pre>
* serviceName = "@@"; the length = 0; illegal
* serviceName = "group@@"; the length = 1; illegal
* serviceName = "@@serviceName"; the length = 2; legal
* serviceName = "group@@serviceName"; the length = 2; legal
* </pre>
*
* @param combineServiceName such as: groupName@@serviceName
*/
private void checkServiceNameFormat(String combineServiceName) {
String[] split = combineServiceName.split(Constants.SERVICE_INFO_SPLITER);
if (split.length <= 1) {
throw new IllegalArgumentException(
"Param 'serviceName' is illegal, it should be format as 'groupName@@serviceName");
}
}

private Instance parseInstance(HttpServletRequest request) throws Exception {

String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
Expand All @@ -483,12 +590,7 @@ private Instance parseInstance(HttpServletRequest request) throws Exception {
}

private Instance getIpAddress(HttpServletRequest request) {
final String ip = WebUtils.required(request, "ip");
final String port = WebUtils.required(request, "port");
String cluster = WebUtils.optional(request, CommonParams.CLUSTER_NAME, StringUtils.EMPTY);
if (StringUtils.isBlank(cluster)) {
cluster = WebUtils.optional(request, "cluster", UtilsAndCommons.DEFAULT_CLUSTER_NAME);
}

String enabledString = WebUtils.optional(request, "enabled", StringUtils.EMPTY);
boolean enabled;
if (StringUtils.isBlank(enabledString)) {
Expand All @@ -497,20 +599,33 @@ private Instance getIpAddress(HttpServletRequest request) {
enabled = BooleanUtils.toBoolean(enabledString);
}

boolean ephemeral = BooleanUtils.toBoolean(
WebUtils.optional(request, "ephemeral", String.valueOf(switchDomain.isDefaultInstanceEphemeral())));

String weight = WebUtils.optional(request, "weight", "1");
boolean healthy = BooleanUtils.toBoolean(WebUtils.optional(request, "healthy", "true"));

Instance instance = new Instance();
instance.setPort(Integer.parseInt(port));
instance.setIp(ip);
Instance instance = getBasicIpAddress(request);
instance.setWeight(Double.parseDouble(weight));
instance.setClusterName(cluster);
instance.setHealthy(healthy);
instance.setEnabled(enabled);

return instance;
}

private Instance getBasicIpAddress(HttpServletRequest request) {

final String ip = WebUtils.required(request, "ip");
final String port = WebUtils.required(request, "port");
String cluster = WebUtils.optional(request, CommonParams.CLUSTER_NAME, StringUtils.EMPTY);
if (StringUtils.isBlank(cluster)) {
cluster = WebUtils.optional(request, "cluster", UtilsAndCommons.DEFAULT_CLUSTER_NAME);
}
boolean ephemeral = BooleanUtils.toBoolean(
WebUtils.optional(request, "ephemeral", String.valueOf(switchDomain.isDefaultInstanceEphemeral())));

Instance instance = new Instance();
instance.setPort(Integer.parseInt(port));
instance.setIp(ip);
instance.setEphemeral(ephemeral);
instance.setClusterName(cluster);

return instance;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import com.alibaba.nacos.core.cluster.Member;
import com.alibaba.nacos.core.cluster.NodeState;
import com.alibaba.nacos.core.cluster.ServerMemberManager;
import com.alibaba.nacos.sys.utils.ApplicationUtils;
import com.alibaba.nacos.naming.cluster.ServerListManager;
import com.alibaba.nacos.naming.cluster.ServerStatusManager;
import com.alibaba.nacos.naming.consistency.persistent.raft.RaftCore;
Expand All @@ -36,10 +35,10 @@
import com.alibaba.nacos.naming.misc.SwitchManager;
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
import com.alibaba.nacos.naming.push.PushService;
import com.alibaba.nacos.sys.utils.ApplicationUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
Expand Down
Loading