From 6ee942ef7c807e4a885444e8e8ade402d2e3c120 Mon Sep 17 00:00:00 2001 From: Pan-YuJie <646836760@qq.com> Date: Thu, 12 Sep 2024 15:24:10 +0800 Subject: [PATCH] feat:Agent dynamic monitor Initialize --- .../spring-boot-2x-plugin/pom.xml | 8 + .../spring-plugin-common/pom.xml | 25 +++ .../alarm/AgentModeNotifyConfigBuilder.java | 200 ------------------ .../spring/common/conf/SpringBootConfig.java | 5 + .../monitor/MonitorHandlersConfigurator.java | 186 ++++++++++++++++ .../common/monitor/MonitorMetricEndpoint.java | 97 +++++++++ .../support/SpringPropertiesLoader.java | 8 +- .../support/ThreadPoolMonitorSupport.java | 137 ++++++++++++ .../core/inittest/AlarmSendMessageTest.java | 2 +- .../agent/config-nacos/pom.xml | 8 - .../monitor/MonitorCollectTypeEnum.java | 50 +++++ .../monitor/MonitorHandlerTypeEnum.java | 36 ++++ .../config/properties/MonitorProperties.java | 5 + 13 files changed, 557 insertions(+), 210 deletions(-) delete mode 100644 agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/alarm/AgentModeNotifyConfigBuilder.java create mode 100644 agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorHandlersConfigurator.java create mode 100644 agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorMetricEndpoint.java create mode 100644 agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolMonitorSupport.java create mode 100644 infra/common/src/main/java/cn/hippo4j/common/monitor/MonitorCollectTypeEnum.java create mode 100644 infra/common/src/main/java/cn/hippo4j/common/monitor/MonitorHandlerTypeEnum.java diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/pom.xml b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/pom.xml index 40c0198a..a05c33f3 100644 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/pom.xml +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/pom.xml @@ -38,11 +38,19 @@ provided + + cn.hippo4j + hippo4j-threadpool-core + ${project.version} + provided + + com.ctrip.framework.apollo apollo-client provided + com.alibaba.nacos nacos-client diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml index c3780048..c472c7b3 100644 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml @@ -49,5 +49,30 @@ ${project.version} provided + + cn.hippo4j + hippo4j-threadpool-adapter-web + ${project.version} + provided + + + cn.hippo4j + hippo4j-threadpool-monitor-elasticsearch + ${project.version} + + + cn.hippo4j + hippo4j-threadpool-monitor-local-log + ${project.version} + + + cn.hippo4j + hippo4j-threadpool-monitor-micrometer + ${project.version} + + + io.micrometer + micrometer-registry-prometheus + \ No newline at end of file diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/alarm/AgentModeNotifyConfigBuilder.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/alarm/AgentModeNotifyConfigBuilder.java deleted file mode 100644 index d1345334..00000000 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/alarm/AgentModeNotifyConfigBuilder.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 cn.hippo4j.agent.plugin.spring.common.alarm; - -import cn.hippo4j.agent.plugin.spring.common.support.ThreadPoolCheckAlarmSupport; -import cn.hippo4j.common.api.IExecutorProperties; -import cn.hippo4j.common.model.executor.ExecutorNotifyProperties; -import cn.hippo4j.common.model.executor.ExecutorProperties; -import cn.hippo4j.common.toolkit.CollectionUtil; -import cn.hippo4j.common.toolkit.StringUtil; -import cn.hippo4j.threadpool.dynamic.mode.config.properties.NotifyPlatformProperties; -import cn.hippo4j.threadpool.message.api.NotifyConfigBuilder; -import cn.hippo4j.threadpool.message.api.NotifyConfigDTO; -import cn.hippo4j.threadpool.message.core.service.AlarmControlHandler; -import lombok.AllArgsConstructor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; - -import static cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader.BOOTSTRAP_CONFIG_PROPERTIES; -import static cn.hippo4j.common.constant.Constants.DEFAULT_INTERVAL; - -/** - * This class is responsible for building the notification configurations for thread pools in an agent mode. - * It implements the {@link NotifyConfigBuilder} interface and provides methods to build and initialize - * notification configurations for various platforms and types (e.g., ALARM, CONFIG). - * - *

The configuration is based on the properties loaded from the bootstrap configuration and includes - * handling for alarm control and notification intervals.

- */ -@AllArgsConstructor -public class AgentModeNotifyConfigBuilder implements NotifyConfigBuilder { - - private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolCheckAlarmSupport.class); - - private final AlarmControlHandler alarmControlHandler; - - /** - * Builds the notification configurations for all executors defined in the bootstrap configuration. - * - *

This method filters the executors based on their alarm settings and constructs the notification - * configurations accordingly. If global alarm settings are disabled and there are no specific alarms - * configured for any executor, the method returns an empty map.

- * - * @return A map containing the notification configurations, keyed by the notification type (e.g., ALARM, CONFIG). - */ - public Map> buildNotify() { - Map> resultMap = new HashMap<>(); - boolean globalAlarm = Optional.ofNullable(BOOTSTRAP_CONFIG_PROPERTIES.getDefaultExecutor()) - .map(ExecutorProperties::getAlarm) - .orElse(true); - List executors = BOOTSTRAP_CONFIG_PROPERTIES.getExecutors(); - if (CollectionUtil.isEmpty(executors)) { - LOGGER.warn("Failed to build notify, executors configuration is empty."); - return resultMap; - } - List actual = executors.stream() - .filter(each -> Optional.ofNullable(each.getAlarm()) - .orElse(false)) - .collect(Collectors.toList()); - if (!globalAlarm && CollectionUtil.isEmpty(actual)) { - return resultMap; - } - for (ExecutorProperties executorProperties : executors) { - Map> buildSingleNotifyConfig = buildSingleNotifyConfig(executorProperties); - initCacheAndLock(buildSingleNotifyConfig); - resultMap.putAll(buildSingleNotifyConfig); - } - - return resultMap; - } - - /** - * Builds the notification configurations for a single executor. - * - *

This method generates two types of notifications: ALARM and CONFIG. For each type, it creates - * notification configurations based on the platforms defined in the bootstrap configuration.

- * - * @param executorProperties The properties of the executor for which to build the notification configurations. - * @return A map containing the notification configurations for the given executor, keyed by the notification type. - */ - public Map> buildSingleNotifyConfig(IExecutorProperties executorProperties) { - String threadPoolId = executorProperties.getThreadPoolId(); - Map> resultMap = new HashMap<>(); - String alarmBuildKey = threadPoolId + "+ALARM"; - List alarmNotifyConfigs = new ArrayList<>(); - List notifyPlatforms = BOOTSTRAP_CONFIG_PROPERTIES.getNotifyPlatforms(); - for (NotifyPlatformProperties platformProperties : notifyPlatforms) { - NotifyConfigDTO notifyConfig = new NotifyConfigDTO(); - notifyConfig.setPlatform(platformProperties.getPlatform()); - notifyConfig.setTpId(threadPoolId); - notifyConfig.setType("ALARM"); - notifyConfig.setSecret(platformProperties.getSecret()); - notifyConfig.setSecretKey(getToken(platformProperties)); - notifyConfig.setInterval(buildInterval(executorProperties)); - notifyConfig.setReceives(buildReceive(executorProperties)); - alarmNotifyConfigs.add(notifyConfig); - } - resultMap.put(alarmBuildKey, alarmNotifyConfigs); - String changeBuildKey = threadPoolId + "+CONFIG"; - List changeNotifyConfigs = new ArrayList<>(); - for (NotifyPlatformProperties platformProperties : notifyPlatforms) { - NotifyConfigDTO notifyConfig = new NotifyConfigDTO(); - notifyConfig.setPlatform(platformProperties.getPlatform()); - notifyConfig.setTpId(threadPoolId); - notifyConfig.setType("CONFIG"); - notifyConfig.setSecretKey(getToken(platformProperties)); - notifyConfig.setSecret(platformProperties.getSecret()); - notifyConfig.setReceives(buildReceive(executorProperties)); - changeNotifyConfigs.add(notifyConfig); - } - resultMap.put(changeBuildKey, changeNotifyConfigs); - return resultMap; - } - - /** - * Retrieves the token for the given notification platform properties. - * - *

If the token is not explicitly set, the method returns the secret key as the fallback.

- * - * @param platformProperties The platform properties from which to retrieve the token. - * @return The token or secret key associated with the given platform properties. - */ - private String getToken(NotifyPlatformProperties platformProperties) { - return StringUtil.isNotBlank(platformProperties.getToken()) ? platformProperties.getToken() : platformProperties.getSecretKey(); - } - - /** - * Builds the notification interval for the given executor properties. - * - *

This method first checks the executor's specific notify configuration. If not set, it falls back - * to the default executor configuration in the bootstrap properties.

- * - * @param executorProperties The properties of the executor for which to build the notification interval. - * @return The notification interval in seconds. - */ - private int buildInterval(IExecutorProperties executorProperties) { - return Optional.ofNullable(executorProperties.getNotify()) - .map(ExecutorNotifyProperties::getInterval) - .orElse(Optional.ofNullable(BOOTSTRAP_CONFIG_PROPERTIES.getDefaultExecutor()) - .map(ExecutorProperties::getNotify) - .map(ExecutorNotifyProperties::getInterval) - .orElse(DEFAULT_INTERVAL)); - } - - /** - * Builds the notification recipients for the given executor properties. - * - *

This method first checks the executor's specific notify configuration. If not set, it falls back - * to the default executor configuration in the bootstrap properties.

- * - * @param executorProperties The properties of the executor for which to build the notification recipients. - * @return A string containing the recipients of the notifications. - */ - private String buildReceive(IExecutorProperties executorProperties) { - return Optional.ofNullable(executorProperties.getNotify()) - .map(ExecutorNotifyProperties::getReceives) - .orElse(Optional.ofNullable(BOOTSTRAP_CONFIG_PROPERTIES.getDefaultExecutor()) - .map(ExecutorProperties::getNotify) - .map(ExecutorNotifyProperties::getReceives).orElse("")); - } - - /** - * Initializes the cache and lock mechanisms for the given notification configurations. - * - *

This method is primarily responsible for setting up alarm controls based on the notification - * configurations, ensuring that the appropriate cache and lock mechanisms are initialized for - * each thread pool and platform combination.

- * - * @param buildSingleNotifyConfig A map containing the notification configurations that need cache and lock initialization. - */ - public void initCacheAndLock(Map> buildSingleNotifyConfig) { - buildSingleNotifyConfig.forEach( - (key, val) -> val.stream() - .filter(each -> Objects.equals("ALARM", each.getType())) - .forEach(each -> alarmControlHandler.initCacheAndLock(each.getTpId(), each.getPlatform(), each.getInterval()))); - } -} diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/conf/SpringBootConfig.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/conf/SpringBootConfig.java index b93149a2..48e05894 100644 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/conf/SpringBootConfig.java +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/conf/SpringBootConfig.java @@ -30,11 +30,13 @@ public class SpringBootConfig { /** * Spring */ + @SpringBootConfigNode(root = SpringBootConfig.class) public static class Spring { /** * Dynamic */ + @SpringBootConfigNode(root = SpringBootConfig.class) public static class Dynamic { /** @@ -79,6 +81,9 @@ public class SpringBootConfig { public static Long initialDelay = 10000L; public static Long collectInterval = 5000L; + + public static Integer AGENT_MICROMETER_PORT; + } public static String CONFIG_FILE_TYPE; diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorHandlersConfigurator.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorHandlersConfigurator.java new file mode 100644 index 00000000..56e1b7f0 --- /dev/null +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorHandlersConfigurator.java @@ -0,0 +1,186 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 cn.hippo4j.agent.plugin.spring.common.monitor; + +import cn.hippo4j.common.extension.spi.ServiceLoaderRegistry; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; +import cn.hippo4j.common.monitor.MonitorCollectTypeEnum; +import cn.hippo4j.common.monitor.MonitorHandlerTypeEnum; +import cn.hippo4j.core.executor.state.ThreadPoolRunStateHandler; +import cn.hippo4j.core.toolkit.inet.InetUtils; +import cn.hippo4j.core.toolkit.inet.InetUtilsProperties; +import cn.hippo4j.monitor.elasticsearch.AdapterThreadPoolElasticSearchMonitorHandler; +import cn.hippo4j.monitor.elasticsearch.DynamicThreadPoolElasticSearchMonitorHandler; +import cn.hippo4j.monitor.elasticsearch.WebThreadPoolElasticSearchMonitorHandler; +import cn.hippo4j.monitor.local.log.AdapterThreadPoolLocalLogMonitorHandler; +import cn.hippo4j.monitor.local.log.DynamicThreadPoolLocalLogMonitorHandler; +import cn.hippo4j.monitor.local.log.WebThreadPoolLocalLogMonitorHandler; +import cn.hippo4j.monitor.micrometer.AdapterThreadPoolMicrometerMonitorHandler; +import cn.hippo4j.monitor.micrometer.DynamicThreadPoolMicrometerMonitorHandler; +import cn.hippo4j.monitor.micrometer.WebThreadPoolMicrometerMonitorHandler; +import cn.hippo4j.threadpool.dynamic.mode.config.properties.MonitorProperties; +import cn.hippo4j.threadpool.monitor.api.ThreadPoolMonitor; +import org.springframework.core.env.ConfigurableEnvironment; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; + +import static cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader.INET_UTILS_PROPERTIES; + +/** + * This class is responsible for configuring and initializing monitoring handlers + * for various types of thread pools. It maps specific monitoring types (e.g., Micrometer, + * Log, Elasticsearch) to their corresponding handler initializers and manages the + * setup process based on the provided configuration. + */ +public class MonitorHandlersConfigurator { + + private static final ILog LOGGER = LogManager.getLogger(MonitorHandlersConfigurator.class); + + // Maps thread pool types to their corresponding handler constructors + private static final Map> handlerMap = new HashMap<>(); + + static { + // Initialize the handler map with specific monitoring types + handlerMap.put(MonitorCollectTypeEnum.MICROMETER.getValue(), MonitorHandlersConfigurator::handleMicrometer); + handlerMap.put(MonitorCollectTypeEnum.LOG.getValue(), MonitorHandlersConfigurator::handleLog); + handlerMap.put(MonitorCollectTypeEnum.ELASTICSEARCH.getValue(), MonitorHandlersConfigurator::handleElasticSearch); + } + + /** + * Initializes the monitoring handlers based on the provided monitoring configuration. + *

+ * This method performs the following tasks: + *

    + *
  • Parses the configured monitoring types and thread pool types.
  • + *
  • Initializes a monitoring context with the necessary thread pool monitors and state handler.
  • + *
  • For each configured monitoring type, invokes the corresponding handler initializer + * for each relevant thread pool type.
  • + *
  • Logs a warning if an unrecognized monitoring type is encountered.
  • + *
  • Registers and adds thread pool monitors that match the configured monitoring types.
  • + *
+ * + * @param monitor The monitoring properties configuration. + * @param environment The application environment from which additional configuration can be loaded. + * @param threadPoolMonitors A list to hold the initialized thread pool monitors. + */ + public static void initializeMonitorHandlers(MonitorProperties monitor, ConfigurableEnvironment environment, List threadPoolMonitors) { + List collectTypes = Arrays.asList(monitor.getCollectTypes().split(",")); + List threadPoolTypes = Arrays.asList(monitor.getThreadPoolTypes().split(",")); + ThreadPoolRunStateHandler threadPoolRunStateHandler = new ThreadPoolRunStateHandler( + new InetUtils(INET_UTILS_PROPERTIES), environment); + + MonitorHandlerContext context = new MonitorHandlerContext(threadPoolMonitors, threadPoolRunStateHandler); + + // Initialize handlers for each configured monitoring type and thread pool type + for (String collectType : collectTypes) { + if (handlerMap.containsKey(collectType)) { + for (MonitorHandlerTypeEnum type : MonitorHandlerTypeEnum.values()) { + if (threadPoolTypes.contains(type.name().toLowerCase())) { + handlerMap.get(collectType).accept(type, context); + } + } + } else { + LOGGER.warn("[Hippo4j-Agent] MonitorConfigurator initialize Unrecognized collect type: [{}]", collectType); + } + } + + // Register and add dynamic thread pool monitors matching the configured types + Collection dynamicThreadPoolMonitors = ServiceLoaderRegistry.getSingletonServiceInstances(ThreadPoolMonitor.class); + dynamicThreadPoolMonitors.stream().filter(each -> collectTypes.contains(each.getType())).forEach(threadPoolMonitors::add); + } + + /** + * Initializes Micrometer-based monitoring handlers for the specified thread pool type. + * + * @param type The type of thread pool to be monitored. + * @param context The context containing the monitors and state handler. + */ + private static void handleMicrometer(MonitorHandlerTypeEnum type, MonitorHandlerContext context) { + switch (type) { + case DYNAMIC: + context.monitors.add(new DynamicThreadPoolMicrometerMonitorHandler(context.threadPoolRunStateHandler)); + break; + case WEB: + context.monitors.add(new WebThreadPoolMicrometerMonitorHandler()); + break; + case ADAPTER: + context.monitors.add(new AdapterThreadPoolMicrometerMonitorHandler()); + break; + } + } + + /** + * Initializes Log-based monitoring handlers for the specified thread pool type. + * + * @param type The type of thread pool to be monitored. + * @param context The context containing the monitors and state handler. + */ + private static void handleLog(MonitorHandlerTypeEnum type, MonitorHandlerContext context) { + switch (type) { + case DYNAMIC: + context.monitors.add(new DynamicThreadPoolLocalLogMonitorHandler(context.threadPoolRunStateHandler)); + break; + case WEB: + context.monitors.add(new WebThreadPoolLocalLogMonitorHandler()); + break; + case ADAPTER: + context.monitors.add(new AdapterThreadPoolLocalLogMonitorHandler()); + break; + } + } + + /** + * Initializes Elasticsearch-based monitoring handlers for the specified thread pool type. + * + * @param type The type of thread pool to be monitored. + * @param context The context containing the monitors and state handler. + */ + private static void handleElasticSearch(MonitorHandlerTypeEnum type, MonitorHandlerContext context) { + switch (type) { + case DYNAMIC: + context.monitors.add(new DynamicThreadPoolElasticSearchMonitorHandler(context.threadPoolRunStateHandler)); + break; + case WEB: + context.monitors.add(new WebThreadPoolElasticSearchMonitorHandler(context.threadPoolRunStateHandler)); + break; + case ADAPTER: + context.monitors.add(new AdapterThreadPoolElasticSearchMonitorHandler(context.threadPoolRunStateHandler)); + break; + } + } + + /** + * A helper class to manage the context in which monitoring handlers are initialized. + */ + private static class MonitorHandlerContext { + + List monitors; + ThreadPoolRunStateHandler threadPoolRunStateHandler; + + MonitorHandlerContext(List monitors, ThreadPoolRunStateHandler handler) { + this.monitors = monitors; + this.threadPoolRunStateHandler = handler; + } + } +} diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorMetricEndpoint.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorMetricEndpoint.java new file mode 100644 index 00000000..b8ae07bc --- /dev/null +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorMetricEndpoint.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 cn.hippo4j.agent.plugin.spring.common.monitor; + +import cn.hippo4j.agent.plugin.spring.common.conf.SpringBootConfig; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import io.micrometer.core.instrument.Metrics; +import io.micrometer.core.instrument.composite.CompositeMeterRegistry; +import io.micrometer.prometheus.PrometheusConfig; +import io.micrometer.prometheus.PrometheusMeterRegistry; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; + +/** + * This class is responsible for exposing Prometheus metrics via an HTTP endpoint. + * It initializes the Prometheus registry, binds it to the global metrics registry, + * and starts an HTTP server to serve the metrics data. + */ +public class MonitorMetricEndpoint { + + private static final ILog LOGGER = LogManager.getLogger(MonitorHandlersConfigurator.class); + + /** + * Starts the Prometheus metrics HTTP server. + *

+ * This method performs the following steps: + *

    + *
  • Initializes the PrometheusMeterRegistry with the default configuration.
  • + *
  • Binds the Prometheus registry to the global CompositeMeterRegistry.
  • + *
  • Attempts to start an HTTP server on the configured port to expose the Prometheus metrics.
  • + *
+ * If the port is not configured, or if there is an error starting the server, appropriate error messages + * are logged, and the method returns without starting the server. + *

+ */ + public static void startPrometheusEndpoint() { + + // Initialize the Prometheus registry + PrometheusMeterRegistry prometheusRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); + + // Bind the Prometheus registry to the global Metrics registry + CompositeMeterRegistry globalRegistry = Metrics.globalRegistry; + globalRegistry.add(prometheusRegistry); + + // Get the configured port for the Prometheus metrics HTTP server + Integer port = SpringBootConfig.Spring.Dynamic.Thread_Pool.Monitor.AGENT_MICROMETER_PORT; + if (port == null) { + LOGGER.error( + "[Hippo4j-Agent] Failed to start Prometheus metrics endpoint server. Please configure the exposed endpoint by adding: spring.dynamic.thread-pool.monitor.port=xxx to the configuration file"); + return; + } + + // Create the HTTP server + HttpServer server = null; + try { + server = HttpServer.create(new InetSocketAddress(port), 0); + } catch (IOException e) { + LOGGER.error("[Hippo4j-Agent] Failed to start Prometheus metrics endpoint server", e); + return; + } + + // Register the /actuator/prometheus context to handle metrics requests + server.createContext("/actuator/prometheus", exchange -> { + String response = prometheusRegistry.scrape(); // Get metrics data in Prometheus format + exchange.sendResponseHeaders(200, response.getBytes().length); + try (OutputStream os = exchange.getResponseBody()) { + os.write(response.getBytes()); + } + }); + + // Start the server + server.start(); + LOGGER.info("[Hippo4j-Agent] Prometheus metrics server started on port {}", port); + } + +} diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/SpringPropertiesLoader.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/SpringPropertiesLoader.java index bb6e521f..d9fbfac7 100644 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/SpringPropertiesLoader.java +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/SpringPropertiesLoader.java @@ -21,6 +21,7 @@ import cn.hippo4j.agent.core.boot.SpringBootConfigInitializer; import cn.hippo4j.agent.plugin.spring.common.toolkit.SpringPropertyBinder; import cn.hippo4j.common.logging.api.ILog; import cn.hippo4j.common.logging.api.LogManager; +import cn.hippo4j.core.toolkit.inet.InetUtilsProperties; import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; @@ -42,6 +43,8 @@ public class SpringPropertiesLoader { public static BootstrapConfigProperties BOOTSTRAP_CONFIG_PROPERTIES = new BootstrapConfigProperties(); + public static InetUtilsProperties INET_UTILS_PROPERTIES = new InetUtilsProperties(); + public static void loadSpringProperties(ConfigurableEnvironment environment) { Iterator> iterator = environment.getPropertySources().iterator(); Properties properties = new Properties(); @@ -83,8 +86,11 @@ public class SpringPropertiesLoader { environment.getPropertySources().addFirst(propertySource); // initialize BootstrapConfigProperties BOOTSTRAP_CONFIG_PROPERTIES = SpringPropertyBinder.bindProperties(environment, PREFIX, BootstrapConfigProperties.class); - + INET_UTILS_PROPERTIES = SpringPropertyBinder.bindProperties(environment, InetUtilsProperties.PREFIX, InetUtilsProperties.class); + // Enable the thread pool check alert handler ThreadPoolCheckAlarmSupport.enableThreadPoolCheckAlarmHandler(); + // Enable thread pool monitor handler + ThreadPoolMonitorSupport.enableThreadPoolMonitorHandler(environment); } } diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolMonitorSupport.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolMonitorSupport.java new file mode 100644 index 00000000..3af1e1e6 --- /dev/null +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolMonitorSupport.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 cn.hippo4j.agent.plugin.spring.common.support; + +import cn.hippo4j.agent.plugin.spring.common.monitor.MonitorHandlersConfigurator; +import cn.hippo4j.agent.plugin.spring.common.monitor.MonitorMetricEndpoint; +import cn.hippo4j.common.executor.ThreadFactoryBuilder; +import cn.hippo4j.common.executor.ThreadPoolExecutorRegistry; +import cn.hippo4j.common.extension.spi.ServiceLoaderRegistry; +import cn.hippo4j.common.monitor.MonitorCollectTypeEnum; +import cn.hippo4j.common.toolkit.StringUtil; +import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties; +import cn.hippo4j.threadpool.dynamic.mode.config.properties.MonitorProperties; +import cn.hippo4j.threadpool.monitor.api.ThreadPoolMonitor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.Environment; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import static cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader.BOOTSTRAP_CONFIG_PROPERTIES; + +/** + * This class provides support for monitoring dynamic thread pools in an application. + * It includes methods to initialize and enable monitoring components, and schedules + * periodic data collection from the thread pools. + */ +public class ThreadPoolMonitorSupport { + + private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolMonitorSupport.class); + + private static List threadPoolMonitors; + + static { + // Register the ThreadPoolMonitor service with the ServiceLoaderRegistry + ServiceLoaderRegistry.register(ThreadPoolMonitor.class); + } + + /** + * Enables the dynamic thread pool monitoring handler. + *

+ * This method performs the following steps: + *

    + *
  • Validates the monitoring configuration from the environment properties.
  • + *
  • Initializes monitoring components for the dynamic thread pools.
  • + *
  • Exposes metric endpoints, such as Prometheus, if configured.
  • + *
  • Schedules periodic collection of metrics from the thread pools.
  • + *
+ * If the monitoring configuration is invalid or disabled, the method returns without + * enabling the monitoring handler. + *

+ * + * @param environment The environment from which the monitoring configuration is loaded. + */ + public static void enableThreadPoolMonitorHandler(Environment environment) { + BootstrapConfigProperties properties = BOOTSTRAP_CONFIG_PROPERTIES; + MonitorProperties monitor = properties.getMonitor(); + if (Objects.isNull(monitor) + || !monitor.getEnable() + || StringUtil.isBlank(monitor.getThreadPoolTypes()) + || StringUtil.isBlank(monitor.getCollectTypes())) { + return; + } + + LOGGER.info("[Hippo4j-Agent] Start monitoring the running status of dynamic thread pools."); + threadPoolMonitors = new ArrayList<>(); + ScheduledExecutorService collectScheduledExecutor = new ScheduledThreadPoolExecutor( + 1, + r -> new Thread(r, "client.agent.scheduled.collect.data")); + + // Initialize monitoring components for the dynamic thread pools + MonitorHandlersConfigurator.initializeMonitorHandlers(monitor, (ConfigurableEnvironment) environment, threadPoolMonitors); + + // Expose metric endpoints based on the configured collect types + List collectTypes = Arrays.asList(monitor.getCollectTypes().split(",")); + if (collectTypes.contains(MonitorCollectTypeEnum.MICROMETER.getValue())) { + MonitorMetricEndpoint.startPrometheusEndpoint(); + } + + // Schedule periodic collection of metrics from the thread pools + collectScheduledExecutor.scheduleWithFixedDelay( + scheduleRunnable(), + monitor.getInitialDelay(), + monitor.getCollectInterval(), + TimeUnit.MILLISECONDS); + + if (ThreadPoolExecutorRegistry.getThreadPoolExecutorSize() > 0) { + LOGGER.info("[Hippo4j-Agent] Dynamic thread pool: [{}]. The dynamic thread pool starts data collection and reporting.", ThreadPoolExecutorRegistry.getThreadPoolExecutorSize()); + } + } + + /** + * Returns a Runnable task that collects metrics from the dynamic thread pools. + *

+ * This method is used to create a task that periodically iterates over the + * registered thread pool monitors and collects their metrics. If an exception + * occurs during the collection, it is logged. + *

+ * + * @return A Runnable task that performs the metrics collection. + */ + private static Runnable scheduleRunnable() { + return () -> { + for (ThreadPoolMonitor each : threadPoolMonitors) { + try { + each.collect(); + } catch (Exception ex) { + LOGGER.error("[Hippo4j-Agent] Error monitoring the running status of dynamic thread pool. Type: {}", each.getType(), ex); + } + } + }; + } + +} diff --git a/examples/threadpool-example/agent/agent-example-core/src/main/java/cn/hippo4j/example/agent/core/inittest/AlarmSendMessageTest.java b/examples/threadpool-example/agent/agent-example-core/src/main/java/cn/hippo4j/example/agent/core/inittest/AlarmSendMessageTest.java index ccf385db..bf67eb75 100644 --- a/examples/threadpool-example/agent/agent-example-core/src/main/java/cn/hippo4j/example/agent/core/inittest/AlarmSendMessageTest.java +++ b/examples/threadpool-example/agent/agent-example-core/src/main/java/cn/hippo4j/example/agent/core/inittest/AlarmSendMessageTest.java @@ -48,7 +48,7 @@ public class AlarmSendMessageTest { * If you need to run this single test, add @PostConstruct to the method. */ @SuppressWarnings("all") - @PostConstruct + // @PostConstruct public void alarmSendMessageTest() { ScheduledExecutorService scheduledThreadPool = Executors.newSingleThreadScheduledExecutor(); scheduledThreadPool.scheduleWithFixedDelay(() -> { diff --git a/examples/threadpool-example/agent/config-nacos/pom.xml b/examples/threadpool-example/agent/config-nacos/pom.xml index 4240c147..8134d488 100644 --- a/examples/threadpool-example/agent/config-nacos/pom.xml +++ b/examples/threadpool-example/agent/config-nacos/pom.xml @@ -44,14 +44,6 @@ slf4j-api 1.7.21 - - io.micrometer - micrometer-registry-prometheus - - - org.springframework.boot - spring-boot-starter-actuator - diff --git a/infra/common/src/main/java/cn/hippo4j/common/monitor/MonitorCollectTypeEnum.java b/infra/common/src/main/java/cn/hippo4j/common/monitor/MonitorCollectTypeEnum.java new file mode 100644 index 00000000..b7a4a8b1 --- /dev/null +++ b/infra/common/src/main/java/cn/hippo4j/common/monitor/MonitorCollectTypeEnum.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 cn.hippo4j.common.monitor; + +/** + * MonitorCollect type enum. + */ +public enum MonitorCollectTypeEnum { + + /** + * Micrometer + */ + MICROMETER("micrometer"), + + /** + * ELASTICSEARCH + */ + ELASTICSEARCH("elasticsearch"), + + /** + * LOG + */ + LOG("log"); + + private final String value; + + MonitorCollectTypeEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + +} diff --git a/infra/common/src/main/java/cn/hippo4j/common/monitor/MonitorHandlerTypeEnum.java b/infra/common/src/main/java/cn/hippo4j/common/monitor/MonitorHandlerTypeEnum.java new file mode 100644 index 00000000..5cf7ad65 --- /dev/null +++ b/infra/common/src/main/java/cn/hippo4j/common/monitor/MonitorHandlerTypeEnum.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 cn.hippo4j.common.monitor; + +/** + * MonitorHandler type enum. + */ +public enum MonitorHandlerTypeEnum { + /** + * DYNAMIC + */ + DYNAMIC, + /** + * WEB + */ + WEB, + /** + * ADAPTER + */ + ADAPTER +} diff --git a/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/properties/MonitorProperties.java b/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/properties/MonitorProperties.java index 38cb1ed1..db4db930 100644 --- a/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/properties/MonitorProperties.java +++ b/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/properties/MonitorProperties.java @@ -51,4 +51,9 @@ public class MonitorProperties { * Collect interval. unit: ms */ private Long collectInterval = 5000L; + + /** + * Agent micrometer exposed port + */ + private Integer agentMicrometerPort; }