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

com.alibaba.nacos.naming.controllers.UpgradeOpsController Maven / Gradle / Ivy

/*
 * Copyright (c) 1999-2021 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.controllers;

import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.CommonParams;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ServiceInfo;
import com.alibaba.nacos.api.naming.utils.NamingUtils;
import com.alibaba.nacos.api.selector.Selector;
import com.alibaba.nacos.auth.annotation.Secured;
import com.alibaba.nacos.auth.common.ActionTypes;
import com.alibaba.nacos.common.utils.ConvertUtils;
import com.alibaba.nacos.common.utils.JacksonUtils;
import com.alibaba.nacos.common.utils.NumberUtils;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.core.utils.WebUtils;
import com.alibaba.nacos.naming.core.InstanceOperator;
import com.alibaba.nacos.naming.core.InstanceOperatorClientImpl;
import com.alibaba.nacos.naming.core.InstanceOperatorServiceImpl;
import com.alibaba.nacos.naming.core.ServiceManager;
import com.alibaba.nacos.naming.core.ServiceOperator;
import com.alibaba.nacos.naming.core.ServiceOperatorV1Impl;
import com.alibaba.nacos.naming.core.ServiceOperatorV2Impl;
import com.alibaba.nacos.naming.core.v2.index.ServiceStorage;
import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata;
import com.alibaba.nacos.naming.core.v2.upgrade.UpgradeJudgement;
import com.alibaba.nacos.naming.core.v2.upgrade.doublewrite.delay.DoubleWriteDelayTaskEngine;
import com.alibaba.nacos.naming.misc.SwitchDomain;
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
import com.alibaba.nacos.naming.monitor.MetricsMonitor;
import com.alibaba.nacos.naming.pojo.Subscriber;
import com.alibaba.nacos.naming.pojo.instance.HttpRequestInstanceBuilder;
import com.alibaba.nacos.naming.selector.NoneSelector;
import com.alibaba.nacos.naming.selector.SelectorManager;
import com.alibaba.nacos.naming.utils.ServiceUtil;
import com.alibaba.nacos.naming.web.CanDistro;
import com.alibaba.nacos.naming.web.NamingResourceParser;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.net.URLDecoder;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP;
import static com.alibaba.nacos.api.common.Constants.SERVICE_INFO_SPLITER;
import static com.alibaba.nacos.naming.misc.UtilsAndCommons.NAMESPACE_SERVICE_CONNECTOR;

/**
 * Some API for v2 data ops when upgrading to v2.
 *
 * 

Helping to resolve some unexpected problems when upgrading. * * @author gengtuo.ygt on 2021/5/14 * @deprecated will be removed at 2.1.x */ @RestController @RequestMapping(UtilsAndCommons.NACOS_NAMING_CONTEXT + "/upgrade/ops") public class UpgradeOpsController { private final SwitchDomain switchDomain; private final ServiceManager serviceManager; private final ServiceOperatorV1Impl serviceOperatorV1; private final ServiceOperatorV2Impl serviceOperatorV2; private final InstanceOperatorServiceImpl instanceServiceV1; private final InstanceOperatorClientImpl instanceServiceV2; private final ServiceStorage serviceStorage; private final DoubleWriteDelayTaskEngine doubleWriteDelayTaskEngine; private final UpgradeJudgement upgradeJudgement; @Autowired private SelectorManager selectorManager; public UpgradeOpsController(SwitchDomain switchDomain, ServiceManager serviceManager, ServiceOperatorV1Impl serviceOperatorV1, ServiceOperatorV2Impl serviceOperatorV2, InstanceOperatorServiceImpl instanceServiceV1, InstanceOperatorClientImpl instanceServiceV2, ServiceStorage serviceStorage, DoubleWriteDelayTaskEngine doubleWriteDelayTaskEngine, UpgradeJudgement upgradeJudgement) { this.switchDomain = switchDomain; this.serviceManager = serviceManager; this.serviceOperatorV1 = serviceOperatorV1; this.serviceOperatorV2 = serviceOperatorV2; this.instanceServiceV1 = instanceServiceV1; this.instanceServiceV2 = instanceServiceV2; this.serviceStorage = serviceStorage; this.doubleWriteDelayTaskEngine = doubleWriteDelayTaskEngine; this.upgradeJudgement = upgradeJudgement; } /** * Get metrics information for upgrading view. * * @param json return json format * @return metrics about services and instances */ @GetMapping("/metrics") public String metrics(@RequestParam(required = false, defaultValue = "false") boolean json) throws NacosException { ObjectNode result = getMetrics(); if (json) { return JacksonUtils.toJson(result); } else { StringBuilder sb = new StringBuilder(); Iterator> fields = result.fields(); fields.forEachRemaining(e -> { sb.append(String.format("%-30s = ", e.getKey())); JsonNode value = e.getValue(); if (value.isIntegralNumber()) { sb.append(String.format("%5d", value.longValue())); } else if (value.isFloatingPointNumber()) { sb.append(String.format("%.3f", value.doubleValue())); } else if (value.isTextual()) { sb.append(value.textValue()); } else { sb.append(value.toString()); } sb.append('\n'); }); return sb.toString(); } } private ObjectNode getMetrics() throws NacosException { ObjectNode result = JacksonUtils.createEmptyJsonNode(); Set serviceNamesV2 = new HashSet<>(); Set persistentServiceNamesV2 = new HashSet<>(); Set ephemeralServiceNamesV2 = new HashSet<>(); int persistentInstanceCountV2 = 0; int ephemeralInstanceCountV2 = 0; Set allNamespaces = com.alibaba.nacos.naming.core.v2.ServiceManager.getInstance().getAllNamespaces(); for (String ns : allNamespaces) { Set services = com.alibaba.nacos.naming.core.v2.ServiceManager .getInstance().getSingletons(ns); for (com.alibaba.nacos.naming.core.v2.pojo.Service service : services) { String nameWithNs = service.getNamespace() + NAMESPACE_SERVICE_CONNECTOR + service.getGroupedServiceName(); serviceNamesV2.add(nameWithNs); if (service.isEphemeral()) { ephemeralServiceNamesV2.add(nameWithNs); } else { persistentServiceNamesV2.add(nameWithNs); } ServiceInfo data = serviceStorage.getPushData(service); for (com.alibaba.nacos.api.naming.pojo.Instance instance : data.getHosts()) { if (instance.isEphemeral()) { ephemeralInstanceCountV2 += 1; } else { persistentInstanceCountV2 += 1; } } } } result.put("upgraded", upgradeJudgement.isUseGrpcFeatures()); result.put("isAll20XVersion", upgradeJudgement.isAll20XVersion()); result.put("isDoubleWriteEnabled", switchDomain.isDoubleWriteEnabled()); result.put("doubleWriteDelayTaskCount", doubleWriteDelayTaskEngine.size()); result.put("serviceCountV1", serviceManager.getServiceCount()); result.put("instanceCountV1", serviceManager.getInstanceCount()); result.put("serviceCountV2", MetricsMonitor.getDomCountMonitor().get()); result.put("instanceCountV2", MetricsMonitor.getIpCountMonitor().get()); result.put("subscribeCountV2", MetricsMonitor.getSubscriberCount().get()); result.put("responsibleServiceCountV1", serviceManager.getResponsibleServiceCount()); result.put("responsibleInstanceCountV1", serviceManager.getResponsibleInstanceCount()); result.put("ephemeralServiceCountV2", ephemeralServiceNamesV2.size()); result.put("persistentServiceCountV2", persistentServiceNamesV2.size()); result.put("ephemeralInstanceCountV2", ephemeralInstanceCountV2); result.put("persistentInstanceCountV2", persistentInstanceCountV2); Set serviceNamesV1 = serviceManager.getAllServiceNames().entrySet().stream() .flatMap(e -> e.getValue().stream().map(name -> { if (!name.contains(SERVICE_INFO_SPLITER)) { name = NamingUtils.getGroupedName(name, DEFAULT_GROUP); } return e.getKey() + NAMESPACE_SERVICE_CONNECTOR + name; })).collect(Collectors.toSet()); result.put("service.V1.not.in.V2", String.join("\n", (Collection) CollectionUtils.subtract(serviceNamesV1, serviceNamesV2))); result.put("service.V2.not.in.V1", String.join("\n", (Collection) CollectionUtils.subtract(serviceNamesV2, serviceNamesV1))); return result; } /** * Create a new service. This API will create a persistence service. * * @param namespaceId namespace id * @param serviceName service name * @param protectThreshold protect threshold * @param metadata service metadata * @param selector selector * @return 'ok' if success * @throws Exception exception */ @PostMapping("/service") @Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE) public String createService(@RequestParam(defaultValue = "v2", required = false) String ver, HttpServletRequest request, @RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, @RequestParam String serviceName, @RequestParam(required = false, defaultValue = "0.0F") float protectThreshold, @RequestParam(defaultValue = StringUtils.EMPTY) String metadata, @RequestParam(defaultValue = StringUtils.EMPTY) String selector) throws Exception { ServiceMetadata serviceMetadata = new ServiceMetadata(); serviceMetadata.setProtectThreshold(protectThreshold); serviceMetadata.setSelector(parseSelector(selector)); serviceMetadata.setExtendData(UtilsAndCommons.parseMetadata(metadata)); boolean ephemeral = ConvertUtils.toBoolean( WebUtils.optional(request, "ephemeral", String.valueOf(switchDomain.isDefaultInstanceEphemeral()))); serviceMetadata.setEphemeral(ephemeral); getServiceOperator(ver).create(namespaceId, serviceName, serviceMetadata); return "ok"; } /** * Remove service. * * @param namespaceId namespace * @param serviceName service name * @return 'ok' if success * @throws Exception exception */ @DeleteMapping("/service") @Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE) public String removeService(@RequestParam(defaultValue = "v2", required = false) String ver, @RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, @RequestParam String serviceName) throws Exception { getServiceOperator(ver).delete(namespaceId, serviceName); return "ok"; } private ServiceOperator getServiceOperator(String ver) { return "v2".equals(ver) ? serviceOperatorV2 : serviceOperatorV1; } /** * Get detail of service. * * @param namespaceId namespace * @param serviceName service name * @return detail information of service * @throws NacosException nacos exception */ @GetMapping("/service") @Secured(parser = NamingResourceParser.class, action = ActionTypes.READ) public ObjectNode detailService(@RequestParam(defaultValue = "v2", required = false) String ver, @RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, @RequestParam String serviceName) throws NacosException { return getServiceOperator(ver).queryService(namespaceId, serviceName); } /** * List all service names. * * @param request http request * @return all service names * @throws Exception exception */ @GetMapping("/service/list") @Secured(parser = NamingResourceParser.class, action = ActionTypes.READ) public ObjectNode listService(@RequestParam(defaultValue = "v2", required = false) String ver, HttpServletRequest request) throws Exception { final int pageNo = NumberUtils.toInt(WebUtils.required(request, "pageNo")); final int pageSize = NumberUtils.toInt(WebUtils.required(request, "pageSize")); String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); String groupName = WebUtils.optional(request, CommonParams.GROUP_NAME, Constants.DEFAULT_GROUP); String selectorString = WebUtils.optional(request, "selector", StringUtils.EMPTY); ObjectNode result = JacksonUtils.createEmptyJsonNode(); Collection serviceNameList = getServiceOperator(ver).listService(namespaceId, groupName, selectorString); result.put("count", serviceNameList.size()); result.replace("doms", JacksonUtils.transferToJsonNode(ServiceUtil.pageServiceName(pageNo, pageSize, serviceNameList))); return result; } /** * Update service. * * @param request http request * @return 'ok' if success * @throws Exception exception */ @PutMapping("/service") @Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE) public String updateService(@RequestParam(defaultValue = "v2", required = false) String ver, HttpServletRequest request) throws Exception { String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME); ServiceMetadata serviceMetadata = new ServiceMetadata(); serviceMetadata.setProtectThreshold(NumberUtils.toFloat(WebUtils.required(request, "protectThreshold"))); serviceMetadata.setExtendData( UtilsAndCommons.parseMetadata(WebUtils.optional(request, "metadata", StringUtils.EMPTY))); serviceMetadata.setSelector(parseSelector(WebUtils.optional(request, "selector", StringUtils.EMPTY))); com.alibaba.nacos.naming.core.v2.pojo.Service service = com.alibaba.nacos.naming.core.v2.pojo.Service .newService(namespaceId, NamingUtils.getGroupName(serviceName), NamingUtils.getServiceName(serviceName)); getServiceOperator(ver).update(service, serviceMetadata); return "ok"; } /** * Search service names. * * @param namespaceId namespace * @param expr search pattern * @param responsibleOnly whether only search responsible service * @return search result */ @RequestMapping("/service/names") @Secured(parser = NamingResourceParser.class, action = ActionTypes.READ) public ObjectNode searchService(@RequestParam(defaultValue = "v2", required = false) String ver, @RequestParam(defaultValue = StringUtils.EMPTY) String namespaceId, @RequestParam(defaultValue = StringUtils.EMPTY) String expr, @RequestParam(required = false) boolean responsibleOnly) throws NacosException { Map> serviceNameMap = new HashMap<>(16); int totalCount = 0; ServiceOperator serviceOperator = getServiceOperator(ver); if (StringUtils.isNotBlank(namespaceId)) { Collection names = serviceOperator.searchServiceName(namespaceId, expr, responsibleOnly); serviceNameMap.put(namespaceId, names); totalCount = names.size(); } else { for (String each : serviceOperator.listAllNamespace()) { Collection names = serviceOperator.searchServiceName(each, expr, responsibleOnly); serviceNameMap.put(each, names); totalCount += names.size(); } } ObjectNode result = JacksonUtils.createEmptyJsonNode(); result.replace("services", JacksonUtils.transferToJsonNode(serviceNameMap)); result.put("count", totalCount); return result; } private Selector parseSelector(String selectorJsonString) throws Exception { if (StringUtils.isBlank(selectorJsonString)) { return new NoneSelector(); } JsonNode selectorJson = JacksonUtils.toObj(URLDecoder.decode(selectorJsonString, "UTF-8")); String type = Optional.ofNullable(selectorJson.get("type")) .orElseThrow(() -> new NacosException(NacosException.INVALID_PARAM, "not match any type of selector!")) .asText(); String expression = Optional.ofNullable(selectorJson.get("expression")) .map(JsonNode::asText) .orElse(null); Selector selector = selectorManager.parseSelector(type, expression); if (Objects.isNull(selector)) { throw new NacosException(NacosException.INVALID_PARAM, "not match any type of selector!"); } return selector; } /** * Register new instance. * * @param request http request * @return 'ok' if success * @throws Exception any error during register */ @CanDistro @PostMapping("/instance") @Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE) public String registerInstance(@RequestParam(defaultValue = "v2", required = false) String ver, 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); NamingUtils.checkServiceNameFormat(serviceName); final Instance instance = HttpRequestInstanceBuilder.newBuilder() .setDefaultInstanceEphemeral(switchDomain.isDefaultInstanceEphemeral()).setRequest(request).build(); getInstanceOperator(ver).registerInstance(namespaceId, serviceName, instance); return "ok"; } /** * Deregister instances. * * @param request http request * @return 'ok' if success * @throws Exception any error during deregister */ @CanDistro @DeleteMapping("/instance") @Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE) public String deregisterInstance(@RequestParam(defaultValue = "v2", required = false) String ver, HttpServletRequest request) throws Exception { Instance instance = HttpRequestInstanceBuilder.newBuilder() .setDefaultInstanceEphemeral(switchDomain.isDefaultInstanceEphemeral()).setRequest(request).build(); String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME); NamingUtils.checkServiceNameFormat(serviceName); getInstanceOperator(ver).removeInstance(namespaceId, serviceName, instance); return "ok"; } /** * Update instance. * * @param request http request * @return 'ok' if success * @throws Exception any error during update */ @CanDistro @PutMapping("/instance") @Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE) public String updateInstance(@RequestParam(defaultValue = "v2", required = false) String ver, 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); Instance instance = HttpRequestInstanceBuilder.newBuilder() .setDefaultInstanceEphemeral(switchDomain.isDefaultInstanceEphemeral()).setRequest(request).build(); getInstanceOperator(ver).updateInstance(namespaceId, serviceName, instance); return "ok"; } /** * Get all instance of input service. * * @param request http request * @return list of instance * @throws Exception any error during list */ @GetMapping("/instance/list") @Secured(parser = NamingResourceParser.class, action = ActionTypes.READ) public Object listInstance(@RequestParam(defaultValue = "v2", required = false) String ver, 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 agent = WebUtils.getUserAgent(request); String clusters = WebUtils.optional(request, "clusters", StringUtils.EMPTY); String clientIP = WebUtils.optional(request, "clientIP", StringUtils.EMPTY); int udpPort = Integer.parseInt(WebUtils.optional(request, "udpPort", "0")); boolean healthyOnly = Boolean.parseBoolean(WebUtils.optional(request, "healthyOnly", "false")); boolean isCheck = Boolean.parseBoolean(WebUtils.optional(request, "isCheck", "false")); String app = WebUtils.optional(request, "app", StringUtils.EMPTY); String env = WebUtils.optional(request, "env", StringUtils.EMPTY); String tenant = WebUtils.optional(request, "tid", StringUtils.EMPTY); Subscriber subscriber = new Subscriber(clientIP + ":" + udpPort, agent, app, clientIP, namespaceId, serviceName, udpPort, clusters); return getInstanceOperator(ver).listInstance(namespaceId, serviceName, subscriber, clusters, healthyOnly); } /** * Get detail information of specified instance. * * @param request http request * @return detail information of instance * @throws Exception any error during get */ @GetMapping("/instance") @Secured(parser = NamingResourceParser.class, action = ActionTypes.READ) public ObjectNode detailInstance(@RequestParam(defaultValue = "v2", required = false) String ver, 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 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")); com.alibaba.nacos.api.naming.pojo.Instance instance = getInstanceOperator(ver) .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; } private InstanceOperator getInstanceOperator(String ver) { return "v2".equals(ver) ? instanceServiceV2 : instanceServiceV1; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy