diff --git a/api/src/main/java/com/alibaba/nacos/api/common/Constants.java b/api/src/main/java/com/alibaba/nacos/api/common/Constants.java index 11ae33b54..e8b538571 100644 --- a/api/src/main/java/com/alibaba/nacos/api/common/Constants.java +++ b/api/src/main/java/com/alibaba/nacos/api/common/Constants.java @@ -109,7 +109,7 @@ public class Constants { public static final String WORD_SEPARATOR = Character.toString((char)2); - public static final String LONGPULLING_LINE_SEPARATOR = "\r\n"; + public static final String LONGPOLLING_LINE_SEPARATOR = "\r\n"; public static final String CLIENT_APPNAME_HEADER = "Client-AppName"; public static final String CLIENT_REQUEST_TS_HEADER = "Client-RequestTS"; diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java b/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java index 0eaa81a57..48f6ae5f8 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java @@ -92,7 +92,17 @@ public interface NamingService { List getAllInstances(String serviceName) throws NacosException; /** - * get all instances within specified clusters of a service + * Get all instances of a service + * + * @param serviceName name of service + * @param subscribe if subscribe the service + * @return A list of instance + * @throws NacosException + */ + List getAllInstances(String serviceName, boolean subscribe) throws NacosException; + + /** + * Get all instances within specified clusters of a service * * @param serviceName name of service * @param clusters list of cluster @@ -102,7 +112,18 @@ public interface NamingService { List getAllInstances(String serviceName, List clusters) throws NacosException; /** - * get qualified instances of service + * Get all instances within specified clusters of a service + * + * @param serviceName name of service + * @param clusters list of cluster + * @param subscribe if subscribe the service + * @return A list of qualified instance + * @throws NacosException + */ + List getAllInstances(String serviceName, List clusters, boolean subscribe) throws NacosException; + + /** + * Get qualified instances of service * * @param serviceName name of service * @param healthy a flag to indicate returning healthy or unhealthy instances @@ -112,7 +133,18 @@ public interface NamingService { List selectInstances(String serviceName, boolean healthy) throws NacosException; /** - * get qualified instances within specified clusters of service + * Get qualified instances of service + * + * @param serviceName name of service + * @param healthy a flag to indicate returning healthy or unhealthy instances + * @param subscribe if subscribe the service + * @return A qualified list of instance + * @throws NacosException + */ + List selectInstances(String serviceName, boolean healthy, boolean subscribe) throws NacosException; + + /** + * Get qualified instances within specified clusters of service * * @param serviceName name of service * @param clusters list of cluster @@ -123,7 +155,19 @@ public interface NamingService { List selectInstances(String serviceName, List clusters, boolean healthy) throws NacosException; /** - * select one healthy instance of service using predefined load balance strategy + * Get qualified instances within specified clusters of service + * + * @param serviceName name of service + * @param clusters list of cluster + * @param healthy a flag to indicate returning healthy or unhealthy instances + * @param subscribe if subscribe the service + * @return A qualified list of instance + * @throws NacosException + */ + List selectInstances(String serviceName, List clusters, boolean healthy, boolean subscribe) throws NacosException; + + /** + * Select one healthy instance of service using predefined load balance strategy * * @param serviceName name of service * @return qualified instance @@ -135,6 +179,16 @@ public interface NamingService { * select one healthy instance of service using predefined load balance strategy * * @param serviceName name of service + * @param subscribe if subscribe the service + * @return qualified instance + * @throws NacosException + */ + Instance selectOneHealthyInstance(String serviceName, boolean subscribe) throws NacosException; + + /** + * Select one healthy instance of service using predefined load balance strategy + * + * @param serviceName name of service * @param clusters a list of clusters should the instance belongs to * @return qualified instance * @throws NacosException @@ -142,7 +196,18 @@ public interface NamingService { Instance selectOneHealthyInstance(String serviceName, List clusters) throws NacosException; /** - * subscribe service to receive events of instances alteration + * Select one healthy instance of service using predefined load balance strategy + * + * @param serviceName name of service + * @param clusters a list of clusters should the instance belongs to + * @param subscribe if subscribe the service + * @return qualified instance + * @throws NacosException + */ + Instance selectOneHealthyInstance(String serviceName, List clusters, boolean subscribe) throws NacosException; + + /** + * Subscribe service to receive events of instances alteration * * @param serviceName name of service * @param listener event listener diff --git a/client/pom.xml b/client/pom.xml index 2ebf9cfe1..c3e578427 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -107,5 +107,10 @@ test + + io.micrometer + micrometer-core + + diff --git a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java index 3b7680422..e34b207dd 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java @@ -23,10 +23,12 @@ import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.config.filter.impl.ConfigFilterChainManager; import com.alibaba.nacos.client.config.filter.impl.ConfigRequest; import com.alibaba.nacos.client.config.filter.impl.ConfigResponse; +import com.alibaba.nacos.client.config.http.HttpAgent; +import com.alibaba.nacos.client.config.http.MetricsHttpAgent; +import com.alibaba.nacos.client.config.http.ServerHttpAgent; import com.alibaba.nacos.client.config.impl.ClientWorker; import com.alibaba.nacos.client.config.impl.HttpSimpleClient.HttpResult; import com.alibaba.nacos.client.config.impl.LocalConfigInfoProcessor; -import com.alibaba.nacos.client.config.impl.ServerHttpAgent; import com.alibaba.nacos.client.config.utils.ContentUtils; import com.alibaba.nacos.client.config.utils.LogUtils; import com.alibaba.nacos.client.config.utils.ParamUtils; @@ -55,9 +57,9 @@ public class NacosConfigService implements ConfigService { /** * http agent */ - private ServerHttpAgent agent; + private HttpAgent agent; /** - * longpulling + * longpolling */ private ClientWorker worker; private String namespace; @@ -79,7 +81,7 @@ public class NacosConfigService implements ConfigService { namespace = namespaceTmp; properties.put(PropertyKeyConst.NAMESPACE, namespace); } - agent = new ServerHttpAgent(properties); + agent = new MetricsHttpAgent(new ServerHttpAgent(properties)); agent.start(); worker = new ClientWorker(agent, configFilterChainManager); } @@ -131,9 +133,11 @@ public class NacosConfigService implements ConfigService { try { content = worker.getServerConfig(dataId, group, tenant, timeoutMs); + cr.setContent(content); configFilterChainManager.doFilter(null, cr); content = cr.getContent(); + return content; } catch (NacosException ioe) { if (NacosException.NO_RIGHT == ioe.getErrCode()) { diff --git a/client/src/main/java/com/alibaba/nacos/client/config/http/HttpAgent.java b/client/src/main/java/com/alibaba/nacos/client/config/http/HttpAgent.java new file mode 100644 index 000000000..1b30bcc1e --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/config/http/HttpAgent.java @@ -0,0 +1,98 @@ +/* + * 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.client.config.http; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.client.config.impl.HttpSimpleClient.HttpResult; + +import java.io.IOException; +import java.util.List; + + +/** + * HttpAgent + * + * @author Nacos + */ +public interface HttpAgent { + /** + * start to get nacos ip list + * @return Nothing. + * @throws NacosException on get ip list error. + */ + void start() throws NacosException; + + /** + * invoke http get method + * @param path http path + * @param headers http headers + * @param paramValues http paramValues http + * @param encoding http encode + * @param readTimeoutMs http timeout + * @return HttpResult http response + * @throws IOException If an input or output exception occurred + */ + + HttpResult httpGet(String path, List headers, List paramValues, String encoding, long readTimeoutMs) throws IOException; + + /** + * invoke http post method + * @param path http path + * @param headers http headers + * @param paramValues http paramValues http + * @param encoding http encode + * @param readTimeoutMs http timeout + * @return HttpResult http response + * @throws IOException If an input or output exception occurred + */ + HttpResult httpPost(String path, List headers, List paramValues, String encoding, long readTimeoutMs) throws IOException; + + /** + * invoke http delete method + * @param path http path + * @param headers http headers + * @param paramValues http paramValues http + * @param encoding http encode + * @param readTimeoutMs http timeout + * @return HttpResult http response + * @throws IOException If an input or output exception occurred + */ + HttpResult httpDelete(String path, List headers, List paramValues, String encoding, long readTimeoutMs) throws IOException; + + /** + * get name + * @return String + */ + String getName(); + + /** + * get namespace + * @return String + */ + String getNamespace(); + + /** + * get tenant + * @return String + */ + String getTenant(); + + /** + * get encode + * @return String + */ + String getEncode(); +} diff --git a/client/src/main/java/com/alibaba/nacos/client/config/http/MetricsHttpAgent.java b/client/src/main/java/com/alibaba/nacos/client/config/http/MetricsHttpAgent.java new file mode 100644 index 000000000..eab97cea8 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/config/http/MetricsHttpAgent.java @@ -0,0 +1,119 @@ +/* + * 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.client.config.http; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.client.config.impl.HttpSimpleClient.HttpResult; +import com.alibaba.nacos.client.monitor.MetricsMonitor; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * MetricsHttpAgent + * + * @author Nacos + */ +public class MetricsHttpAgent implements HttpAgent { + private HttpAgent httpAgent; + + public MetricsHttpAgent(HttpAgent httpAgent) { + this.httpAgent = httpAgent; + } + + @Override + public void start() throws NacosException { + httpAgent.start(); + } + + @Override + public HttpResult httpGet(String path, List headers, List paramValues, String encoding, long readTimeoutMs) throws IOException { + long start = System.currentTimeMillis(); + long end = 0; + HttpResult result = null; + try { + result = httpAgent.httpGet(path, headers, paramValues, encoding, readTimeoutMs); + } catch (IOException e) { + end = System.currentTimeMillis(); + MetricsMonitor.getConfigRequestMonitor("GET", path, "NA").record(end - start, TimeUnit.MILLISECONDS); + throw e; + } + + end = System.currentTimeMillis(); + MetricsMonitor.getConfigRequestMonitor("GET", path, String.valueOf(result.code)).record(end - start, TimeUnit.MILLISECONDS); + + return result; + } + + @Override + public HttpResult httpPost(String path, List headers, List paramValues, String encoding, long readTimeoutMs) throws IOException { + long start = System.currentTimeMillis(); + long end = 0; + HttpResult result = null; + try { + result = httpAgent.httpPost(path, headers, paramValues, encoding, readTimeoutMs); + } catch (IOException e) { + end = System.currentTimeMillis(); + MetricsMonitor.getConfigRequestMonitor("POST", path, "NA").record(end - start, TimeUnit.MILLISECONDS); + throw e; + } + + end = System.currentTimeMillis(); + MetricsMonitor.getConfigRequestMonitor("POST", path, String.valueOf(result.code)).record(end - start, TimeUnit.MILLISECONDS); + + return result; + } + + @Override + public HttpResult httpDelete(String path, List headers, List paramValues, String encoding, long readTimeoutMs) throws IOException { + long start = System.currentTimeMillis(); + long end = 0; + HttpResult result = null; + try { + result = httpAgent.httpDelete(path, headers, paramValues, encoding, readTimeoutMs); + } catch (IOException e) { + end = System.currentTimeMillis(); + MetricsMonitor.getConfigRequestMonitor("DELETE", path, "NA").record(end - start, TimeUnit.MILLISECONDS); + throw e; + } + + end = System.currentTimeMillis(); + MetricsMonitor.getConfigRequestMonitor("DELETE", path, String.valueOf(result.code)).record(end - start, TimeUnit.MILLISECONDS); + + return result; + } + + @Override + public String getName() { + return httpAgent.getName(); + } + + @Override + public String getNamespace() { + return httpAgent.getNamespace(); + } + + @Override + public String getTenant() { + return httpAgent.getTenant(); + } + + @Override + public String getEncode() { + return httpAgent.getEncode(); + } +} diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/ServerHttpAgent.java b/client/src/main/java/com/alibaba/nacos/client/config/http/ServerHttpAgent.java similarity index 97% rename from client/src/main/java/com/alibaba/nacos/client/config/impl/ServerHttpAgent.java rename to client/src/main/java/com/alibaba/nacos/client/config/http/ServerHttpAgent.java index 26a26fd79..07cba956e 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/ServerHttpAgent.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/http/ServerHttpAgent.java @@ -13,17 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.alibaba.nacos.client.config.impl; +package com.alibaba.nacos.client.config.http; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.client.config.impl.HttpSimpleClient; import com.alibaba.nacos.client.config.impl.HttpSimpleClient.HttpResult; +import com.alibaba.nacos.client.config.impl.ServerListManager; +import com.alibaba.nacos.client.config.impl.SpasAdapter; import com.alibaba.nacos.client.config.utils.IOUtils; import com.alibaba.nacos.client.config.utils.LogUtils; import com.alibaba.nacos.client.identify.STSConfig; import com.alibaba.nacos.client.logger.Logger; import com.alibaba.nacos.client.logger.support.LoggerHelper; +import com.alibaba.nacos.client.monitor.MetricsMonitor; import com.alibaba.nacos.client.utils.JSONUtils; import com.alibaba.nacos.client.utils.ParamUtil; import com.alibaba.nacos.client.utils.StringUtils; @@ -39,13 +43,14 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Properties; +import java.util.concurrent.TimeUnit; /** * Server Agent * * @author water.lyl */ -public class ServerHttpAgent { +public class ServerHttpAgent implements HttpAgent { final static public Logger log = LogUtils.logger(ServerHttpAgent.class); @@ -58,6 +63,7 @@ public class ServerHttpAgent { * @return * @throws IOException */ + @Override public HttpResult httpGet(String path, List headers, List paramValues, String encoding, long readTimeoutMs) throws IOException { final long endTime = System.currentTimeMillis() + readTimeoutMs; @@ -101,6 +107,7 @@ public class ServerHttpAgent { throw new ConnectException("no available server"); } + @Override public HttpResult httpPost(String path, List headers, List paramValues, String encoding, long readTimeoutMs) throws IOException { final long endTime = System.currentTimeMillis() + readTimeoutMs; @@ -143,6 +150,7 @@ public class ServerHttpAgent { throw new ConnectException("no available server"); } + @Override public HttpResult httpDelete(String path, List headers, List paramValues, String encoding, long readTimeoutMs) throws IOException { final long endTime = System.currentTimeMillis() + readTimeoutMs; @@ -241,6 +249,7 @@ public class ServerHttpAgent { } } + @Override public synchronized void start() throws NacosException { serverListMgr.start(); } @@ -324,18 +333,22 @@ public class ServerHttpAgent { "can not get security credentials, responseCode: " + respCode + ", response: " + response); } + @Override public String getName() { return serverListMgr.getName(); } + @Override public String getNamespace() { return serverListMgr.getNamespace(); } + @Override public String getTenant() { return serverListMgr.getTenant(); } + @Override public String getEncode() { return encode; } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java index ba86785d4..a4ac5b4a5 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java @@ -20,6 +20,7 @@ import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.config.common.GroupKey; import com.alibaba.nacos.client.config.filter.impl.ConfigFilterChainManager; +import com.alibaba.nacos.client.config.http.HttpAgent; import com.alibaba.nacos.client.config.impl.HttpSimpleClient.HttpResult; import com.alibaba.nacos.client.config.utils.ContentUtils; import com.alibaba.nacos.client.config.utils.LogUtils; @@ -27,6 +28,7 @@ import com.alibaba.nacos.client.config.utils.MD5; import com.alibaba.nacos.client.config.utils.TenantUtil; import com.alibaba.nacos.client.logger.Logger; import com.alibaba.nacos.client.logger.support.LoggerHelper; +import com.alibaba.nacos.client.monitor.MetricsMonitor; import com.alibaba.nacos.client.utils.ParamUtil; import com.alibaba.nacos.client.utils.StringUtils; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @@ -43,7 +45,7 @@ import static com.alibaba.nacos.api.common.Constants.LINE_SEPARATOR; import static com.alibaba.nacos.api.common.Constants.WORD_SEPARATOR; /** - * Longpulling + * Longpolling * * @author Nacos */ @@ -100,6 +102,8 @@ public class ClientWorker { cacheMap.set(copy); } log.info(agent.getName(), "[unsubscribe] {}", groupKey); + + MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.get().size()); } @SuppressFBWarnings("JLM_JSR166_UTILCONCURRENT_MONITORENTER") @@ -111,6 +115,8 @@ public class ClientWorker { cacheMap.set(copy); } log.info(agent.getName(), "[unsubscribe] {}", groupKey); + + MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.get().size()); } @SuppressFBWarnings("JLM_JSR166_UTILCONCURRENT_MONITORENTER") @@ -143,6 +149,8 @@ public class ClientWorker { log.info(agent.getName(), "[subscribe] {}", key); + MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.get().size()); + return cache; } @@ -170,6 +178,9 @@ public class ClientWorker { cacheMap.set(copy); } log.info(agent.getName(), "[subscribe] {}", key); + + MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.get().size()); + return cache; } @@ -289,7 +300,7 @@ public class ClientWorker { if (longingTaskCount > currentLongingTaskCount) { for (int i = (int)currentLongingTaskCount; i < longingTaskCount; i++) { // 要判断任务是否在执行 这块需要好好想想。 任务列表现在是无序的。变化过程可能有问题 - executorService.execute(new LongPullingRunnable(i)); + executorService.execute(new LongPollingRunnable(i)); } currentLongingTaskCount = longingTaskCount; } @@ -406,9 +417,10 @@ public class ClientWorker { } @SuppressWarnings("PMD.ThreadPoolCreationRule") - public ClientWorker(final ServerHttpAgent agent, final ConfigFilterChainManager configFilterChainManager) { + public ClientWorker(final HttpAgent agent, final ConfigFilterChainManager configFilterChainManager) { this.agent = agent; this.configFilterChainManager = configFilterChainManager; + executor = Executors.newScheduledThreadPool(1, new ThreadFactory() { @Override public Thread newThread(Runnable r) { @@ -423,7 +435,7 @@ public class ClientWorker { @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); - t.setName("com.alibaba.nacos.client.Worker.longPulling" + agent.getName()); + t.setName("com.alibaba.nacos.client.Worker.longPolling" + agent.getName()); t.setDaemon(true); return t; } @@ -440,10 +452,10 @@ public class ClientWorker { }, 1L, 10L, TimeUnit.MILLISECONDS); } - class LongPullingRunnable implements Runnable { + class LongPollingRunnable implements Runnable { private int taskId; - public LongPullingRunnable(int taskId) { + public LongPollingRunnable(int taskId) { this.taskId = taskId; } @@ -498,7 +510,7 @@ public class ClientWorker { } inInitializingCacheList.clear(); } catch (Throwable e) { - log.error("500", "longPulling error", e); + log.error("500", "longPolling error", e); } finally { executorService.execute(this); } @@ -522,9 +534,9 @@ public class ClientWorker { */ AtomicReference> cacheMap = new AtomicReference>( new HashMap()); - ServerHttpAgent agent; + + HttpAgent agent; ConfigFilterChainManager configFilterChainManager; private boolean isHealthServer = true; private double currentLongingTaskCount = 0; - } diff --git a/client/src/main/java/com/alibaba/nacos/client/monitor/MetricsMonitor.java b/client/src/main/java/com/alibaba/nacos/client/monitor/MetricsMonitor.java new file mode 100644 index 000000000..2392965bf --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/monitor/MetricsMonitor.java @@ -0,0 +1,81 @@ +/* + * 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.client.monitor; + +import io.micrometer.core.instrument.ImmutableTag; +import io.micrometer.core.instrument.Metrics; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Timer; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Metrics Monitor + * + * @author Nacos + */ +public class MetricsMonitor { + private static AtomicInteger serviceInfoMapSize = new AtomicInteger(); + private static AtomicInteger dom2BeatSize = new AtomicInteger(); + private static AtomicInteger listenConfigCount = new AtomicInteger(); + + static { + List tags = new ArrayList(); + tags.add(new ImmutableTag("module", "naming")); + tags.add(new ImmutableTag("name", "subServiceCount")); + Metrics.gauge("nacos_monitor", tags, serviceInfoMapSize); + + tags = new ArrayList(); + tags.add(new ImmutableTag("module", "naming")); + tags.add(new ImmutableTag("name", "pubServiceCount")); + Metrics.gauge("nacos_monitor", tags, dom2BeatSize); + + tags = new ArrayList(); + tags.add(new ImmutableTag("module", "config")); + tags.add(new ImmutableTag("name", "listenConfigCount")); + Metrics.gauge("nacos_monitor", tags, listenConfigCount); + } + + public static AtomicInteger getServiceInfoMapSizeMonitor() { + return serviceInfoMapSize; + } + + public static AtomicInteger getDom2BeatSizeMonitor() { + return dom2BeatSize; + } + + public static AtomicInteger getListenConfigCountMonitor() { + return listenConfigCount; + } + + public static Timer getConfigRequestMonitor(String method, String url, String code) { + return Metrics.timer("nacos_client_request", + "module", "config", + "method", method, + "url", url, + "code", code); + } + + public static Timer getNamingRequestMonitor(String method, String url, String code) { + return Metrics.timer("nacos_client_request", + "module", "naming", + "method", method, + "url", url, + "code", code); + } +} diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java b/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java index 28880b969..74e73007d 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java @@ -195,9 +195,24 @@ public class NacosNamingService implements NamingService { } @Override - public List getAllInstances(String serviceName, List clusters) throws NacosException { + public List getAllInstances(String serviceName, boolean subscribe) throws NacosException { + return getAllInstances(serviceName, new ArrayList(), subscribe); + } - ServiceInfo serviceInfo = hostReactor.getServiceInfo(serviceName, StringUtils.join(clusters, ",")); + @Override + public List getAllInstances(String serviceName, List clusters) throws NacosException { + return getAllInstances(serviceName, clusters, true); + } + + @Override + public List getAllInstances(String serviceName, List clusters, boolean subscribe) throws NacosException { + + ServiceInfo serviceInfo; + if (subscribe) { + serviceInfo = hostReactor.getServiceInfo(serviceName, StringUtils.join(clusters, ",")); + } else { + serviceInfo = hostReactor.getServiceInfoDirectlyFromServer(serviceName, StringUtils.join(clusters, ",")); + } List list; if (serviceInfo == null || CollectionUtils.isEmpty(list = serviceInfo.getHosts())) { return new ArrayList(); @@ -206,40 +221,58 @@ public class NacosNamingService implements NamingService { } @Override - public List selectInstances(String serviceName, boolean healthyOnly) throws NacosException { - return selectInstances(serviceName, new ArrayList(), healthyOnly); + public List selectInstances(String serviceName, boolean healthy) throws NacosException { + return selectInstances(serviceName, new ArrayList(), healthy); + } + + @Override + public List selectInstances(String serviceName, boolean healthy, boolean subscribe) throws NacosException { + return selectInstances(serviceName, new ArrayList(), healthy, subscribe); } @Override public List selectInstances(String serviceName, List clusters, boolean healthy) throws NacosException { - - ServiceInfo serviceInfo = hostReactor.getServiceInfo(serviceName, StringUtils.join(clusters, ",")); - List list; - if (serviceInfo == null || CollectionUtils.isEmpty(list = serviceInfo.getHosts())) { - return new ArrayList(); - } - - Iterator iterator = list.iterator(); - while (iterator.hasNext()) { - Instance instance = iterator.next(); - if (healthy != instance.isHealthy() || !instance.isEnabled() || instance.getWeight() <= 0) { - iterator.remove(); - } - } - - return list; + return selectInstances(serviceName, clusters, healthy, true); } @Override - public Instance selectOneHealthyInstance(String serviceName) { + public List selectInstances(String serviceName, List clusters, boolean healthy, + boolean subscribe) throws NacosException { + ServiceInfo serviceInfo; + if (subscribe) { + serviceInfo = hostReactor.getServiceInfo(serviceName, StringUtils.join(clusters, ",")); + } else { + serviceInfo = hostReactor.getServiceInfoDirectlyFromServer(serviceName, StringUtils.join(clusters, ",")); + } + return selectInstances(serviceInfo, healthy); + } + + @Override + public Instance selectOneHealthyInstance(String serviceName) throws NacosException { return selectOneHealthyInstance(serviceName, new ArrayList()); } @Override - public Instance selectOneHealthyInstance(String serviceName, List clusters) { - return Balancer.RandomByWeight.selectHost( - hostReactor.getServiceInfo(serviceName, StringUtils.join(clusters, ","))); + public Instance selectOneHealthyInstance(String serviceName, boolean subscribe) throws NacosException { + return selectOneHealthyInstance(serviceName, new ArrayList(), subscribe); + } + + @Override + public Instance selectOneHealthyInstance(String serviceName, List clusters) throws NacosException { + return selectOneHealthyInstance(serviceName, clusters, true); + } + + @Override + public Instance selectOneHealthyInstance(String serviceName, List clusters, boolean subscribe) throws NacosException { + + if (subscribe) { + return Balancer.RandomByWeight.selectHost( + hostReactor.getServiceInfo(serviceName, StringUtils.join(clusters, ","))); + } else { + return Balancer.RandomByWeight.selectHost( + hostReactor.getServiceInfoDirectlyFromServer(serviceName, StringUtils.join(clusters, ","))); + } } @Override @@ -284,6 +317,23 @@ public class NacosNamingService implements NamingService { return serverProxy.serverHealthy() ? "UP" : "DOWN"; } + private List selectInstances(ServiceInfo serviceInfo, boolean healthy) { + List list; + if (serviceInfo == null || CollectionUtils.isEmpty(list = serviceInfo.getHosts())) { + return new ArrayList(); + } + + Iterator iterator = list.iterator(); + while (iterator.hasNext()) { + Instance instance = iterator.next(); + if (healthy != instance.isHealthy() || !instance.isEnabled() || instance.getWeight() <= 0) { + iterator.remove(); + } + } + + return list; + } + public BeatReactor getBeatReactor() { return beatReactor; } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/beat/BeatReactor.java b/client/src/main/java/com/alibaba/nacos/client/naming/beat/BeatReactor.java index 1aa387856..ba8888818 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/beat/BeatReactor.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/beat/BeatReactor.java @@ -16,6 +16,7 @@ package com.alibaba.nacos.client.naming.beat; import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.client.monitor.MetricsMonitor; import com.alibaba.nacos.client.naming.net.NamingProxy; import com.alibaba.nacos.client.naming.utils.LogUtils; import com.alibaba.nacos.client.naming.utils.UtilAndComs; @@ -59,11 +60,13 @@ public class BeatReactor { public void addBeatInfo(String dom, BeatInfo beatInfo) { LogUtils.LOG.info("BEAT", "adding beat: {} to beat map.", beatInfo); dom2Beat.put(buildKey(dom, beatInfo.getIp(), beatInfo.getPort()), beatInfo); + MetricsMonitor.getDom2BeatSizeMonitor().set(dom2Beat.size()); } public void removeBeatInfo(String dom, String ip, int port) { LogUtils.LOG.info("BEAT", "removing beat: {}:{}:{} from beat map.", dom, ip, port); dom2Beat.remove(buildKey(dom, ip, port)); + MetricsMonitor.getDom2BeatSizeMonitor().set(dom2Beat.size()); } public String buildKey(String dom, String ip, int port) { diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/core/HostReactor.java b/client/src/main/java/com/alibaba/nacos/client/naming/core/HostReactor.java index add39e439..dee2bf4c1 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/core/HostReactor.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/core/HostReactor.java @@ -16,8 +16,10 @@ package com.alibaba.nacos.client.naming.core; import com.alibaba.fastjson.JSON; +import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; +import com.alibaba.nacos.client.monitor.MetricsMonitor; import com.alibaba.nacos.client.naming.backups.FailoverReactor; import com.alibaba.nacos.client.naming.cache.DiskCache; import com.alibaba.nacos.client.naming.net.NamingProxy; @@ -44,7 +46,7 @@ public class HostReactor { private Map updatingMap; - private PushRecver pushRecver; + private PushReceiver pushReceiver; private EventDispatcher eventDispatcher; @@ -84,7 +86,7 @@ public class HostReactor { this.updatingMap = new ConcurrentHashMap(); this.failoverReactor = new FailoverReactor(this, cacheDir); - this.pushRecver = new PushRecver(this); + this.pushReceiver = new PushReceiver(this); } public Map getServiceInfoMap() { @@ -188,6 +190,8 @@ public class HostReactor { DiskCache.write(serviceInfo, cacheDir); } + MetricsMonitor.getServiceInfoMapSizeMonitor().set(serviceInfoMap.size()); + LogUtils.LOG.info("current ips:(" + serviceInfo.ipCount() + ") service: " + serviceInfo.getName() + " -> " + JSON.toJSONString(serviceInfo.getHosts())); @@ -201,6 +205,14 @@ public class HostReactor { return serviceInfoMap.get(key); } + public ServiceInfo getServiceInfoDirectlyFromServer(final String serviceName, final String clusters) throws NacosException { + String result = serverProxy.queryList(serviceName, clusters, 0, false); + if (StringUtils.isNotEmpty(result)) { + return JSON.parseObject(result, ServiceInfo.class); + } + return null; + } + public ServiceInfo getServiceInfo(final String serviceName, final String clusters) { LogUtils.LOG.debug("failover-mode: " + failoverReactor.isFailoverSwitch()); @@ -260,7 +272,7 @@ public class HostReactor { ServiceInfo oldService = getSerivceInfo0(serviceName, clusters); try { - String result = serverProxy.queryList(serviceName, clusters, pushRecver.getUDPPort(), false); + String result = serverProxy.queryList(serviceName, clusters, pushReceiver.getUDPPort(), false); if (StringUtils.isNotEmpty(result)) { processServiceJSON(result); } @@ -277,7 +289,7 @@ public class HostReactor { public void refreshOnly(String serviceName, String clusters) { try { - serverProxy.queryList(serviceName, clusters, pushRecver.getUDPPort(), false); + serverProxy.queryList(serviceName, clusters, pushReceiver.getUDPPort(), false); } catch (Exception e) { LogUtils.LOG.error("NA", "failed to update serviceName: " + serviceName, e); } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/core/PushRecver.java b/client/src/main/java/com/alibaba/nacos/client/naming/core/PushReceiver.java similarity index 97% rename from client/src/main/java/com/alibaba/nacos/client/naming/core/PushRecver.java rename to client/src/main/java/com/alibaba/nacos/client/naming/core/PushReceiver.java index d6012048f..bd9a6ebf5 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/core/PushRecver.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/core/PushReceiver.java @@ -30,7 +30,7 @@ import java.util.concurrent.ThreadFactory; /** * @author xuanyin */ -public class PushRecver implements Runnable { +public class PushReceiver implements Runnable { private ScheduledExecutorService executorService; @@ -40,7 +40,7 @@ public class PushRecver implements Runnable { private HostReactor hostReactor; - public PushRecver(HostReactor hostReactor) { + public PushReceiver(HostReactor hostReactor) { try { this.hostReactor = hostReactor; udpSocket = new DatagramSocket(); diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/net/NamingProxy.java b/client/src/main/java/com/alibaba/nacos/client/naming/net/NamingProxy.java index 03b644ce9..95ad44643 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/net/NamingProxy.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/net/NamingProxy.java @@ -25,6 +25,7 @@ import com.alibaba.nacos.api.naming.pojo.ListView; import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.api.selector.ExpressionSelector; import com.alibaba.nacos.api.selector.SelectorType; +import com.alibaba.nacos.client.monitor.MetricsMonitor; import com.alibaba.nacos.client.naming.beat.BeatInfo; import com.alibaba.nacos.client.naming.utils.*; import com.alibaba.nacos.common.util.HttpMethod; @@ -295,6 +296,8 @@ public class NamingProxy { public String callServer(String api, Map params, String curServer, String method) throws NacosException { + long start = System.currentTimeMillis(); + long end = 0; List headers = Arrays.asList("Client-Version", UtilAndComs.VERSION, "Accept-Encoding", "gzip,deflate,sdch", @@ -310,6 +313,10 @@ public class NamingProxy { url = HttpClient.getPrefix() + curServer + api; HttpClient.HttpResult result = HttpClient.request(url, headers, params, UtilAndComs.ENCODING, method); + end = System.currentTimeMillis(); + + MetricsMonitor.getNamingRequestMonitor(method, url, String.valueOf(result.code)) + .record(end - start, TimeUnit.MILLISECONDS); if (HttpURLConnection.HTTP_OK == result.code) { return result.content; diff --git a/config/pom.xml b/config/pom.xml index fedf02b5b..ee4f6557c 100644 --- a/config/pom.xml +++ b/config/pom.xml @@ -126,6 +126,26 @@ commons-lang3 3.4 + + io.micrometer + micrometer-registry-prometheus + + + io.micrometer + micrometer-registry-influx + + + io.micrometer + micrometer-registry-elastic + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.boot + spring-boot-starter-actuator + diff --git a/config/src/main/java/com/alibaba/nacos/config/server/Config.java b/config/src/main/java/com/alibaba/nacos/config/server/Config.java index 4a5e7dcd4..61cc2884a 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/Config.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/Config.java @@ -17,6 +17,7 @@ package com.alibaba.nacos.config.server; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; import java.net.UnknownHostException; @@ -25,6 +26,7 @@ import java.net.UnknownHostException; * * @author Nacos */ +@EnableScheduling @SpringBootApplication public class Config { diff --git a/config/src/main/java/com/alibaba/nacos/config/server/aspect/RequestLogAspect.java b/config/src/main/java/com/alibaba/nacos/config/server/aspect/RequestLogAspect.java index 4e7e60f83..5d45323be 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/aspect/RequestLogAspect.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/aspect/RequestLogAspect.java @@ -16,6 +16,7 @@ package com.alibaba.nacos.config.server.aspect; import com.alibaba.nacos.config.server.service.ConfigService; +import com.alibaba.nacos.config.server.monitor.MetricsMonitor; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.config.server.utils.MD5; @@ -23,6 +24,7 @@ import com.alibaba.nacos.config.server.utils.RequestUtil; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -33,8 +35,8 @@ import javax.servlet.http.HttpServletResponse; * @author Nacos */ @Aspect +@Component public class RequestLogAspect { - /** * publish config */ @@ -54,22 +56,24 @@ public class RequestLogAspect { */ private static final String CLIENT_INTERFACE_REMOVE_ALL_CONFIG = "execution(* com.alibaba.nacos.config.server.controller.ConfigController.deleteConfig(..)) && args(request," - + "response,dataId,group,..)"; + + "response,dataId,group,tenant,..)"; + /** * publishSingle - */ + * */ @Around(CLIENT_INTERFACE_PUBLISH_SINGLE_CONFIG) public Object interfacePublishSingle(ProceedingJoinPoint pjp, HttpServletRequest request, HttpServletResponse response, String dataId, String group, String tenant, String content) throws Throwable { final String md5 = content == null ? null : MD5.getInstance().getMD5String(content); + MetricsMonitor.getPublishMonitor().incrementAndGet(); return logClientRequest("publish", pjp, request, response, dataId, group, tenant, md5); } /** * removeAll - */ + * */ @Around(CLIENT_INTERFACE_REMOVE_ALL_CONFIG) public Object interfaceRemoveAll(ProceedingJoinPoint pjp, HttpServletRequest request, HttpServletResponse response, String dataId, String group, String tenant) throws Throwable { @@ -84,6 +88,7 @@ public class RequestLogAspect { String dataId, String group, String tenant) throws Throwable { final String groupKey = GroupKey2.getKey(dataId, group, tenant); final String md5 = ConfigService.getContentMd5(groupKey); + MetricsMonitor.getConfigMonitor().incrementAndGet(); return logClientRequest("get", pjp, request, response, dataId, group, tenant, md5); } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigServletInner.java b/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigServletInner.java index 7206febad..4db112173 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigServletInner.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigServletInner.java @@ -60,7 +60,7 @@ public class ConfigServletInner { private static final int TRY_GET_LOCK_TIMES = 9; - private static final int START_LONGPULLING_VERSION_NUM = 204; + private static final int START_LONGPOLLING_VERSION_NUM = 204; /** * 轮询接口 @@ -70,8 +70,8 @@ public class ConfigServletInner { throws IOException, ServletException { // 长轮询 - if (LongPollingService.isSupportLongPulling(request)) { - longPollingService.addLongPullingClient(request, response, clientMd5Map, probeRequestSize); + if (LongPollingService.isSupportLongPolling(request)) { + longPollingService.addLongPollingClient(request, response, clientMd5Map, probeRequestSize); return HttpServletResponse.SC_OK + ""; } @@ -91,7 +91,7 @@ public class ConfigServletInner { /** * 2.0.4版本以前, 返回值放入header中 */ - if (versionNum < START_LONGPULLING_VERSION_NUM) { + if (versionNum < START_LONGPOLLING_VERSION_NUM) { response.addHeader(Constants.PROBE_MODIFY_RESPONSE, oldResult); response.addHeader(Constants.PROBE_MODIFY_RESPONSE_NEW, newResult); } else { diff --git a/config/src/main/java/com/alibaba/nacos/config/server/controller/ListenerController.java b/config/src/main/java/com/alibaba/nacos/config/server/controller/ListenerController.java index 537a84153..c47d2fb4c 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/controller/ListenerController.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/controller/ListenerController.java @@ -35,7 +35,7 @@ import java.util.HashMap; import java.util.Map; /** - * Config longpulling + * Config longpolling * * @author Nacos */ diff --git a/config/src/main/java/com/alibaba/nacos/config/server/exception/GlobalExceptionHandler.java b/config/src/main/java/com/alibaba/nacos/config/server/exception/GlobalExceptionHandler.java index 8fbd2b5a7..5982213a1 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/exception/GlobalExceptionHandler.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/exception/GlobalExceptionHandler.java @@ -15,6 +15,9 @@ */ package com.alibaba.nacos.config.server.exception; +import com.alibaba.nacos.config.server.monitor.MetricsMonitor; +import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.CannotGetJdbcConnectionException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -37,6 +40,7 @@ public class GlobalExceptionHandler { */ @ExceptionHandler(IllegalArgumentException.class) public void handleIllegalArgumentException(HttpServletResponse response, Exception ex) throws IOException { + MetricsMonitor.getIllegalArgumentException().increment(); response.setStatus(400); if (ex.getMessage() != null) { response.getWriter().println(ex.getMessage()); @@ -52,6 +56,7 @@ public class GlobalExceptionHandler { */ @ExceptionHandler(NacosException.class) public void handleNacosException(HttpServletResponse response, NacosException ex) throws IOException { + MetricsMonitor.getNacosException().increment(); response.setStatus(ex.getErrCode()); if (ex.getErrMsg() != null) { response.getWriter().println(ex.getErrMsg()); @@ -60,4 +65,15 @@ public class GlobalExceptionHandler { } } + /** + * For DataAccessException + * + * @throws DataAccessException + */ + @ExceptionHandler(DataAccessException.class) + public void handleDataAccessException(HttpServletResponse response, DataAccessException ex) throws DataAccessException { + MetricsMonitor.getDbException().increment(); + throw new CannotGetJdbcConnectionException(ex.getMessage()); + } + } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/manager/TaskManager.java b/config/src/main/java/com/alibaba/nacos/config/server/manager/TaskManager.java index e7b08e784..d1311c02b 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/manager/TaskManager.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/manager/TaskManager.java @@ -16,6 +16,7 @@ package com.alibaba.nacos.config.server.manager; import com.alibaba.nacos.config.server.constant.Constants; +import com.alibaba.nacos.config.server.monitor.MetricsMonitor; import com.alibaba.nacos.config.server.utils.LogUtil; import org.slf4j.Logger; @@ -51,6 +52,7 @@ public final class TaskManager implements TaskManagerMBean { private String name; + class ProcessRunnable implements Runnable { public void run() { @@ -140,6 +142,7 @@ public final class TaskManager implements TaskManagerMBean { this.lock.lock(); try { this.tasks.remove(type); + MetricsMonitor.getDumpTaskMonitor().set(tasks.size()); } finally { this.lock.unlock(); } @@ -150,12 +153,12 @@ public final class TaskManager implements TaskManagerMBean { * * @param type * @param task - * @param previousTask */ public void addTask(String type, AbstractTask task) { this.lock.lock(); try { AbstractTask oldTask = tasks.put(type, task); + MetricsMonitor.getDumpTaskMonitor().set(tasks.size()); if (null != oldTask) { task.merge(oldTask); } @@ -181,6 +184,7 @@ public final class TaskManager implements TaskManagerMBean { } // 先将任务从任务Map中删除 this.tasks.remove(entry.getKey()); + MetricsMonitor.getDumpTaskMonitor().set(tasks.size()); } } finally { this.lock.unlock(); diff --git a/config/src/main/java/com/alibaba/nacos/config/server/monitor/MemoryMonitor.java b/config/src/main/java/com/alibaba/nacos/config/server/monitor/MemoryMonitor.java index a79bf7cf8..f96f6a706 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/monitor/MemoryMonitor.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/monitor/MemoryMonitor.java @@ -20,10 +20,12 @@ import com.alibaba.nacos.config.server.service.ConfigService; import com.alibaba.nacos.config.server.service.TimerTaskService; import com.alibaba.nacos.config.server.service.notify.AsyncNotifyService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import static com.alibaba.nacos.config.server.utils.LogUtil.memoryLog; @@ -34,6 +36,7 @@ import static com.alibaba.nacos.config.server.utils.LogUtil.memoryLog; */ @Service public class MemoryMonitor { + @Autowired public MemoryMonitor(AsyncNotifyService notifySingleService) { @@ -45,9 +48,16 @@ public class MemoryMonitor { TimerTaskService.scheduleWithFixedDelay(new NotifyTaskQueueMonitorTask(notifySingleService), DELAY_SECONDS, DELAY_SECONDS, TimeUnit.SECONDS); + } static final long DELAY_SECONDS = 10; + + @Scheduled(cron = "0 0 0 * * ?") + public void clear() { + MetricsMonitor.getConfigMonitor().set(0); + MetricsMonitor.getPublishMonitor().set(0); + } } class PrintGetConfigResponeTask implements Runnable { @@ -58,6 +68,7 @@ class PrintGetConfigResponeTask implements Runnable { } class PrintMemoryTask implements Runnable { + @Override public void run() { int groupCount = ConfigService.groupCount(); @@ -65,11 +76,14 @@ class PrintMemoryTask implements Runnable { long subCount = ClientTrackService.subscriberCount(); memoryLog.info("groupCount={}, subscriberClientCount={}, subscriberCount={}", groupCount, subClientCount, subCount); + MetricsMonitor.getConfigCountMonitor().set(groupCount); } } class NotifyTaskQueueMonitorTask implements Runnable { final private AsyncNotifyService notifySingleService; + private AtomicInteger notifyTask = new AtomicInteger(); + NotifyTaskQueueMonitorTask(AsyncNotifyService notifySingleService) { this.notifySingleService = notifySingleService; @@ -77,16 +91,10 @@ class NotifyTaskQueueMonitorTask implements Runnable { @Override public void run() { - + int size = ((ScheduledThreadPoolExecutor)notifySingleService.getExecutor()).getQueue().size(); memoryLog.info("notifySingleServiceThreadPool-{}, toNotifyTaskSize={}", new Object[] {((ScheduledThreadPoolExecutor)notifySingleService.getExecutor()).getClass().getName(), - ((ScheduledThreadPoolExecutor)notifySingleService.getExecutor()).getQueue().size()}); - - // for(Map.Entry entry: notifySingleService.getExecutors().entrySet()) { - // ThreadPoolExecutor pool = (ThreadPoolExecutor) entry.getValue(); - // String target = entry.getKey(); - // memoryLog.info("notifySingleServiceThreadPool-{}, toNotifyTaskSize={}", - // new Object[] { target, pool.getQueue().size() }); - // } + size}); + MetricsMonitor.getNotifyTaskMonitor().set(size); } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/monitor/MetricsMonitor.java b/config/src/main/java/com/alibaba/nacos/config/server/monitor/MetricsMonitor.java new file mode 100644 index 000000000..51f9023da --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/monitor/MetricsMonitor.java @@ -0,0 +1,124 @@ +/* + * 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.config.server.monitor; + +import io.micrometer.core.instrument.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Metrics Monitor + * + * @author Nacos + */ +public class MetricsMonitor { + private static AtomicInteger getConfig = new AtomicInteger(); + private static AtomicInteger publish = new AtomicInteger(); + private static AtomicInteger longPolling = new AtomicInteger(); + private static AtomicInteger configCount = new AtomicInteger(); + private static AtomicInteger notifyTask = new AtomicInteger(); + private static AtomicInteger dumpTask = new AtomicInteger(); + + static { + List tags = new ArrayList(); + tags.add(new ImmutableTag("module", "config")); + tags.add(new ImmutableTag("name", "getConfig")); + Metrics.gauge("nacos_monitor", tags, getConfig); + + tags = new ArrayList(); + tags.add(new ImmutableTag("module", "config")); + tags.add(new ImmutableTag("name", "publish")); + Metrics.gauge("nacos_monitor", tags, publish); + + tags = new ArrayList(); + tags.add(new ImmutableTag("module", "config")); + tags.add(new ImmutableTag("name", "longPolling")); + Metrics.gauge("nacos_monitor", tags, longPolling); + + tags = new ArrayList(); + tags.add(new ImmutableTag("module", "config")); + tags.add(new ImmutableTag("name", "configCount")); + Metrics.gauge("nacos_monitor", tags, configCount); + + tags = new ArrayList(); + tags.add(new ImmutableTag("module", "config")); + tags.add(new ImmutableTag("name", "notifyTask")); + Metrics.gauge("nacos_monitor", tags, notifyTask); + + tags = new ArrayList(); + tags.add(new ImmutableTag("module", "config")); + tags.add(new ImmutableTag("name", "dumpTask")); + + Metrics.gauge("nacos_monitor", tags, dumpTask); + } + + public static AtomicInteger getConfigMonitor() { + return getConfig; + } + + public static AtomicInteger getPublishMonitor() { + return publish; + } + + public static AtomicInteger getLongPollingMonitor() { + return longPolling; + } + + public static AtomicInteger getConfigCountMonitor() { + return configCount; + } + + public static AtomicInteger getNotifyTaskMonitor() { + return notifyTask; + } + + public static AtomicInteger getDumpTaskMonitor() { + return dumpTask; + } + + public static Timer getNotifyRtTimer() { + return Metrics.timer("nacos_timer", + "module", "config", "name", "notifyRt"); + } + + public static Counter getIllegalArgumentException() { + return Metrics.counter("nacos_exception", + "module", "config", "name", "illegalArgument"); + } + + public static Counter getNacosException() { + return Metrics.counter("nacos_exception", + "module", "config", "name", "nacos"); + } + + public static Counter getDbException() { + return Metrics.counter("nacos_exception", + "module", "config", "name", "db"); + } + + public static Counter getConfigNotifyException() { + return Metrics.counter("nacos_exception", + "module", "config", "name", "configNotify"); + } + + public static Counter getUnhealthException() { + return Metrics.counter("nacos_exception", + "module", "config", "name", "unhealth"); + } + +} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/BasicDataSourceServiceImpl.java b/config/src/main/java/com/alibaba/nacos/config/server/service/BasicDataSourceServiceImpl.java index 96dfbce43..29f806ce5 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/BasicDataSourceServiceImpl.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/BasicDataSourceServiceImpl.java @@ -15,6 +15,7 @@ */ package com.alibaba.nacos.config.server.service; +import com.alibaba.nacos.config.server.monitor.MetricsMonitor; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -309,6 +310,7 @@ public class BasicDataSourceServiceImpl implements DataSourceService { if (!isFound) { fatalLog.error("[master-db] master db not found."); + MetricsMonitor.getDbException().increment(); } } } @@ -332,6 +334,8 @@ public class BasicDataSourceServiceImpl implements DataSourceService { fatalLog.error("[db-error] slave db {} down.", getIpFromUrl(dataSourceList.get(i).getUrl())); } isHealthList.set(i, Boolean.FALSE); + + MetricsMonitor.getDbException().increment(); } } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/LongPollingService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/LongPollingService.java index ff7b9e9a6..42f277357 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/LongPollingService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/LongPollingService.java @@ -16,6 +16,7 @@ package com.alibaba.nacos.config.server.service; import com.alibaba.nacos.config.server.model.SampleResult; +import com.alibaba.nacos.config.server.monitor.MetricsMonitor; import com.alibaba.nacos.config.server.utils.GroupKey; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.config.server.utils.MD5Util; @@ -65,7 +66,7 @@ public class LongPollingService extends AbstractEventListener { } public Map getClientSubConfigInfo(String clientIp) { - ClientLongPulling record = getClientPollingRecord(clientIp); + ClientLongPolling record = getClientPollingRecord(clientIp); if (record == null) { return Collections.emptyMap(); @@ -79,9 +80,9 @@ public class LongPollingService extends AbstractEventListener { SampleResult sampleResult = new SampleResult(); Map lisentersGroupkeyStatus = new HashMap(50); - for (ClientLongPulling clientLongPulling : allSubs) { - if (clientLongPulling.clientMd5Map.containsKey(groupKey)) { - lisentersGroupkeyStatus.put(clientLongPulling.ip, clientLongPulling.clientMd5Map.get(groupKey)); + for (ClientLongPolling clientLongPolling : allSubs) { + if (clientLongPolling.clientMd5Map.containsKey(groupKey)) { + lisentersGroupkeyStatus.put(clientLongPolling.ip, clientLongPolling.clientMd5Map.get(groupKey)); } } sampleResult.setLisentersGroupkeyStatus(lisentersGroupkeyStatus); @@ -92,11 +93,11 @@ public class LongPollingService extends AbstractEventListener { SampleResult sampleResult = new SampleResult(); Map lisentersGroupkeyStatus = new HashMap(50); - for (ClientLongPulling clientLongPulling : allSubs) { - if (clientLongPulling.ip.equals(clientIp)) { + for (ClientLongPolling clientLongPolling : allSubs) { + if (clientLongPolling.ip.equals(clientIp)) { // 一个ip可能有多个监听 - if (!lisentersGroupkeyStatus.equals(clientLongPulling.clientMd5Map)) { - lisentersGroupkeyStatus.putAll(clientLongPulling.clientMd5Map); + if (!lisentersGroupkeyStatus.equals(clientLongPolling.clientMd5Map)) { + lisentersGroupkeyStatus.putAll(clientLongPolling.clientMd5Map); } } } @@ -128,18 +129,18 @@ public class LongPollingService extends AbstractEventListener { return null; } HashMap> app2Groupkeys = new HashMap>(50); - for (ClientLongPulling clientLongPulling : allSubs) { - if (StringUtils.isEmpty(clientLongPulling.appName) || "unknown".equalsIgnoreCase( - clientLongPulling.appName)) { + for (ClientLongPolling clientLongPolling : allSubs) { + if (StringUtils.isEmpty(clientLongPolling.appName) || "unknown".equalsIgnoreCase( + clientLongPolling.appName)) { continue; } - Set appSubscribeConfigs = app2Groupkeys.get(clientLongPulling.appName); - Set clientSubscribeConfigs = clientLongPulling.clientMd5Map.keySet(); + Set appSubscribeConfigs = app2Groupkeys.get(clientLongPolling.appName); + Set clientSubscribeConfigs = clientLongPolling.clientMd5Map.keySet(); if (appSubscribeConfigs == null) { appSubscribeConfigs = new HashSet(clientSubscribeConfigs.size()); } appSubscribeConfigs.addAll(clientSubscribeConfigs); - app2Groupkeys.put(clientLongPulling.appName, appSubscribeConfigs); + app2Groupkeys.put(clientLongPolling.appName, appSubscribeConfigs); } return app2Groupkeys; @@ -187,27 +188,27 @@ public class LongPollingService extends AbstractEventListener { return sampleResult; } - private ClientLongPulling getClientPollingRecord(String clientIp) { + private ClientLongPolling getClientPollingRecord(String clientIp) { if (allSubs == null) { return null; } - for (ClientLongPulling clientLongPulling : allSubs) { - HttpServletRequest request = (HttpServletRequest)clientLongPulling.asyncContext.getRequest(); + for (ClientLongPolling clientLongPolling : allSubs) { + HttpServletRequest request = (HttpServletRequest) clientLongPolling.asyncContext.getRequest(); if (clientIp.equals(RequestUtil.getRemoteIp(request))) { - return clientLongPulling; + return clientLongPolling; } } return null; } - public void addLongPullingClient(HttpServletRequest req, HttpServletResponse rsp, Map clientMd5Map, + public void addLongPollingClient(HttpServletRequest req, HttpServletResponse rsp, Map clientMd5Map, int probeRequestSize) { - String str = req.getHeader(LongPollingService.LONG_PULLING_HEADER); - String noHangUpFlag = req.getHeader(LongPollingService.LONG_PULLING_NO_HANG_UP_HEADER); + String str = req.getHeader(LongPollingService.LONG_POLLING_HEADER); + String noHangUpFlag = req.getHeader(LongPollingService.LONG_POLLING_NO_HANG_UP_HEADER); String appName = req.getHeader(RequestUtil.CLIENT_APPNAME_HEADER); String tag = req.getHeader("Vipserver-Tag"); int delayTime = SwitchService.getSwitchInteger(SwitchService.FIXED_DELAY_TIME, 500); @@ -241,7 +242,7 @@ public class LongPollingService extends AbstractEventListener { asyncContext.setTimeout(0L); scheduler.execute( - new ClientLongPulling(asyncContext, clientMd5Map, ip, probeRequestSize, timeout, appName, tag)); + new ClientLongPolling(asyncContext, clientMd5Map, ip, probeRequestSize, timeout, appName, tag)); } @Override @@ -263,20 +264,20 @@ public class LongPollingService extends AbstractEventListener { } } - static public boolean isSupportLongPulling(HttpServletRequest req) { - return null != req.getHeader(LONG_PULLING_HEADER); + static public boolean isSupportLongPolling(HttpServletRequest req) { + return null != req.getHeader(LONG_POLLING_HEADER); } @SuppressWarnings("PMD.ThreadPoolCreationRule") public LongPollingService() { - allSubs = new ConcurrentLinkedQueue(); + allSubs = new ConcurrentLinkedQueue(); scheduler = Executors.newScheduledThreadPool(1, new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setDaemon(true); - t.setName("com.alibaba.nacos.LongPulling"); + t.setName("com.alibaba.nacos.LongPolling"); return t; } }); @@ -285,15 +286,15 @@ public class LongPollingService extends AbstractEventListener { // ================= - static public final String LONG_PULLING_HEADER = "Long-Pulling-Timeout"; - static public final String LONG_PULLING_NO_HANG_UP_HEADER = "Long-Pulling-Timeout-No-Hangup"; + static public final String LONG_POLLING_HEADER = "Long-Pulling-Timeout"; + static public final String LONG_POLLING_NO_HANG_UP_HEADER = "Long-Pulling-Timeout-No-Hangup"; final ScheduledExecutorService scheduler; /** * 长轮询订阅关系 */ - final Queue allSubs; + final Queue allSubs; // ================= @@ -302,8 +303,8 @@ public class LongPollingService extends AbstractEventListener { public void run() { try { ConfigService.getContentBetaMd5(groupKey); - for (Iterator iter = allSubs.iterator(); iter.hasNext(); ) { - ClientLongPulling clientSub = iter.next(); + for (Iterator iter = allSubs.iterator(); iter.hasNext(); ) { + ClientLongPolling clientSub = iter.next(); if (clientSub.clientMd5Map.containsKey(groupKey)) { // 如果beta发布且不在beta列表直接跳过 if (isBeta && !betaIps.contains(clientSub.ip)) { @@ -359,23 +360,24 @@ public class LongPollingService extends AbstractEventListener { @Override public void run() { memoryLog.info("[long-pulling] client count " + allSubs.size()); + MetricsMonitor.getLongPollingMonitor().set(allSubs.size()); } } // ================= - class ClientLongPulling implements Runnable { + class ClientLongPolling implements Runnable { @Override public void run() { asyncTimeoutFuture = scheduler.schedule(new Runnable() { public void run() { try { - getRetainIps().put(ClientLongPulling.this.ip, System.currentTimeMillis()); + getRetainIps().put(ClientLongPolling.this.ip, System.currentTimeMillis()); /** * 删除订阅关系 */ - allSubs.remove(ClientLongPulling.this); + allSubs.remove(ClientLongPolling.this); if (isFixedPolling()) { LogUtil.clientLog.info("{}|{}|{}|{}|{}|{}", @@ -400,7 +402,7 @@ public class LongPollingService extends AbstractEventListener { sendResponse(null); } } catch (Throwable t) { - LogUtil.defaultLog.error("long pulling error:" + t.getMessage(), t.getCause()); + LogUtil.defaultLog.error("long polling error:" + t.getMessage(), t.getCause()); } } @@ -446,7 +448,7 @@ public class LongPollingService extends AbstractEventListener { } } - ClientLongPulling(AsyncContext ac, Map clientMd5Map, String ip, int probeRequestSize, + ClientLongPolling(AsyncContext ac, Map clientMd5Map, String ip, int probeRequestSize, long timeoutTime, String appName, String tag) { this.asyncContext = ac; this.clientMd5Map = clientMd5Map; diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/ServerListService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/ServerListService.java index 1cd9f4bf9..c4fa11d4b 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/ServerListService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/ServerListService.java @@ -16,6 +16,7 @@ package com.alibaba.nacos.config.server.service; import com.alibaba.nacos.config.server.constant.Constants; +import com.alibaba.nacos.config.server.monitor.MetricsMonitor; import com.alibaba.nacos.config.server.service.notify.NotifyService; import com.alibaba.nacos.config.server.service.notify.NotifyService.HttpResult; import com.alibaba.nacos.config.server.utils.LogUtil; @@ -377,6 +378,7 @@ public class ServerListService implements ApplicationListenercom.alibaba.nacos nacos-cmdb - + + io.micrometer + micrometer-registry-prometheus + + + io.micrometer + micrometer-registry-elastic + + + io.micrometer + micrometer-registry-influx + + + org.springframework.boot + spring-boot-starter-actuator + diff --git a/naming/src/main/java/com/alibaba/nacos/naming/NamingApp.java b/naming/src/main/java/com/alibaba/nacos/naming/NamingApp.java index 2ce5d5f24..4d8304444 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/NamingApp.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/NamingApp.java @@ -17,12 +17,14 @@ package com.alibaba.nacos.naming; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; /** * Hello world! * * @author xxc */ +@EnableScheduling @SpringBootApplication public class NamingApp { diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/cp/simpleraft/RaftCore.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/cp/simpleraft/RaftCore.java index 1013ab076..b78a6fba9 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/cp/simpleraft/RaftCore.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/consistency/cp/simpleraft/RaftCore.java @@ -22,6 +22,7 @@ import com.alibaba.fastjson.TypeReference; import com.alibaba.nacos.naming.boot.RunningConfig; import com.alibaba.nacos.naming.consistency.DataListener; import com.alibaba.nacos.naming.misc.*; +import com.alibaba.nacos.naming.monitor.MetricsMonitor; import com.ning.http.client.AsyncCompletionHandler; import com.ning.http.client.Response; import org.apache.commons.collections.CollectionUtils; @@ -544,6 +545,7 @@ public class RaftCore { if (response.getStatusCode() != HttpURLConnection.HTTP_OK) { Loggers.RAFT.error("NACOS-RAFT beat failed: {}, peer: {}", response.getResponseBody(), server); + MetricsMonitor.getLeaderSendBeatFailedException().increment(); return 1; } @@ -555,10 +557,12 @@ public class RaftCore { @Override public void onThrowable(Throwable t) { Loggers.RAFT.error("NACOS-RAFT error while sending heart-beat to peer: {} {}", server, t); + MetricsMonitor.getLeaderSendBeatFailedException().increment(); } }); } catch (Exception e) { Loggers.RAFT.error("VIPSRV error while sending heart-beat to peer: {} {}", server, e); + MetricsMonitor.getLeaderSendBeatFailedException().increment(); } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/consistency/cp/simpleraft/RaftStore.java b/naming/src/main/java/com/alibaba/nacos/naming/consistency/cp/simpleraft/RaftStore.java index ce2b59936..6e38879ed 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/consistency/cp/simpleraft/RaftStore.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/consistency/cp/simpleraft/RaftStore.java @@ -18,6 +18,7 @@ package com.alibaba.nacos.naming.consistency.cp.simpleraft; import com.alibaba.fastjson.JSON; import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.misc.UtilsAndCommons; +import com.alibaba.nacos.naming.monitor.MetricsMonitor; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; @@ -154,6 +155,8 @@ public class RaftStore { } if (!cacheFile.exists() && !cacheFile.getParentFile().mkdirs() && !cacheFile.createNewFile()) { + MetricsMonitor.getDiskException().increment(); + throw new IllegalStateException("can not make cache file: " + cacheFile.getName()); } @@ -164,6 +167,9 @@ public class RaftStore { fc = new FileOutputStream(cacheFile, false).getChannel(); fc.write(data, data.position()); fc.force(true); + } catch (Exception e) { + MetricsMonitor.getDiskException().increment(); + throw e; } finally { if (fc != null) { fc.close(); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/HealthController.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/HealthController.java index 703990f21..a3d0ef510 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/HealthController.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/HealthController.java @@ -15,16 +15,89 @@ */ package com.alibaba.nacos.naming.controllers; +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.core.utils.WebUtils; +import com.alibaba.nacos.naming.boot.RunningConfig; +import com.alibaba.nacos.naming.core.DistroMapper; +import com.alibaba.nacos.naming.core.DomainsManager; +import com.alibaba.nacos.naming.core.IpAddress; +import com.alibaba.nacos.naming.core.VirtualClusterDomain; +import com.alibaba.nacos.naming.misc.HttpClient; +import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.misc.UtilsAndCommons; -import com.alibaba.nacos.naming.web.ApiCommands; +import com.alibaba.nacos.naming.push.PushService; +import com.google.common.collect.Lists; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; +import javax.servlet.http.HttpServletRequest; +import java.net.HttpURLConnection; +import java.util.HashMap; +import java.util.Map; + /** * @author nkorange + * @author nanamikon * @since 0.8.0 */ @RestController("namingHealthController") -@RequestMapping(UtilsAndCommons.NACOS_NAMING_CONTEXT + UtilsAndCommons.NACOS_NAMING_HEALTH_CONTEXT) -public class HealthController extends ApiCommands { +@RequestMapping(UtilsAndCommons.NACOS_NAMING_CONTEXT + "/health") +public class HealthController { + @Autowired + private DomainsManager domainsManager; + + @RequestMapping(method = {RequestMethod.POST, RequestMethod.PUT}) + public String update(HttpServletRequest request) throws Exception { + + String namespaceId = WebUtils.optional(request, Constants.REQUEST_PARAM_NAMESPACE_ID, + UtilsAndCommons.getDefaultNamespaceId()); + String dom = WebUtils.required(request, "serviceName"); + String ip = WebUtils.required(request, "ip"); + int port = Integer.parseInt(WebUtils.required(request, "port")); + boolean valid = Boolean.valueOf(WebUtils.required(request, "valid")); + String clusterName = WebUtils.optional(request, "clusterName", UtilsAndCommons.DEFAULT_CLUSTER_NAME); + + if (!DistroMapper.responsible(dom)) { + String server = DistroMapper.mapSrv(dom); + Loggers.EVT_LOG.info("I'm not responsible for " + dom + ", proxy it to " + server); + Map proxyParams = new HashMap<>(16); + for (Map.Entry entry : request.getParameterMap().entrySet()) { + String key = entry.getKey(); + String value = entry.getValue()[0]; + proxyParams.put(key, value); + } + + if (!server.contains(UtilsAndCommons.CLUSTER_CONF_IP_SPLITER)) { + server = server + UtilsAndCommons.CLUSTER_CONF_IP_SPLITER + RunningConfig.getServerPort(); + } + + String url = "http://" + server + RunningConfig.getContextPath() + + UtilsAndCommons.NACOS_NAMING_CONTEXT + "/health"; + HttpClient.HttpResult httpResult = HttpClient.httpPost(url, null, proxyParams); + + if (httpResult.code != HttpURLConnection.HTTP_OK) { + throw new IllegalArgumentException("failed to proxy health update to " + server + ", dom: " + dom); + } + } else { + VirtualClusterDomain virtualClusterDomain = (VirtualClusterDomain) domainsManager.getDomain(namespaceId, dom); + // Only health check "none" need update health status with api + if (!virtualClusterDomain.getEnableHealthCheck() && !virtualClusterDomain.getEnableClientBeat()) { + for (IpAddress ipAddress : virtualClusterDomain.allIPs(Lists.newArrayList(clusterName))) { + if (ipAddress.getIp().equals(ip) && ipAddress.getPort() == port) { + ipAddress.setValid(valid); + Loggers.EVT_LOG.info((valid ? "[IP-ENABLED]" : "[IP-DISABLED]") + " ips: " + + ipAddress.getIp() + ":" + ipAddress.getPort() + "@" + ipAddress.getClusterName() + + ", dom: " + dom + ", msg: update thought HealthController api"); + PushService.domChanged(namespaceId, virtualClusterDomain.getName()); + break; + } + } + } else { + throw new IllegalArgumentException("health check mode 'client' and 'server' are not supported , dom: " + dom); + } + } + return "ok"; + } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/ServiceController.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/ServiceController.java index 85396fe87..0e91260b8 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/ServiceController.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/ServiceController.java @@ -271,7 +271,7 @@ public class ServiceController { continue; } for (IpAddress address : serviceObj.allIPs()) { - if (value.equals(address.getMetadata().get(key))) { + if (address.getMetadata() != null && value.equals(address.getMetadata().get(key))) { filteredServices.add(service); break; } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceManager.java b/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceManager.java index fa4e988a4..0413ca45c 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceManager.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceManager.java @@ -300,6 +300,9 @@ public class ServiceManager implements DataListener { } public List getAllDomNamesList(String namespaceId) { + if (chooseDomMap(namespaceId) == null) { + return new ArrayList<>(); + } return new ArrayList<>(chooseDomMap(namespaceId).keySet()); } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/exception/ResponseExceptionHandler.java b/naming/src/main/java/com/alibaba/nacos/naming/exception/ResponseExceptionHandler.java index c47c89df9..9a20a2f57 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/exception/ResponseExceptionHandler.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/exception/ResponseExceptionHandler.java @@ -15,6 +15,7 @@ */ package com.alibaba.nacos.naming.exception; +import com.alibaba.nacos.naming.misc.Loggers; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.MissingServletRequestParameterException; @@ -29,22 +30,26 @@ public class ResponseExceptionHandler { @ExceptionHandler(NacosException.class) private ResponseEntity handleNacosException(NacosException e) { + Loggers.SRV_LOG.error("got exception.", e); return ResponseEntity.status(e.getErrorCode()).body(e.getMessage()); } @ExceptionHandler(IllegalArgumentException.class) public ResponseEntity handleParameterError(IllegalArgumentException ex) { + Loggers.SRV_LOG.error("got exception.", ex); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ex.getMessage()); } @ExceptionHandler(MissingServletRequestParameterException.class) public ResponseEntity handleMissingParams(MissingServletRequestParameterException ex) { + Loggers.SRV_LOG.error("got exception.", ex); String name = ex.getParameterName(); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Parameter '" + name + "' is missing"); } @ExceptionHandler(Exception.class) private ResponseEntity handleNacosException(Exception e) { + Loggers.SRV_LOG.error("got exception.", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.toString()); } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HttpHealthCheckProcessor.java b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HttpHealthCheckProcessor.java index bbe77d760..992469896 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HttpHealthCheckProcessor.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HttpHealthCheckProcessor.java @@ -133,6 +133,7 @@ public class HttpHealthCheckProcessor implements HealthCheckProcessor { } builder.execute(new HttpHealthCheckCallback(ip, task)); + MetricsMonitor.getHttpHealthCheckMonitor().incrementAndGet(); } catch (Throwable e) { ip.setCheckRT(switchDomain.getHttpHealthParams().getMax()); healthCheckCommon.checkFail(ip, task, "http:error:" + e.getMessage()); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/MysqlHealthCheckProcessor.java b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/MysqlHealthCheckProcessor.java index 09e0f2606..0b6eaf915 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/MysqlHealthCheckProcessor.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/MysqlHealthCheckProcessor.java @@ -120,6 +120,7 @@ public class MysqlHealthCheckProcessor implements HealthCheckProcessor { } EXECUTOR.execute(new MysqlCheckTask(ip, task)); + MetricsMonitor.getMysqlHealthCheckMonitor().incrementAndGet(); } catch (Exception e) { ip.setCheckRT(switchDomain.getMysqlHealthParams().getMax()); healthCheckCommon.checkFail(ip, task, "mysql:error:" + e.getMessage()); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/TcpSuperSenseProcessor.java b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/TcpSuperSenseProcessor.java index 8ab4b5a10..8a39c9a0b 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/TcpSuperSenseProcessor.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/TcpSuperSenseProcessor.java @@ -19,6 +19,8 @@ import com.alibaba.nacos.naming.core.Cluster; import com.alibaba.nacos.naming.core.IpAddress; import com.alibaba.nacos.naming.core.VirtualClusterDomain; import com.alibaba.nacos.naming.misc.Loggers; +import com.alibaba.nacos.naming.misc.Switch; +import com.alibaba.nacos.naming.monitor.MetricsMonitor; import com.alibaba.nacos.naming.misc.SwitchDomain; import org.apache.commons.collections.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -138,6 +140,7 @@ public class TcpSuperSenseProcessor implements HealthCheckProcessor, Runnable { Beat beat = new Beat(ip, task); taskQueue.add(beat); + MetricsMonitor.getTcpHealthCheckMonitor().incrementAndGet(); } // selector.wakeup(); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/monitor/MetricsMonitor.java b/naming/src/main/java/com/alibaba/nacos/naming/monitor/MetricsMonitor.java new file mode 100644 index 000000000..3dae43c46 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/monitor/MetricsMonitor.java @@ -0,0 +1,146 @@ +/* + * 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.monitor; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.ImmutableTag; +import io.micrometer.core.instrument.Metrics; +import io.micrometer.core.instrument.Tag; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Metrics Monitor + * + * @author Nacos + */ +public class MetricsMonitor { + private static AtomicInteger mysqlHealthCheck = new AtomicInteger(); + private static AtomicInteger httpHealthCheck = new AtomicInteger(); + private static AtomicInteger tcpHealthCheck = new AtomicInteger(); + private static AtomicInteger domCount = new AtomicInteger(); + private static AtomicInteger ipCount = new AtomicInteger(); + private static AtomicLong maxPushCost = new AtomicLong(); + private static AtomicLong avgPushCost = new AtomicLong(); + private static AtomicLong leaderStatus = new AtomicLong(); + private static AtomicInteger totalPush = new AtomicInteger(); + private static AtomicInteger failedPush = new AtomicInteger(); + + static { + List tags = new ArrayList(); + tags.add(new ImmutableTag("module", "naming")); + tags.add(new ImmutableTag("name", "mysqlhealthCheck")); + Metrics.gauge("nacos_monitor", tags, mysqlHealthCheck); + + tags = new ArrayList(); + tags.add(new ImmutableTag("module", "naming")); + tags.add(new ImmutableTag("name", "httpHealthCheck")); + Metrics.gauge("nacos_monitor", tags, httpHealthCheck); + + tags = new ArrayList(); + tags.add(new ImmutableTag("module", "naming")); + tags.add(new ImmutableTag("name", "tcpHealthCheck")); + Metrics.gauge("nacos_monitor", tags, tcpHealthCheck); + + tags = new ArrayList(); + tags.add(new ImmutableTag("module", "naming")); + tags.add(new ImmutableTag("name", "domCount")); + Metrics.gauge("nacos_monitor", tags, domCount); + + tags = new ArrayList(); + tags.add(new ImmutableTag("module", "naming")); + tags.add(new ImmutableTag("name", "ipCount")); + Metrics.gauge("nacos_monitor", tags, ipCount); + + tags = new ArrayList(); + tags.add(new ImmutableTag("module", "naming")); + tags.add(new ImmutableTag("name", "maxPushCost")); + Metrics.gauge("nacos_monitor", tags, maxPushCost); + + tags = new ArrayList(); + tags.add(new ImmutableTag("module", "naming")); + tags.add(new ImmutableTag("name", "avgPushCost")); + Metrics.gauge("nacos_monitor", tags, avgPushCost); + + tags = new ArrayList(); + tags.add(new ImmutableTag("module", "naming")); + tags.add(new ImmutableTag("name", "leaderStatus")); + Metrics.gauge("nacos_monitor", tags, leaderStatus); + + tags = new ArrayList(); + tags.add(new ImmutableTag("module", "naming")); + tags.add(new ImmutableTag("name", "totalPush")); + Metrics.gauge("nacos_monitor", tags, totalPush); + + tags = new ArrayList(); + tags.add(new ImmutableTag("module", "naming")); + tags.add(new ImmutableTag("name", "failedPush")); + Metrics.gauge("nacos_monitor", tags, failedPush); + } + + public static AtomicInteger getMysqlHealthCheckMonitor() { + return mysqlHealthCheck; + } + + public static AtomicInteger getHttpHealthCheckMonitor() { + return httpHealthCheck; + } + + public static AtomicInteger getTcpHealthCheckMonitor() { + return tcpHealthCheck; + } + + public static AtomicInteger getDomCountMonitor() { + return domCount; + } + + public static AtomicInteger getIpCountMonitor() { + return ipCount; + } + + public static AtomicLong getMaxPushCostMonitor() { + return maxPushCost; + } + + public static AtomicLong getAvgPushCostMonitor() { + return avgPushCost; + } + + public static AtomicLong getLeaderStatusMonitor() { + return leaderStatus; + } + + public static AtomicInteger getTotalPushMonitor() { + return totalPush; + } + + public static AtomicInteger getFailedPushMonitor() { + return failedPush; + } + + public static Counter getDiskException() { + return Metrics.counter("nacos_exception", + "module", "naming", "name", "disk"); + } + + public static Counter getLeaderSendBeatFailedException() { + return Metrics.counter("nacos_exception", + "module", "naming", "name", "leaderSendBeatFailed"); + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/monitor/PerformanceLoggerThread.java b/naming/src/main/java/com/alibaba/nacos/naming/monitor/PerformanceLoggerThread.java index c74b83cab..972d7c562 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/monitor/PerformanceLoggerThread.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/monitor/PerformanceLoggerThread.java @@ -21,14 +21,20 @@ import com.alibaba.nacos.naming.misc.SwitchDomain; import com.alibaba.nacos.naming.push.PushService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; +import javax.annotation.PostConstruct; +import java.util.ArrayList; import java.util.Map; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import static com.alibaba.nacos.naming.raft.RaftPeer.State.FOLLOWER; + /** * @author nacos */ @@ -52,7 +58,7 @@ public class PerformanceLoggerThread { } }); - private static final long PERIOD = 1 * 60 * 60; + private static final long PERIOD = 5 * 60; private static final long HEALTH_CHECK_PERIOD = 5 * 60; @PostConstruct @@ -80,6 +86,42 @@ public class PerformanceLoggerThread { PerformanceLogTask task = new PerformanceLogTask(); executor.scheduleWithFixedDelay(task, 30, PERIOD, TimeUnit.SECONDS); executor.scheduleWithFixedDelay(new HealthCheckSwitchTask(), 30, HEALTH_CHECK_PERIOD, TimeUnit.SECONDS); + + } + + @Scheduled(cron = "0 0 0 * * ?") + public void refreshMetrics() { + PushService.setFailedPush(0); + PushService.setTotalPush(0); + MetricsMonitor.getHttpHealthCheckMonitor().set(0); + MetricsMonitor.getMysqlHealthCheckMonitor().set(0); + MetricsMonitor.getTcpHealthCheckMonitor().set(0); + } + + @Scheduled(cron = "0/15 * * * * ?") + public void collectmetrics() { + int domCount = domainsManager.getDomCount(); + MetricsMonitor.getDomCountMonitor().set(domCount); + + int ipCount = domainsManager.getInstanceCount(); + MetricsMonitor.getIpCountMonitor().set(ipCount); + + long maxPushCost = getMaxPushCost(); + MetricsMonitor.getMaxPushCostMonitor().set(maxPushCost); + + long avgPushCost = getAvgPushCost(); + MetricsMonitor.getAvgPushCostMonitor().set(avgPushCost); + + MetricsMonitor.getTotalPushMonitor().set(PushService.getTotalPush()); + MetricsMonitor.getFailedPushMonitor().set(PushService.getFailedPushCount()); + + if (RaftCore.isLeader()) { + MetricsMonitor.getLeaderStatusMonitor().set(1); + } else if (RaftCore.getPeerSet().local().state == FOLLOWER) { + MetricsMonitor.getLeaderStatusMonitor().set(0); + } else { + MetricsMonitor.getLeaderStatusMonitor().set(2); + } } class PerformanceLogTask implements Runnable { @@ -90,8 +132,12 @@ public class PerformanceLoggerThread { int domCount = serviceManager.getDomCount(); int ipCount = serviceManager.getInstanceCount(); long maxPushMaxCost = getMaxPushCost(); + int domCount = domainsManager.getDomCount(); + int ipCount = domainsManager.getInstanceCount(); + long maxPushCost = getMaxPushCost(); long avgPushCost = getAvgPushCost(); - Loggers.PERFORMANCE_LOG.info("[PERFORMANCE] " + "|" + domCount + "|" + ipCount + "|" + maxPushMaxCost + "|" + avgPushCost); + + Loggers.PERFORMANCE_LOG.info("PERFORMANCE:" + "|" + domCount + "|" + ipCount + "|" + maxPushCost + "|" + avgPushCost); } catch (Exception e) { Loggers.SRV_LOG.warn("[PERFORMANCE] Exception while print performance log.", e); } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/PushService.java b/naming/src/main/java/com/alibaba/nacos/naming/push/PushService.java index 6042df4fb..9342d0377 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/PushService.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/PushService.java @@ -126,6 +126,11 @@ public class PushService { } public void addClient(String namespaceId, + public static void setTotalPush(int totalPush) { + PushService.totalPush = totalPush; + } + + public static void addClient(String namespaceId, String dom, String clusters, String agent, @@ -306,6 +311,11 @@ public class PushService { return ackMap.size() + failedPush; } + public static void setFailedPush(int failedPush) { + PushService.failedPush = failedPush; + } + + public static void resetPushState() { ackMap.clear(); } diff --git a/pom.xml b/pom.xml index 76ae3b8e6..28304dcd6 100644 --- a/pom.xml +++ b/pom.xml @@ -481,7 +481,7 @@ org.springframework.boot spring-boot-dependencies - 2.0.5.RELEASE + 2.1.1.RELEASE pom import diff --git a/test/src/test/java/com/alibaba/nacos/test/config/ConfigAPI_ITCase.java b/test/src/test/java/com/alibaba/nacos/test/config/ConfigAPI_ITCase.java index 5d8cf4d57..103fce8dd 100644 --- a/test/src/test/java/com/alibaba/nacos/test/config/ConfigAPI_ITCase.java +++ b/test/src/test/java/com/alibaba/nacos/test/config/ConfigAPI_ITCase.java @@ -18,16 +18,14 @@ package com.alibaba.nacos.test.config; import com.alibaba.fastjson.JSON; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.PropertyKeyConst; -import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.listener.AbstractListener; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.client.config.http.MetricsHttpAgent; +import com.alibaba.nacos.client.config.http.ServerHttpAgent; import com.alibaba.nacos.client.config.impl.HttpSimpleClient.HttpResult; -import com.alibaba.nacos.client.config.impl.LocalConfigInfoProcessor; -import com.alibaba.nacos.client.config.impl.ServerHttpAgent; -import com.alibaba.nacos.client.logger.json.JSONObject; -import com.alibaba.nacos.client.utils.StringUtils; +import com.alibaba.nacos.client.config.http.HttpAgent; import com.alibaba.nacos.config.server.Config; import org.junit.After; import org.junit.Assert; @@ -40,7 +38,6 @@ import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.web.server.LocalServerPort; import org.springframework.test.context.junit4.SpringRunner; -import java.io.IOException; import java.net.HttpURLConnection; import java.util.Arrays; import java.util.List; @@ -57,7 +54,7 @@ import java.util.concurrent.atomic.AtomicInteger; public class ConfigAPI_ITCase { public static final long TIME_OUT = 2000; public ConfigService iconfig = null; - ServerHttpAgent agent = null; + HttpAgent agent = null; static final String CONFIG_CONTROLLER_PATH = "/v1/cs/configs"; String SPECIAL_CHARACTERS = "!@#$%^&*()_+-=_|/'?."; @@ -76,7 +73,7 @@ public class ConfigAPI_ITCase { properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1"+":"+port); iconfig = NacosFactory.createConfigService(properties); - agent = new ServerHttpAgent(properties); + agent = new MetricsHttpAgent(new ServerHttpAgent(properties)); agent.start(); } diff --git a/test/src/test/java/com/alibaba/nacos/test/naming/SelectInstances_ITCase.java b/test/src/test/java/com/alibaba/nacos/test/naming/SelectInstances_ITCase.java index 508efa7d3..372d0faf9 100644 --- a/test/src/test/java/com/alibaba/nacos/test/naming/SelectInstances_ITCase.java +++ b/test/src/test/java/com/alibaba/nacos/test/naming/SelectInstances_ITCase.java @@ -394,7 +394,7 @@ public class SelectInstances_ITCase { TimeUnit.SECONDS.sleep(10); - expressionSelector.setExpression("INSTANCE.metadata.registerSource = 'spring'"); + expressionSelector.setExpression("INSTANCE.label.registerSource = 'spring'"); serviceList = naming.getServicesOfServer(1, 10, expressionSelector); Assert.assertTrue(serviceList.getData().contains(serviceName));