+ * This method uses Spring's {@link Binder} to bind the configuration values to an instance
+ * of {@link BootstrapConfigProperties}, which can then be used to configure the thread pool
+ * dynamically.
+ *
+ * @param configInfo the configuration map containing properties to bind.
+ * @return the bound {@link BootstrapConfigProperties} instance.
+ */
@Override
public BootstrapConfigProperties buildBootstrapProperties(Map
cn.hippo4jhippo4j-threadpool-dynamic-core${project.version}
- provided
+
+
+ cn.hippo4j
+ hippo4j-threadpool-kernel-alarm
+ ${project.version}
+
+
+ cn.hippo4j
+ hippo4j-threadpool-core
+ ${project.version}
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+
+ cn.hippo4j
+ hippo4j-threadpool-adapter-web
+ ${project.version}
+
+
+ cn.hippo4j
+ hippo4j-threadpool-infra-common
+ ${project.version}
+
+
+ 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
new file mode 100644
index 00000000..9e8c1ce1
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/alarm/AgentModeNotifyConfigBuilder.java
@@ -0,0 +1,218 @@
+/*
+ * 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.adapter.web.WebThreadPoolService;
+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.dynamic.mode.config.properties.WebExecutorProperties;
+import cn.hippo4j.threadpool.message.api.NotifyConfigBuilder;
+import cn.hippo4j.threadpool.message.api.NotifyConfigDTO;
+import cn.hippo4j.threadpool.message.core.service.AlarmControlHandler;
+import cn.hippo4j.threadpool.message.core.service.ThreadPoolBaseSendMessageService;
+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.
+ *
+ * TODO: This is copied from {@link cn.hippo4j.config.springboot.starter.notify.ConfigModeNotifyConfigBuilder} and can be refactored later
+ */
+@AllArgsConstructor
+public class AgentModeNotifyConfigBuilder implements NotifyConfigBuilder {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolCheckAlarmSupport.class);
+
+ private final AlarmControlHandler alarmControlHandler;
+
+ private final WebThreadPoolService webThreadPoolService;
+
+ /**
+ * 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);
+ }
+ // register notify config for web
+ WebExecutorProperties webProperties = BOOTSTRAP_CONFIG_PROPERTIES.getWeb();
+ if (webProperties == null) {
+ return resultMap;
+ }
+ if (StringUtil.isBlank(webProperties.getThreadPoolId())) {
+ webProperties.setThreadPoolId(webThreadPoolService.getWebContainerType().getName());
+ }
+ Map> webSingleNotifyConfigMap = buildSingleNotifyConfig(webProperties);
+ initCacheAndLock(webSingleNotifyConfigMap);
+ resultMap.putAll(webSingleNotifyConfigMap);
+
+ 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/NacosCloudConfig.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/conf/NacosCloudConfig.java
new file mode 100644
index 00000000..62019812
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/conf/NacosCloudConfig.java
@@ -0,0 +1,51 @@
+/*
+ * 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.conf;
+
+import cn.hippo4j.agent.core.boot.SpringBootConfigNode;
+
+/**
+ * Nacos Cloud config
+ */
+public class NacosCloudConfig {
+
+ public static class Spring {
+
+ /**
+ * Cloud
+ */
+ public static class Cloud {
+
+ /**
+ * Nacos
+ */
+ public static class Nacos {
+
+ /**
+ * Config
+ */
+ @SpringBootConfigNode(root = NacosConfig.class)
+ public static class Config {
+
+ public static String SERVER_ADDR = "";
+
+ }
+ }
+ }
+ }
+}
\ 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/conf/NacosConfig.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/conf/NacosConfig.java
new file mode 100644
index 00000000..07227530
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/conf/NacosConfig.java
@@ -0,0 +1,42 @@
+/*
+ * 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.conf;
+
+import cn.hippo4j.agent.core.boot.SpringBootConfigNode;
+
+/**
+ * nacos config
+ */
+public class NacosConfig {
+
+ /**
+ * Nacos
+ */
+ public static class Nacos {
+
+ /**
+ * Config
+ */
+ @SpringBootConfigNode(root = NacosCloudConfig.class)
+ public static class Config {
+
+ public static String SERVER_ADDR = "";
+
+ }
+ }
+}
\ 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/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 ca979432..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 {
/**
@@ -52,6 +54,18 @@ public class SpringBootConfig {
public static List NAMESPACE = Arrays.asList("application");
}
+ @SpringBootConfigNode(root = SpringBootConfig.class)
+ public static class Nacos {
+
+ public static String SERVER_ADDR = "localhost";
+
+ public static List NAMESPACE = Arrays.asList("");
+
+ public static String DATA_ID = "";
+
+ public static String GROUP = "DEFAULT_GROUP";
+ }
+
/**
* Monitor
*/
@@ -67,10 +81,27 @@ 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;
}
}
+
+ @SpringBootConfigNode(root = SpringBootConfig.class)
+ public static class Application {
+
+ public static String name = "";
+
+ }
+
+ @SpringBootConfigNode(root = SpringBootConfig.class)
+ public static class Profiles {
+
+ public static String active = "";
+
+ }
}
}
diff --git a/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/refresher/event/DynamicThreadPoolRefreshListener.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/event/DynamicThreadPoolRefreshListener.java
similarity index 50%
rename from kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/refresher/event/DynamicThreadPoolRefreshListener.java
rename to agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/event/DynamicThreadPoolRefreshListener.java
index 252e6d0b..836ab712 100644
--- a/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/refresher/event/DynamicThreadPoolRefreshListener.java
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/event/DynamicThreadPoolRefreshListener.java
@@ -15,8 +15,11 @@
* limitations under the License.
*/
-package cn.hippo4j.threadpool.dynamic.mode.config.refresher.event;
+package cn.hippo4j.agent.plugin.spring.common.event;
+import cn.hippo4j.agent.core.util.CollectionUtil;
+import cn.hippo4j.agent.plugin.spring.common.alarm.AgentModeNotifyConfigBuilder;
+import cn.hippo4j.agent.plugin.spring.common.support.ThreadPoolCheckAlarmSupport;
import cn.hippo4j.common.executor.ThreadPoolExecutorHolder;
import cn.hippo4j.common.executor.ThreadPoolExecutorRegistry;
import cn.hippo4j.common.executor.support.BlockingQueueTypeEnum;
@@ -29,10 +32,17 @@ import cn.hippo4j.common.logging.api.LogManager;
import cn.hippo4j.common.model.executor.ExecutorProperties;
import cn.hippo4j.common.toolkit.ThreadPoolExecutorUtil;
import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties;
+import cn.hippo4j.threadpool.message.api.NotifyConfigDTO;
+import cn.hippo4j.threadpool.message.core.request.ChangeParameterNotifyRequest;
+import cn.hippo4j.threadpool.message.core.service.GlobalNotifyAlarmManage;
+import cn.hippo4j.threadpool.message.core.service.ThreadPoolBaseSendMessageService;
+import cn.hippo4j.threadpool.message.core.service.ThreadPoolNotifyAlarm;
import lombok.RequiredArgsConstructor;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -56,6 +66,10 @@ public class DynamicThreadPoolRefreshListener implements Observer> newDynamicThreadPoolNotifyMap = agentNotifyConfigBuilder.buildSingleNotifyConfig(executorProperties);
+ Map> notifyConfigs = threadPoolBaseSendMessageService.getNotifyConfigs();
+ if (CollectionUtil.isNotEmpty(notifyConfigs)) {
+ for (Map.Entry> each : newDynamicThreadPoolNotifyMap.entrySet()) {
+ if (checkNotifyConfig) {
+ break;
+ }
+ List notifyConfigDTOS = notifyConfigs.get(each.getKey());
+ for (NotifyConfigDTO notifyConfig : each.getValue()) {
+ if (!notifyConfigDTOS.contains(notifyConfig)) {
+ checkNotifyConfig = true;
+ break;
+ }
+ }
+ }
+ }
+ if (checkNotifyConfig) {
+ agentNotifyConfigBuilder.initCacheAndLock(newDynamicThreadPoolNotifyMap);
+ threadPoolBaseSendMessageService.putPlatform(newDynamicThreadPoolNotifyMap);
+ }
+ ThreadPoolNotifyAlarm threadPoolNotifyAlarm = GlobalNotifyAlarmManage.get(executorProperties.getThreadPoolId());
+ if (threadPoolNotifyAlarm != null) {
+ Boolean isAlarm = executorProperties.getAlarm();
+ Integer activeAlarm = executorProperties.getActiveAlarm();
+ Integer capacityAlarm =
+ executorProperties.getCapacityAlarm();
+ if ((isAlarm != null && !Objects.equals(isAlarm, threadPoolNotifyAlarm.getAlarm())) || (activeAlarm != null && !Objects.equals(activeAlarm,
+ threadPoolNotifyAlarm.getActiveAlarm())) || (capacityAlarm != null && !Objects.equals(capacityAlarm, threadPoolNotifyAlarm.getCapacityAlarm()))) {
+ checkNotifyAlarm = true;
+ threadPoolNotifyAlarm.setAlarm(Optional.ofNullable(isAlarm).orElse(threadPoolNotifyAlarm.getAlarm()));
+ threadPoolNotifyAlarm.setActiveAlarm(Optional.ofNullable(activeAlarm).orElse(threadPoolNotifyAlarm.getActiveAlarm()));
+ threadPoolNotifyAlarm.setCapacityAlarm(Optional.ofNullable(capacityAlarm).orElse(threadPoolNotifyAlarm.getCapacityAlarm()));
+ }
+ }
+ if (checkNotifyConfig || checkNotifyAlarm) {
+ LOG.info("[{}] Dynamic thread pool notification property changes.", executorProperties.getThreadPoolId());
+ }
+ }
+
/**
* Check consistency.
*
@@ -132,15 +196,57 @@ public class DynamicThreadPoolRefreshListener implements Observer> 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(
+ SpringPropertiesLoader.inetUtils, 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..e5276104
--- /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.agent-micrometer-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/SpringEnvironmentSupport.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/SpringEnvironmentSupport.java
index 4733acdc..d4b7e619 100644
--- a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/SpringEnvironmentSupport.java
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/SpringEnvironmentSupport.java
@@ -32,6 +32,6 @@ public class SpringEnvironmentSupport {
Map map = new HashMap<>();
map.put("spring.dynamic.thread-pool.enable", false); // Switch off in non-Agent mode
MapPropertySource propertySource = new MapPropertySource("Hippo4j-Agent-Properties", map);
- environment.getPropertySources().addFirst(propertySource);
+ environment.getPropertySources().addLast(propertySource);
}
}
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 8b215ec0..907b2074 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
@@ -18,16 +18,27 @@
package cn.hippo4j.agent.plugin.spring.common.support;
import cn.hippo4j.agent.core.boot.SpringBootConfigInitializer;
+import cn.hippo4j.agent.plugin.spring.common.toolkit.SpringPropertyBinder;
+import cn.hippo4j.common.extension.design.AbstractSubjectCenter;
import cn.hippo4j.common.logging.api.ILog;
import cn.hippo4j.common.logging.api.LogManager;
+import cn.hippo4j.core.toolkit.inet.InetUtils;
+import cn.hippo4j.core.toolkit.inet.InetUtilsProperties;
+import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties;
+import lombok.Getter;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
+import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties.PREFIX;
/**
* Spring properties loader
@@ -36,6 +47,19 @@ public class SpringPropertiesLoader {
private static final ILog LOGGER = LogManager.getLogger(SpringPropertiesLoader.class);
+ /**
+ * A flag used to indicate whether loadSpringProperties() method has been called,
+ * Used to determine whether the SpringPropertiesLoader has been initialized
+ */
+ @Getter
+ private static final AtomicBoolean active = new AtomicBoolean(Boolean.FALSE);
+
+ public static BootstrapConfigProperties BOOTSTRAP_CONFIG_PROPERTIES = new BootstrapConfigProperties();
+
+ public static InetUtilsProperties INET_UTILS_PROPERTIES = new InetUtilsProperties();
+
+ public static InetUtils inetUtils;
+
public static void loadSpringProperties(ConfigurableEnvironment environment) {
Iterator> iterator = environment.getPropertySources().iterator();
Properties properties = new Properties();
@@ -43,6 +67,12 @@ public class SpringPropertiesLoader {
while (iterator.hasNext()) {
propertySourceList.add(iterator.next());
}
+ // Sort to ensure that the configuration in the configuration center is after the array
+ // To get the latest configuration information
+ propertySourceList.sort(Comparator.comparing(
+ // Make sure that Nacos boot's propertySource is placed first in the propertySourceList
+ item -> !item.getClass().getName().equals("com.alibaba.nacos.spring.core.env.NacosPropertySource")));
+
for (int i = propertySourceList.size() - 1; i >= 0; i--) {
PropertySource> propertySource = propertySourceList.get(i);
if (!(propertySource instanceof EnumerablePropertySource)) {
@@ -65,5 +95,20 @@ public class SpringPropertiesLoader {
}
}
SpringBootConfigInitializer.setSpringProperties(properties);
+ PropertiesPropertySource propertySource = new PropertiesPropertySource("customPropertySource", properties);
+ 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);
+ // Send AGENT_SPRING_PROPERTIES_LOADER_COMPLETED notification event Before active is false
+ if (AbstractSubjectCenter.get(AbstractSubjectCenter.SubjectType.AGENT_SPRING_PROPERTIES_LOADER_COMPLETED) != null && Boolean.FALSE.equals(active.get())) {
+ AbstractSubjectCenter.notify(AbstractSubjectCenter.SubjectType.AGENT_SPRING_PROPERTIES_LOADER_COMPLETED, () -> "");
+ }
+ active.set(Boolean.TRUE);
+ // 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/SpringThreadPoolRegisterSupport.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/SpringThreadPoolRegisterSupport.java
index 65a9f4cb..2af5a280 100644
--- a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/SpringThreadPoolRegisterSupport.java
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/SpringThreadPoolRegisterSupport.java
@@ -22,17 +22,30 @@ import cn.hippo4j.common.executor.ThreadPoolExecutorRegistry;
import cn.hippo4j.common.executor.support.BlockingQueueTypeEnum;
import cn.hippo4j.common.executor.support.RejectedPolicyTypeEnum;
import cn.hippo4j.common.handler.DynamicThreadPoolAdapterChoose;
+import cn.hippo4j.common.model.executor.ExecutorNotifyProperties;
import cn.hippo4j.common.model.executor.ExecutorProperties;
-import cn.hippo4j.common.toolkit.BooleanUtil;
+import cn.hippo4j.common.toolkit.StringUtil;
+import cn.hippo4j.common.toolkit.ThreadPoolExecutorUtil;
+import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties;
+import cn.hippo4j.threadpool.message.core.service.GlobalNotifyAlarmManage;
+
+import cn.hippo4j.threadpool.message.core.service.ThreadPoolNotifyAlarm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import java.lang.reflect.Field;
+import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import static cn.hippo4j.common.constant.Constants.DYNAMIC_THREAD_POOL_EXECUTOR;
/**
* Spring thread pool register support
@@ -41,6 +54,14 @@ public class SpringThreadPoolRegisterSupport {
private static final Logger LOGGER = LoggerFactory.getLogger(SpringThreadPoolRegisterSupport.class);
+ private static final int DEFAULT_ACTIVE_ALARM = 80;
+
+ private static final int DEFAULT_CAPACITY_ALARM = 80;
+
+ private static final int DEFAULT_INTERVAL = 5;
+
+ private static final String DEFAULT_RECEIVES = "";
+
public static void registerThreadPoolInstances(ApplicationContext context) {
Map> referencedClassMap = ThreadPoolExecutorRegistry.REFERENCED_CLASS_MAP;
for (Map.Entry> entry : referencedClassMap.entrySet()) {
@@ -52,7 +73,7 @@ public class SpringThreadPoolRegisterSupport {
Object value = field.get(null);
if (value == enhancedInstance) {
String threadPoolId = declaredClass.getName() + "#" + field.getName();
- register(threadPoolId, enhancedInstance);
+ register(threadPoolId, enhancedInstance, Boolean.TRUE);
break;
}
} catch (IllegalAccessException e) {
@@ -74,25 +95,147 @@ public class SpringThreadPoolRegisterSupport {
if (executor == null) {
LOGGER.warn("[Hippo4j-Agent] Thread pool is null, ignore bean registration. beanName={}, beanClass={}", beanName, bean.getClass().getName());
} else {
- register(beanName, executor);
+ register(beanName, executor, Boolean.FALSE);
}
}
LOGGER.info("[Hippo4j-Agent] Registered thread pool instances successfully.");
}
- public static void register(String threadPoolId, ThreadPoolExecutor executor) {
+ public static void register(String threadPoolId, ThreadPoolExecutor executor, Boolean isAgentScanEnhancePool) {
if (executor == null) {
return;
}
- ExecutorProperties executorProperties = ExecutorProperties.builder()
- .threadPoolId(threadPoolId)
- .corePoolSize(executor.getCorePoolSize())
- .maximumPoolSize(executor.getMaximumPoolSize())
- .allowCoreThreadTimeOut(BooleanUtil.toBoolean(String.valueOf(executor.allowsCoreThreadTimeOut())))
- .blockingQueue(BlockingQueueTypeEnum.getBlockingQueueTypeEnumByName(executor.getQueue().getClass().getSimpleName()).getName())
- .queueCapacity(executor.getQueue().remainingCapacity())
- .rejectedHandler(RejectedPolicyTypeEnum.getRejectedPolicyTypeEnumByName(executor.getRejectedExecutionHandler().getClass().getSimpleName()).getName())
- .build();
+ ExecutorProperties executorProperties = SpringPropertiesLoader.BOOTSTRAP_CONFIG_PROPERTIES.getExecutors().stream()
+ .filter(each -> Objects.equals(threadPoolId, each.getThreadPoolId()))
+ .findFirst()
+ .orElse(null);
+
+ // Determines the thread pool that is currently obtained by bean scanning
+ if (Objects.isNull(executorProperties)) {
+ if (isAgentScanEnhancePool) {
+ throw new RuntimeException(String.format("The thread pool id [%s] does not exist in the configuration.", threadPoolId));
+ } else {
+ // Thread pool that do not require enhancement are skipped by the agent
+ return;
+ }
+ }
+
+ try {
+ executorProperties = buildActualExecutorProperties(executorProperties);
+ // Replace the original configuration and refresh the thread pool
+ threadPoolParamReplace(executor, executorProperties);
+ } catch (Exception ex) {
+ LOGGER.error("[Hippo4j-Agent] Failed to initialize thread pool configuration.", ex);
+ }
+ // Build notification information entity
+ ThreadPoolNotifyAlarm threadPoolNotifyAlarm = buildThreadPoolNotifyAlarm(executorProperties);
+ GlobalNotifyAlarmManage.put(threadPoolId, threadPoolNotifyAlarm);
+
ThreadPoolExecutorRegistry.putHolder(threadPoolId, executor, executorProperties);
}
+
+ /**
+ * Thread-pool param replace.
+ *
+ * @param executor dynamic thread-pool executor
+ * @param executorProperties executor properties
+ */
+ private static void threadPoolParamReplace(ThreadPoolExecutor executor, ExecutorProperties executorProperties) {
+ BlockingQueue workQueue = BlockingQueueTypeEnum.createBlockingQueue(executorProperties.getBlockingQueue(), executorProperties.getQueueCapacity());
+ cn.hippo4j.common.toolkit.ReflectUtil.setFieldValue(executor, "workQueue", workQueue);
+ ThreadPoolExecutorUtil.safeSetPoolSize(executor, executorProperties.getCorePoolSize(), executorProperties.getMaximumPoolSize());
+ executor.setKeepAliveTime(executorProperties.getKeepAliveTime(), TimeUnit.SECONDS);
+ executor.allowCoreThreadTimeOut(executorProperties.getAllowCoreThreadTimeOut());
+ executor.setRejectedExecutionHandler(RejectedPolicyTypeEnum.createPolicy(executorProperties.getRejectedHandler()));
+ // Reflection sets the thread pool setExecuteTimeOut
+ if (DYNAMIC_THREAD_POOL_EXECUTOR.equals(executor.getClass().getName())) {
+ try {
+ Method setExecuteTimeOutMethod = executor.getClass().getMethod("setExecuteTimeOut", Long.class);
+ Long executeTimeOut = executorProperties.getExecuteTimeOut();
+ if (executeTimeOut != null) {
+ setExecuteTimeOutMethod.invoke(executor, executeTimeOut);
+ }
+ } catch (Exception e) {
+ LOGGER.error("[Hippo4j-Agent] Failed to set executeTimeOut.", e);
+ }
+ }
+ }
+
+ /**
+ * Build actual executor properties.
+ *
+ * @param executorProperties executor properties
+ * @return executor properties
+ */
+ private static ExecutorProperties buildActualExecutorProperties(ExecutorProperties executorProperties) {
+ return Optional.ofNullable(SpringPropertiesLoader.BOOTSTRAP_CONFIG_PROPERTIES.getDefaultExecutor()).map(each -> buildExecutorProperties(executorProperties)).orElse(executorProperties);
+ }
+
+ /**
+ * Build executor properties.
+ *
+ * @param executorProperties executor properties
+ * @return executor properties
+ */
+ private static ExecutorProperties buildExecutorProperties(ExecutorProperties executorProperties) {
+ BootstrapConfigProperties configProperties = SpringPropertiesLoader.BOOTSTRAP_CONFIG_PROPERTIES;
+ return ExecutorProperties.builder()
+ .corePoolSize(Optional.ofNullable(executorProperties.getCorePoolSize())
+ .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getCorePoolSize).get()))
+ .maximumPoolSize(Optional.ofNullable(executorProperties.getMaximumPoolSize())
+ .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getMaximumPoolSize).get()))
+ .allowCoreThreadTimeOut(Optional.ofNullable(executorProperties.getAllowCoreThreadTimeOut())
+ .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getAllowCoreThreadTimeOut).get()))
+ .keepAliveTime(Optional.ofNullable(executorProperties.getKeepAliveTime())
+ .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getKeepAliveTime).get()))
+ .blockingQueue(Optional.ofNullable(executorProperties.getBlockingQueue())
+ .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getBlockingQueue).get()))
+ .executeTimeOut(Optional.ofNullable(executorProperties.getExecuteTimeOut())
+ .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getExecuteTimeOut).orElse(0L)))
+ .queueCapacity(Optional.ofNullable(executorProperties.getQueueCapacity())
+ .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getQueueCapacity).get()))
+ .rejectedHandler(Optional.ofNullable(executorProperties.getRejectedHandler())
+ .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getRejectedHandler).get()))
+ .threadNamePrefix(StringUtil.isBlank(executorProperties.getThreadNamePrefix()) ? executorProperties.getThreadPoolId() : executorProperties.getThreadNamePrefix())
+ .threadPoolId(executorProperties.getThreadPoolId())
+ .alarm(Optional.ofNullable(executorProperties.getAlarm())
+ .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getAlarm).orElse(null)))
+ .activeAlarm(Optional.ofNullable(executorProperties.getActiveAlarm())
+ .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getActiveAlarm).orElse(null)))
+ .capacityAlarm(Optional.ofNullable(executorProperties.getCapacityAlarm())
+ .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getCapacityAlarm).orElse(null)))
+ .notify(Optional.ofNullable(executorProperties.getNotify())
+ .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getNotify).orElse(null)))
+ .nodes(Optional.ofNullable(executorProperties.getNodes())
+ .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getNodes).orElse(null)))
+ .build();
+ }
+
+ /**
+ * Build thread-pool notify alarm
+ *
+ * @param executorProperties executor properties
+ * @return thread-pool notify alarm
+ */
+ private static ThreadPoolNotifyAlarm buildThreadPoolNotifyAlarm(ExecutorProperties executorProperties) {
+ BootstrapConfigProperties configProperties = SpringPropertiesLoader.BOOTSTRAP_CONFIG_PROPERTIES;
+ ExecutorNotifyProperties notify = Optional.ofNullable(executorProperties).map(ExecutorProperties::getNotify).orElse(null);
+ boolean isAlarm = Optional.ofNullable(executorProperties.getAlarm())
+ .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getAlarm).orElse(true));
+ int activeAlarm = Optional.ofNullable(executorProperties.getActiveAlarm())
+ .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getActiveAlarm).orElse(DEFAULT_ACTIVE_ALARM));
+ int capacityAlarm = Optional.ofNullable(executorProperties.getCapacityAlarm())
+ .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getCapacityAlarm).orElse(DEFAULT_CAPACITY_ALARM));
+ int interval = Optional.ofNullable(notify)
+ .map(ExecutorNotifyProperties::getInterval)
+ .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getNotify).map(ExecutorNotifyProperties::getInterval).orElse(DEFAULT_INTERVAL));
+ String receive = Optional.ofNullable(notify)
+ .map(ExecutorNotifyProperties::getReceives)
+ .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getNotify).map(ExecutorNotifyProperties::getReceives).orElse(DEFAULT_RECEIVES));
+ ThreadPoolNotifyAlarm threadPoolNotifyAlarm = new ThreadPoolNotifyAlarm(isAlarm, activeAlarm, capacityAlarm);
+ threadPoolNotifyAlarm.setInterval(interval);
+ threadPoolNotifyAlarm.setReceives(receive);
+ return threadPoolNotifyAlarm;
+ }
+
}
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolCheckAlarmSupport.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolCheckAlarmSupport.java
new file mode 100644
index 00000000..955f3bdb
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolCheckAlarmSupport.java
@@ -0,0 +1,165 @@
+/*
+ * 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.alarm.AgentModeNotifyConfigBuilder;
+import cn.hippo4j.agent.plugin.spring.common.conf.SpringBootConfig;
+import cn.hippo4j.agent.plugin.spring.common.toolkit.SpringPropertyBinder;
+import cn.hippo4j.common.propertie.EnvironmentProperties;
+import cn.hippo4j.core.config.ApplicationContextHolder;
+import cn.hippo4j.core.toolkit.IdentifyUtil;
+import cn.hippo4j.core.toolkit.inet.InetUtils;
+import cn.hippo4j.core.toolkit.inet.InetUtilsProperties;
+import cn.hippo4j.threadpool.alarm.handler.DefaultThreadPoolCheckAlarmHandler;
+import cn.hippo4j.threadpool.message.api.NotifyConfigDTO;
+import cn.hippo4j.threadpool.message.core.platform.DingSendMessageHandler;
+import cn.hippo4j.threadpool.message.core.platform.LarkSendMessageHandler;
+import cn.hippo4j.threadpool.message.core.platform.WeChatSendMessageHandler;
+import cn.hippo4j.threadpool.message.core.service.AlarmControlHandler;
+import cn.hippo4j.threadpool.message.core.service.DefaultThreadPoolConfigChangeHandler;
+import cn.hippo4j.threadpool.message.core.service.SendMessageHandler;
+import cn.hippo4j.threadpool.message.core.service.ThreadPoolBaseSendMessageService;
+import lombok.Getter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.env.ConfigurableEnvironment;
+
+import java.util.List;
+import java.util.Map;
+
+import static cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader.BOOTSTRAP_CONFIG_PROPERTIES;
+
+/**
+ * The {@code ThreadPoolCheckAlarmSupport} class provides functionality to enable and configure
+ * a thread pool check alarm handler. This is typically used to monitor thread pools for potential
+ * issues and send notifications based on the configured alert mechanisms.
+ */
+public class ThreadPoolCheckAlarmSupport {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolCheckAlarmSupport.class);
+
+ @Getter
+ private static ThreadPoolBaseSendMessageService threadPoolBaseSendMessageService;
+
+ @Getter
+ private static DefaultThreadPoolConfigChangeHandler threadPoolConfigChangeHandler;
+
+ @Getter
+ private static AgentModeNotifyConfigBuilder agentNotifyConfigBuilder;
+
+ private static DefaultThreadPoolCheckAlarmHandler checkAlarmHandler;
+
+ /**
+ * Enables the thread pool check alarm handler if the corresponding configuration property is set to {@code true}.
+ *
+ * This method performs the following actions:
+ *
+ *
Checks the value of the {@code enable} property in the bootstrap configuration. If it is {@code true}, it proceeds.
+ *
Initializes environment properties needed for the monitoring process.
+ *
Creates an instance of {@link AlarmControlHandler} and {@link ThreadPoolBaseSendMessageService} with necessary dependencies.
+ *
Initializes and registers message handlers and notification configurations.
+ *
Creates an instance of {@link DefaultThreadPoolCheckAlarmHandler} and schedules it to start monitoring the thread pool.
+ *
+ */
+ public static void enableThreadPoolCheckAlarmHandler() {
+ // Check if the thread pool checker is enabled in the bootstrap configuration properties
+ if (Boolean.TRUE.equals(BOOTSTRAP_CONFIG_PROPERTIES.getEnable())) {
+
+ // Initialize EnvironmentProperties
+ initializeEnvironmentProperties();
+
+ // Initialize the AlarmControlHandler and ThreadPoolBaseSendMessageService
+ AlarmControlHandler alarmControlHandler = new AlarmControlHandler();
+ threadPoolBaseSendMessageService = createThreadPoolBaseSendMessageService(alarmControlHandler);
+ threadPoolConfigChangeHandler = new DefaultThreadPoolConfigChangeHandler(threadPoolBaseSendMessageService);
+
+ // Initialize the alarm platform information
+ initializeSendMessageHandlers(threadPoolBaseSendMessageService, alarmControlHandler);
+
+ // Execute scheduled task to check an alarm
+ scheduleExecute(threadPoolBaseSendMessageService);
+ }
+ }
+
+ /**
+ * Initializes environment properties used for thread pool monitoring.
+ *
+ * This method sets the state check interval, item ID, application name, and active profile from the bootstrap configuration.
+ */
+ private static void initializeEnvironmentProperties() {
+ EnvironmentProperties.checkStateInterval = Long.valueOf(BOOTSTRAP_CONFIG_PROPERTIES.getCheckStateInterval());
+ EnvironmentProperties.itemId = BOOTSTRAP_CONFIG_PROPERTIES.getItemId();
+ EnvironmentProperties.applicationName = SpringBootConfig.Spring.Application.name;
+ EnvironmentProperties.active = SpringBootConfig.Spring.Profiles.active;
+ ConfigurableEnvironment environment = ApplicationContextHolder.getBean(ConfigurableEnvironment.class);
+ InetUtilsProperties inetUtilsProperties = SpringPropertyBinder.bindProperties(environment, InetUtilsProperties.PREFIX, InetUtilsProperties.class);
+ SpringPropertiesLoader.inetUtils = new InetUtils(inetUtilsProperties);
+ IdentifyUtil.generate(environment, SpringPropertiesLoader.inetUtils);
+ }
+
+ /**
+ * Creates and returns a new instance of {@link ThreadPoolBaseSendMessageService} with the specified {@link AlarmControlHandler}.
+ *
+ * @param alarmControlHandler The {@link AlarmControlHandler} used to control and handle alarms.
+ * @return A new instance of {@link ThreadPoolBaseSendMessageService}.
+ */
+ private static ThreadPoolBaseSendMessageService createThreadPoolBaseSendMessageService(AlarmControlHandler alarmControlHandler) {
+ return new ThreadPoolBaseSendMessageService(alarmControlHandler);
+ }
+
+ /**
+ * Initializes and registers the message handlers and notification configurations in the specified
+ * {@link ThreadPoolBaseSendMessageService}.
+ *
+ * This method creates instances of various {@link SendMessageHandler} implementations and registers them.
+ * It also constructs and registers notification configurations using the {@link AgentModeNotifyConfigBuilder}.
+ *
+ * @param threadPoolBaseSendMessageService The {@link ThreadPoolBaseSendMessageService} in which message handlers and notification configurations will be registered.
+ * @param alarmControlHandler The {@link AlarmControlHandler} used to handle alarms and notifications.
+ */
+ private static void initializeSendMessageHandlers(ThreadPoolBaseSendMessageService threadPoolBaseSendMessageService, AlarmControlHandler alarmControlHandler) {
+ // Initialize message handlers
+ DingSendMessageHandler dingSendMessageHandler = new DingSendMessageHandler();
+ WeChatSendMessageHandler weChatSendMessageHandler = new WeChatSendMessageHandler();
+ LarkSendMessageHandler larkSendMessageHandler = new LarkSendMessageHandler();
+
+ // Register message handlers
+ threadPoolBaseSendMessageService.getSendMessageHandlers().put(dingSendMessageHandler.getType(), dingSendMessageHandler);
+ threadPoolBaseSendMessageService.getSendMessageHandlers().put(weChatSendMessageHandler.getType(), weChatSendMessageHandler);
+ threadPoolBaseSendMessageService.getSendMessageHandlers().put(larkSendMessageHandler.getType(), larkSendMessageHandler);
+
+ // Construct and register notification configurations
+ // TODO : register notify config for web , null Can be replaced with tomcat, jetty, undertow, etc. implementation classes
+ agentNotifyConfigBuilder = new AgentModeNotifyConfigBuilder(alarmControlHandler, null);
+ Map> notifyConfigs = agentNotifyConfigBuilder.buildNotify();
+ threadPoolBaseSendMessageService.getNotifyConfigs().putAll(notifyConfigs);
+ }
+
+ // 启动或重新启动检查任务
+ public static void scheduleExecute(ThreadPoolBaseSendMessageService threadPoolBaseSendMessageService) {
+ // If a task is already running, cancel it first
+ if (checkAlarmHandler != null) {
+ // Shut down the thread pool and prepare to regenerate the listener thread pool
+ checkAlarmHandler.destroyScheduleExecute();
+ }
+ // Initialize the thread pool check alarm handler with necessary services
+ checkAlarmHandler = new DefaultThreadPoolCheckAlarmHandler(threadPoolBaseSendMessageService);
+ // Run the check alarm handler to start monitoring the thread pool
+ checkAlarmHandler.scheduleExecute();
+ }
+}
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..a45c4fa9
--- /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,142 @@
+/*
+ * 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.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 lombok.Getter;
+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.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+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);
+
+ /**
+ * A flag used to indicate whether enableThreadPoolMonitorHandler() method has been called,
+ * Used to determine whether the ThreadPoolMonitorHandler has been enable
+ */
+ @Getter
+ private static final AtomicBoolean active = new AtomicBoolean(Boolean.FALSE);
+
+ private static final ScheduledExecutorService collectScheduledExecutor = new ScheduledThreadPoolExecutor(1, r -> new Thread(r, "client.agent.scheduled.collect.data"));
+
+ private static final List threadPoolMonitors = new ArrayList<>();
+
+ 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.");
+
+ // Initialize monitoring components for the dynamic thread pools
+ MonitorHandlersConfigurator.initializeMonitorHandlers(monitor, (ConfigurableEnvironment) environment, threadPoolMonitors);
+
+ // Determine whether the task is successfully enabled
+ // return directly if it has been enabled, and do not start the thread pool repeatedly
+ if (Boolean.TRUE.equals(active.get()))
+ return;
+
+ // 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
+ Runnable scheduledTask = scheduleRunnable();
+ collectScheduledExecutor.scheduleWithFixedDelay(scheduledTask, monitor.getInitialDelay(), monitor.getCollectInterval(), TimeUnit.MILLISECONDS);
+
+ active.set(true);
+ 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 (Throwable ex) {
+ LOGGER.error("[Hippo4j-Agent] Error monitoring the running status of dynamic thread pool. Type: {}", each.getType(), ex);
+ }
+ }
+ };
+ }
+
+}
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/toolkit/SpringPropertyBinder.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/toolkit/SpringPropertyBinder.java
new file mode 100644
index 00000000..22a6bc62
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/toolkit/SpringPropertyBinder.java
@@ -0,0 +1,241 @@
+/*
+ * 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.toolkit;
+
+import cn.hippo4j.threadpool.dynamic.mode.config.parser.ConfigFileTypeEnum;
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.BeanWrapperImpl;
+import org.springframework.core.env.Environment;
+import org.springframework.core.env.PropertySource;
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.env.MapPropertySource;
+
+import java.beans.PropertyEditorSupport;
+import java.util.*;
+
+/**
+ * CustomPropertyBinder is a utility class for binding properties from Spring's Environment
+ * to Java objects based on a given prefix. This is useful for dynamically binding configurations
+ * in Spring applications where configuration properties are organized hierarchically and need to be
+ * mapped to corresponding Java objects.
+ *
+ *
This class handles complex property structures, including nested properties and collections,
+ * ensuring that all properties prefixed with the specified string are correctly bound to the target
+ * Java object.
+ */
+public class SpringPropertyBinder {
+
+ /**
+ * Binds properties from the Spring Environment to an instance of the specified configuration class.
+ *
+ * @param environment the Spring Environment containing property sources.
+ * @param prefix the prefix to filter properties for binding (e.g., "spring.dynamic.thread-pool").
+ * @param clazz the class type of the configuration object to bind properties to.
+ * @param the type of the configuration class.
+ * @return an instance of the configuration class with properties bound from the environment.
+ * @throws RuntimeException if there is an error instantiating the configuration class or binding properties.
+ */
+ public static T bindProperties(Environment environment, String prefix, Class clazz) {
+ try {
+ // Create an instance of the target class
+ T instance = clazz.getDeclaredConstructor().newInstance();
+ BeanWrapper beanWrapper = new BeanWrapperImpl(instance);
+
+ // Register custom editor for ConfigFileTypeEnum to handle specific type conversions
+ beanWrapper.registerCustomEditor(ConfigFileTypeEnum.class, new ConfigFileTypeEnumEditor());
+
+ // Iterate over all property keys that match the given prefix
+ for (String key : getAllPropertyKeys(environment, prefix)) {
+ String propertyName = key.substring(prefix.length() + 1); // Remove prefix from the property key
+ String[] tokens = propertyName.split("\\."); // Split the property name by dot for nested properties
+ setPropertyValue(tokens, beanWrapper, environment.getProperty(key)); // Set the property value recursively
+ }
+
+ return instance;
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to bind properties to " + clazz.getName(), e);
+ }
+ }
+
+ /**
+ * Binds properties from a map to an instance of the specified configuration class.
+ *
+ * @param configInfo a map containing property paths and their values.
+ * @param prefix the prefix to filter properties for binding (e.g., "spring.dynamic.thread-pool").
+ * @param clazz the class type of the configuration object to bind properties to.
+ * @param the type of the configuration class.
+ * @return an instance of the configuration class with properties bound from the configInfo map.
+ */
+ public static T bindProperties(Map configInfo, String prefix, Class clazz) {
+ try {
+ // Create an instance of the target class
+ T instance = clazz.getDeclaredConstructor().newInstance();
+ BeanWrapper beanWrapper = new BeanWrapperImpl(instance);
+
+ // Register custom editor for specific type conversions (if needed)
+ beanWrapper.registerCustomEditor(ConfigFileTypeEnum.class, new ConfigFileTypeEnumEditor());
+
+ // Iterate over all property keys that match the given prefix in the configInfo map
+ for (Map.Entry entry : configInfo.entrySet()) {
+ String key = entry.getKey().toString();
+ if (key.startsWith(prefix)) {
+ String propertyName = key.substring(prefix.length() + 1); // Remove prefix from the property key
+ String[] tokens = propertyName.split("\\."); // Split the property name by dot for nested properties
+ setPropertyValue(tokens, beanWrapper, entry.getValue().toString()); // Set the property value recursively
+ }
+ }
+ return instance;
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to bind properties to " + clazz.getName(), e);
+ }
+ }
+
+ /**
+ * Recursively sets property values on the target object, handling nested properties and collections.
+ *
+ * @param tokens an array of property path tokens (e.g., ["nested", "property", "name"]).
+ * @param beanWrapper the BeanWrapper instance used to manipulate the target object.
+ * @param value the value to set on the target property.
+ */
+ private static void setPropertyValue(String[] tokens, BeanWrapper beanWrapper, String value) {
+ for (int i = 0; i < tokens.length - 1; i++) {
+ String token = tokens[i];
+
+ if (token.matches(".*\\[\\d+\\]$")) { // Handle array/list property
+ token = token.substring(0, token.indexOf('['));
+ int index = Integer.parseInt(tokens[i].substring(token.length() + 1, tokens[i].length() - 1));
+
+ token = convertToCamelCase(token); // Convert token to camelCase if necessary
+ List list = (List) beanWrapper.getPropertyValue(token);
+ if (list == null) {
+ list = new ArrayList<>();
+ beanWrapper.setPropertyValue(convertToCamelCase(token), list); // Initialize the list if it's null
+ }
+
+ // Ensure the list has enough size to accommodate the index
+ if (list.size() <= index) {
+ try {
+ // Instantiate the list element if it does not exist
+ list.add(index, beanWrapper.getPropertyTypeDescriptor(token)
+ .getElementTypeDescriptor().getType().newInstance());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ // Move the beanWrapper context to the current list element
+ beanWrapper = new BeanWrapperImpl(list.get(index));
+ } else { // Handle simple or nested property
+ Object nestedObject = beanWrapper.getPropertyValue(token);
+ if (nestedObject == null) {
+ Class> nestedClass = beanWrapper.getPropertyType(token);
+ if (Map.class.isAssignableFrom(nestedClass)) {
+ nestedObject = new HashMap<>(); // Initialize nested Map if necessary
+ } else {
+ try {
+ nestedObject = nestedClass.getDeclaredConstructor().newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ beanWrapper.setPropertyValue(convertToCamelCase(token), nestedObject);
+ }
+ // Move the beanWrapper context to the nested object
+ beanWrapper = new BeanWrapperImpl(nestedObject);
+ }
+ }
+
+ // Finally, set the actual property value on the resolved object
+ String finalPropertyName = tokens[tokens.length - 1];
+ Object currentObject = beanWrapper.getWrappedInstance();
+ if (currentObject instanceof Map) {
+ // If the current object is a Map, set the value as a key-value pair
+ ((Map) currentObject).put(finalPropertyName, value);
+ } else {
+ // Otherwise, set it as a simple property
+ beanWrapper.setPropertyValue(convertToCamelCase(finalPropertyName), value);
+ }
+ }
+
+ /**
+ * Retrieves all property keys from the environment that start with the given prefix.
+ *
+ * @param environment the Spring Environment containing property sources.
+ * @param prefix the prefix to filter property keys.
+ * @return a set of property keys that match the prefix.
+ */
+ private static Set getAllPropertyKeys(Environment environment, String prefix) {
+ Set keys = new HashSet<>();
+ // Iterate through all property sources in the environment
+ for (PropertySource> propertySource : ((ConfigurableEnvironment) environment).getPropertySources()) {
+ if (propertySource instanceof MapPropertySource) {
+ Map source = ((MapPropertySource) propertySource).getSource();
+ // Collect keys that start with the specified prefix
+ for (String key : source.keySet()) {
+ if (key.startsWith(prefix)) {
+ keys.add(key);
+ }
+ }
+ }
+ }
+ return keys;
+ }
+
+ /**
+ * Converts a dashed-separated string to camelCase.
+ *
+ * For example, "my-property-name" -> "myPropertyName".
+ *
+ * @param dashed the dashed-separated string to be converted.
+ * @return the camelCase representation of the input string.
+ */
+ private static String convertToCamelCase(String dashed) {
+ String[] parts = dashed.split("-");
+ return Arrays.stream(parts)
+ .map(part -> part.substring(0, 1).toUpperCase() + part.substring(1)) // Capitalize each part
+ .reduce((first, second) -> first + second) // Concatenate all parts together
+ .map(result -> result.substring(0, 1).toLowerCase() + result.substring(1)) // Lowercase the first letter
+ .orElse(dashed);
+ }
+
+ /**
+ * ConfigFileTypeEnumEditor is a custom property editor for converting string representations
+ * of {@link ConfigFileTypeEnum} into the corresponding enum instances.
+ *
+ * This editor is useful in scenarios where properties are read as strings but need to be
+ * converted to enum types for further processing.
+ */
+ public static class ConfigFileTypeEnumEditor extends PropertyEditorSupport {
+
+ /**
+ * Converts the given text value to the corresponding {@link ConfigFileTypeEnum} instance.
+ *
+ * This method overrides the default implementation to parse the input string and convert
+ * it into a {@link ConfigFileTypeEnum}. If the input string does not match any known enum
+ * value, an {@link IllegalArgumentException} will be thrown.
+ *
+ * @param text the string representation of the enum to be converted.
+ * @throws IllegalArgumentException if the text does not match any known enum value.
+ */
+ @Override
+ public void setAsText(String text) throws IllegalArgumentException {
+ setValue(ConfigFileTypeEnum.of(text));
+ }
+ }
+
+}
diff --git a/agent/hippo4j-agent-plugin/threadpool-plugin/pom.xml b/agent/hippo4j-agent-plugin/threadpool-plugin/pom.xml
index b571302e..16517f4c 100644
--- a/agent/hippo4j-agent-plugin/threadpool-plugin/pom.xml
+++ b/agent/hippo4j-agent-plugin/threadpool-plugin/pom.xml
@@ -16,7 +16,6 @@
cn.hippo4jhippo4j-threadpool-core${project.version}
- providedcn.hippo4j
diff --git a/agent/hippo4j-agent-plugin/threadpool-plugin/src/main/java/cn/hippo4j/agent/plugin/thread/pool/interceptor/ThreadPoolExecutorConstructorMethodInterceptor.java b/agent/hippo4j-agent-plugin/threadpool-plugin/src/main/java/cn/hippo4j/agent/plugin/thread/pool/interceptor/ThreadPoolExecutorConstructorMethodInterceptor.java
index fa4424a8..db1019d4 100644
--- a/agent/hippo4j-agent-plugin/threadpool-plugin/src/main/java/cn/hippo4j/agent/plugin/thread/pool/interceptor/ThreadPoolExecutorConstructorMethodInterceptor.java
+++ b/agent/hippo4j-agent-plugin/threadpool-plugin/src/main/java/cn/hippo4j/agent/plugin/thread/pool/interceptor/ThreadPoolExecutorConstructorMethodInterceptor.java
@@ -52,7 +52,16 @@ public class ThreadPoolExecutorConstructorMethodInterceptor implements InstanceC
}
StackTraceElement declaredClassStackTraceElement = stackTraceElements.get(0);
String declaredClassName = declaredClassStackTraceElement.getClassName();
- Class> declaredClass = Thread.currentThread().getContextClassLoader().loadClass(declaredClassName);
+ Class> declaredClass = null;
+ try {
+ declaredClass = Thread.currentThread().getContextClassLoader().loadClass(declaredClassName);
+ } catch (ClassNotFoundException e) {
+ // The thread pool in the Agent plug-in is loaded by AgentclassLodaer.
+ // Due to the delegation model, it can only be searched upwards, so searching here will result in ClassNotFount.
+ // Because the parent of AgentClassLoader is AppclassLoder, it is ignored here ,skip the enhancement logic
+ LOGGER.debug("searching {} result in ClassNotFount , so skip the enhancement logic", declaredClassName);
+ return;
+ }
ThreadPoolExecutorRegistry.REFERENCED_CLASS_MAP.put((ThreadPoolExecutor) objInst, declaredClass);
}
diff --git a/agent/pom.xml b/agent/pom.xml
index ac0db601..18de9269 100644
--- a/agent/pom.xml
+++ b/agent/pom.xml
@@ -140,7 +140,6 @@
org.objenesisobjenesis${objenesis.version}
- testcom.github.tomakehurst
diff --git a/examples/threadpool-example/agent/agent-example-core/pom.xml b/examples/threadpool-example/agent/agent-example-core/pom.xml
new file mode 100644
index 00000000..3feb4a56
--- /dev/null
+++ b/examples/threadpool-example/agent/agent-example-core/pom.xml
@@ -0,0 +1,43 @@
+
+
+ 4.0.0
+
+ cn.hippo4j
+ hippo4j-threadpool-agent-example
+ 2.0.0-SNAPSHOT
+
+
+ hippo4j-agent-example-core
+
+
+ true
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ cn.hippo4j
+ hippo4j-threadpool-message
+ ${revision}
+
+
+ org.openjdk.jmh
+ jmh-core
+ 1.23
+ test
+
+
+ org.openjdk.jmh
+ jmh-generator-annprocess
+ 1.23
+ test
+
+
+
+
\ No newline at end of file
diff --git a/examples/threadpool-example/agent/config-apollo/src/main/java/cn/hippo4j/example/agent/config/apollo/ThreadPoolConfiguration.java b/examples/threadpool-example/agent/agent-example-core/src/main/java/cn/hippo4j/example/agent/core/config/ThreadPoolConfiguration.java
similarity index 92%
rename from examples/threadpool-example/agent/config-apollo/src/main/java/cn/hippo4j/example/agent/config/apollo/ThreadPoolConfiguration.java
rename to examples/threadpool-example/agent/agent-example-core/src/main/java/cn/hippo4j/example/agent/core/config/ThreadPoolConfiguration.java
index cec7c3bb..77182468 100644
--- a/examples/threadpool-example/agent/config-apollo/src/main/java/cn/hippo4j/example/agent/config/apollo/ThreadPoolConfiguration.java
+++ b/examples/threadpool-example/agent/agent-example-core/src/main/java/cn/hippo4j/example/agent/core/config/ThreadPoolConfiguration.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package cn.hippo4j.example.agent.config.apollo;
+package cn.hippo4j.example.agent.core.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -46,7 +46,7 @@ public class ThreadPoolConfiguration {
// 演示 Agent 模式修改线程池
// -------------------------------------------------------------------------
- public static final ThreadPoolExecutor RUN_MESSAGE_SEND_TASK_EXECUTOR = new ThreadPoolExecutor(
+ public static final ThreadPoolExecutor AGENT_RUN_MESSAGE_SEND_TASK_EXECUTOR = new ThreadPoolExecutor(
1,
10,
1024,
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
new file mode 100644
index 00000000..bf67eb75
--- /dev/null
+++ b/examples/threadpool-example/agent/agent-example-core/src/main/java/cn/hippo4j/example/agent/core/inittest/AlarmSendMessageTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.example.agent.core.inittest;
+
+import cn.hippo4j.common.executor.ThreadPoolExecutorHolder;
+import cn.hippo4j.common.executor.ThreadPoolExecutorRegistry;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test alarm send message.
+ */
+@Slf4j
+@Component
+public class AlarmSendMessageTest {
+
+ private static final int SLEEP_TIME = 10240124;
+
+ private static final int INITIAL_DELAY = 3;
+
+ private static final String RUN_MESSAGE_SEND_TASK_EXECUTOR = "runMessageSendTaskExecutor";
+
+ private static final String AGENT_RUN_MESSAGE_SEND_TASK_EXECUTOR = "cn.hippo4j.example.agent.core.config.ThreadPoolConfiguration#AGENT_RUN_MESSAGE_SEND_TASK_EXECUTOR";
+
+ /**
+ * Test alarm notification.
+ * If you need to run this single test, add @PostConstruct to the method.
+ */
+ @SuppressWarnings("all")
+ // @PostConstruct
+ public void alarmSendMessageTest() {
+ ScheduledExecutorService scheduledThreadPool = Executors.newSingleThreadScheduledExecutor();
+ scheduledThreadPool.scheduleWithFixedDelay(() -> {
+ ThreadPoolExecutorHolder executorHolder = ThreadPoolExecutorRegistry.getHolder(AGENT_RUN_MESSAGE_SEND_TASK_EXECUTOR);
+ ThreadPoolExecutor poolExecutor = executorHolder.getExecutor();
+ try {
+ poolExecutor.execute(() -> {
+ try {
+ Thread.sleep(SLEEP_TIME);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ } catch (Exception ex) {
+ log.error("Throw reject policy.", ex.getMessage());
+ }
+ }, INITIAL_DELAY, 2, TimeUnit.SECONDS);
+
+ scheduledThreadPool.scheduleWithFixedDelay(() -> {
+ ThreadPoolExecutorHolder executorHolder = ThreadPoolExecutorRegistry.getHolder(RUN_MESSAGE_SEND_TASK_EXECUTOR);
+ ThreadPoolExecutor poolExecutor = executorHolder.getExecutor();
+ try {
+ poolExecutor.execute(() -> {
+ try {
+ Thread.sleep(SLEEP_TIME);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ } catch (Exception ex) {
+ log.error("Throw reject policy.", ex.getMessage());
+ }
+ }, INITIAL_DELAY, 2, TimeUnit.SECONDS);
+
+ }
+}
diff --git a/examples/threadpool-example/agent/config-apollo-spring-boot-1x/pom.xml b/examples/threadpool-example/agent/config-apollo-spring-boot-1x/pom.xml
new file mode 100644
index 00000000..6277fad0
--- /dev/null
+++ b/examples/threadpool-example/agent/config-apollo-spring-boot-1x/pom.xml
@@ -0,0 +1,83 @@
+
+
+ 4.0.0
+
+ cn.hippo4j
+ hippo4j-threadpool-agent-example
+ 2.0.0-SNAPSHOT
+
+
+ hippo4j-threadpool-agent-config-apollo-spring-boot-1x
+
+
+ true
+ 1.5.22.RELEASE
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ cn.hippo4j
+ hippo4j-agent-example-core
+ ${revision}
+
+
+ com.ctrip.framework.apollo
+ apollo-client
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.21
+
+
+ org.springframework.cloud
+ spring-cloud-context
+ 1.3.6.RELEASE
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot.version}
+ pom
+ import
+
+
+
+
+
+ ${project.artifactId}
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/threadpool-example/agent/config-apollo-spring-boot-1x/src/main/java/cn/hippo4j/example/agent/config/apollo/v1/AgentConfigApolloSpringBoot1xExampleApplication.java b/examples/threadpool-example/agent/config-apollo-spring-boot-1x/src/main/java/cn/hippo4j/example/agent/config/apollo/v1/AgentConfigApolloSpringBoot1xExampleApplication.java
new file mode 100644
index 00000000..26896afd
--- /dev/null
+++ b/examples/threadpool-example/agent/config-apollo-spring-boot-1x/src/main/java/cn/hippo4j/example/agent/config/apollo/v1/AgentConfigApolloSpringBoot1xExampleApplication.java
@@ -0,0 +1,32 @@
+/*
+ * 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.example.agent.config.apollo.v1;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * Agent config Nacos example application.
+ */
+@SpringBootApplication(scanBasePackages = "cn.hippo4j.example.agent.core")
+public class AgentConfigApolloSpringBoot1xExampleApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(AgentConfigApolloSpringBoot1xExampleApplication.class, args);
+ }
+}
diff --git a/examples/threadpool-example/agent/config-apollo-spring-boot-1x/src/main/resources/bootstrap.properties b/examples/threadpool-example/agent/config-apollo-spring-boot-1x/src/main/resources/bootstrap.properties
new file mode 100644
index 00000000..456739df
--- /dev/null
+++ b/examples/threadpool-example/agent/config-apollo-spring-boot-1x/src/main/resources/bootstrap.properties
@@ -0,0 +1,65 @@
+server.port=8092
+server.servlet.context-path=/example
+
+app.id=dynamic-threadpool-example
+apollo.meta=http://127.0.0.1:8080
+apollo.autoUpdateInjectedSpringProperties=true
+apollo.bootstrap.enabled=true
+apollo.bootstrap.namespaces=application
+apollo.bootstrap.eagerLoad.enabled=true
+
+# The following parameters are used for testing
+env=dev
+apollo.configService=http://127.0.0.1:8080
+spring.profiles.active=dev
+spring.application.name=hippo4j-config-apollo-spring-boot-starter-example
+
+spring.dynamic.thread-pool.enable=true
+spring.dynamic.thread-pool.banner=true
+spring.dynamic.thread-pool.check-state-interval=10
+spring.dynamic.thread-pool.monitor.enable=true
+spring.dynamic.thread-pool.monitor.collect-types=micrometer
+spring.dynamic.thread-pool.monitor.thread-pool-types=dynamic
+spring.dynamic.thread-pool.monitor.initial-delay=3000
+spring.dynamic.thread-pool.monitor.collect-interval=3000
+spring.dynamic.thread-pool.monitor.agent-micrometer-port=29999
+
+spring.dynamic.thread-pool.notify-platforms[0].platform=LARK
+spring.dynamic.thread-pool.notify-platforms[0].token=6de41bdc-0799-45be-b128-7cddb9e777f0
+#spring.dynamic.thread-pool.notify-platforms[1].platform=WECHAT
+#spring.dynamic.thread-pool.notify-platforms[1].token=ac0426a5-c712-474c-9bff-72b8b8f5caff
+#spring.dynamic.thread-pool.notify-platforms[2].platform=DING
+#spring.dynamic.thread-pool.notify-platforms[2].token=56417ebba6a27ca352f0de77a2ae9da66d01f39610b5ee8a6033c60ef9071c55
+
+spring.dynamic.thread-pool.apollo.namespace=application
+spring.dynamic.thread-pool.config-file-type=properties
+
+spring.dynamic.thread-pool.executors[0].thread-name-prefix = DynamicThreadPoolConfig#FIELD1
+spring.dynamic.thread-pool.executors[0].core-pool-size = 2
+spring.dynamic.thread-pool.executors[0].thread-pool-id = cn.hippo4j.example.agent.core.config.ThreadPoolConfiguration#AGENT_RUN_MESSAGE_SEND_TASK_EXECUTOR
+spring.dynamic.thread-pool.executors[0].maximum-pool-size = 20
+spring.dynamic.thread-pool.executors[0].queue-capacity = 1024
+spring.dynamic.thread-pool.executors[0].blocking-queue = ResizableCapacityLinkedBlockingQueue
+spring.dynamic.thread-pool.executors[0].execute-time-out = 800
+spring.dynamic.thread-pool.executors[0].rejected-handler = AbortPolicy
+spring.dynamic.thread-pool.executors[0].keep-alive-time = 6691
+spring.dynamic.thread-pool.executors[0].allow-core-thread-time-out = true
+spring.dynamic.thread-pool.executors[0].alarm = true
+spring.dynamic.thread-pool.executors[0].active-alarm = 80
+spring.dynamic.thread-pool.executors[0].capacity-alarm = 80
+spring.dynamic.thread-pool.executors[0].notify.interval = 8
+spring.dynamic.thread-pool.executors[0].notify.receives = nobodyiam
+spring.dynamic.thread-pool.executors[1].thread-pool-id = runMessageSendTaskExecutor
+spring.dynamic.thread-pool.executors[1].thread-name-prefix = runMessageSendTaskExecutor
+spring.dynamic.thread-pool.executors[1].core-pool-size = 3
+spring.dynamic.thread-pool.executors[1].maximum-pool-size = 4
+spring.dynamic.thread-pool.executors[1].queue-capacity = 1024
+spring.dynamic.thread-pool.executors[1].blocking-queue = ResizableCapacityLinkedBlockingQueue
+spring.dynamic.thread-pool.executors[1].execute-time-out = 800
+spring.dynamic.thread-pool.executors[1].rejected-handler = AbortPolicy
+spring.dynamic.thread-pool.executors[1].keep-alive-time = 6691
+spring.dynamic.thread-pool.executors[1].allow-core-thread-time-out = true
+spring.dynamic.thread-pool.executors[1].active-alarm = 80
+spring.dynamic.thread-pool.executors[1].capacity-alarm = 80
+spring.dynamic.thread-pool.executors[1].notify.interval = 8
+spring.dynamic.thread-pool.executors[1].notify.receives = nobodyiam
diff --git a/examples/threadpool-example/agent/config-apollo/pom.xml b/examples/threadpool-example/agent/config-apollo/pom.xml
index 7a7adfd4..f87d6ddf 100644
--- a/examples/threadpool-example/agent/config-apollo/pom.xml
+++ b/examples/threadpool-example/agent/config-apollo/pom.xml
@@ -16,6 +16,12 @@
+
+ cn.hippo4j
+ hippo4j-agent-example-core
+ ${revision}
+
+
org.springframework.bootspring-boot-starter
diff --git a/examples/threadpool-example/agent/config-apollo/src/main/java/cn/hippo4j/example/agent/config/apollo/AgentConfigApolloExampleApplication.java b/examples/threadpool-example/agent/config-apollo/src/main/java/cn/hippo4j/example/agent/config/apollo/AgentConfigApolloExampleApplication.java
index 33283990..365282b7 100644
--- a/examples/threadpool-example/agent/config-apollo/src/main/java/cn/hippo4j/example/agent/config/apollo/AgentConfigApolloExampleApplication.java
+++ b/examples/threadpool-example/agent/config-apollo/src/main/java/cn/hippo4j/example/agent/config/apollo/AgentConfigApolloExampleApplication.java
@@ -23,7 +23,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Agent config apollo example application.
*/
-@SpringBootApplication
+@SpringBootApplication(scanBasePackages = "cn.hippo4j.example.agent.core")
public class AgentConfigApolloExampleApplication {
public static void main(String[] args) {
diff --git a/examples/threadpool-example/agent/config-apollo/src/main/resources/bootstrap.properties b/examples/threadpool-example/agent/config-apollo/src/main/resources/bootstrap.properties
index d467930f..5192a761 100644
--- a/examples/threadpool-example/agent/config-apollo/src/main/resources/bootstrap.properties
+++ b/examples/threadpool-example/agent/config-apollo/src/main/resources/bootstrap.properties
@@ -1,39 +1,42 @@
server.port=8092
server.servlet.context-path=/example
+
app.id=dynamic-threadpool-example
apollo.meta=http://127.0.0.1:8080
apollo.autoUpdateInjectedSpringProperties=true
apollo.bootstrap.enabled=true
apollo.bootstrap.namespaces=application
apollo.bootstrap.eagerLoad.enabled=true
+
# The following parameters are used for testing
env=dev
apollo.configService=http://127.0.0.1:8080
spring.profiles.active=dev
spring.application.name=hippo4j-config-apollo-spring-boot-starter-example
-management.metrics.export.prometheus.enabled=true
-management.server.port=29998
-management.endpoints.web.exposure.include=*
+
spring.dynamic.thread-pool.enable=true
spring.dynamic.thread-pool.banner=true
-spring.dynamic.thread-pool.check-state-interval=3
-#spring.dynamic.thread-pool.monitor.enable=true
-#spring.dynamic.thread-pool.monitor.collect-types=micrometer
-#spring.dynamic.thread-pool.monitor.thread-pool-types=dynamic,web
-#spring.dynamic.thread-pool.monitor.initial-delay=10000
-#spring.dynamic.thread-pool.monitor.collect-interval=5000
-#spring.dynamic.thread-pool.notify-platforms[0].platform=WECHAT
-#spring.dynamic.thread-pool.notify-platforms[0].token=ac0426a5-c712-474c-9bff-72b8b8f5caff
-#spring.dynamic.thread-pool.notify-platforms[1].platform=DING
-#spring.dynamic.thread-pool.notify-platforms[1].token=56417ebba6a27ca352f0de77a2ae9da66d01f39610b5ee8a6033c60ef9071c55
-#spring.dynamic.thread-pool.notify-platforms[2].platform=LARK
-#spring.dynamic.thread-pool.notify-platforms[2].token=2cbf2808-3839-4c26-a04d-fd201dd51f9e
+spring.dynamic.thread-pool.check-state-interval=10
+spring.dynamic.thread-pool.monitor.enable=true
+spring.dynamic.thread-pool.monitor.collect-types=micrometer
+spring.dynamic.thread-pool.monitor.thread-pool-types=dynamic
+spring.dynamic.thread-pool.monitor.initial-delay=3000
+spring.dynamic.thread-pool.monitor.collect-interval=3000
+spring.dynamic.thread-pool.monitor.agent-micrometer-port=29999
+
+spring.dynamic.thread-pool.notify-platforms[0].platform=LARK
+spring.dynamic.thread-pool.notify-platforms[0].token=6de41bdc-0799-45be-b128-7cddb9e777f0
+#spring.dynamic.thread-pool.notify-platforms[1].platform=WECHAT
+#spring.dynamic.thread-pool.notify-platforms[1].token=ac0426a5-c712-474c-9bff-72b8b8f5caff
+#spring.dynamic.thread-pool.notify-platforms[2].platform=DING
+#spring.dynamic.thread-pool.notify-platforms[2].token=56417ebba6a27ca352f0de77a2ae9da66d01f39610b5ee8a6033c60ef9071c55
+
spring.dynamic.thread-pool.apollo.namespace=application
spring.dynamic.thread-pool.config-file-type=properties
spring.dynamic.thread-pool.executors[0].thread-name-prefix = DynamicThreadPoolConfig#FIELD1
spring.dynamic.thread-pool.executors[0].core-pool-size = 2
-spring.dynamic.thread-pool.executors[0].thread-pool-id = cn.hippo4j.example.agent.config.apollo.ThreadPoolConfiguration#RUN_MESSAGE_SEND_TASK_EXECUTOR
+spring.dynamic.thread-pool.executors[0].thread-pool-id = cn.hippo4j.example.agent.core.config.ThreadPoolConfiguration#AGENT_RUN_MESSAGE_SEND_TASK_EXECUTOR
spring.dynamic.thread-pool.executors[0].maximum-pool-size = 20
spring.dynamic.thread-pool.executors[0].queue-capacity = 1024
spring.dynamic.thread-pool.executors[0].blocking-queue = ResizableCapacityLinkedBlockingQueue
@@ -46,3 +49,18 @@ spring.dynamic.thread-pool.executors[0].active-alarm = 80
spring.dynamic.thread-pool.executors[0].capacity-alarm = 80
spring.dynamic.thread-pool.executors[0].notify.interval = 8
spring.dynamic.thread-pool.executors[0].notify.receives = nobodyiam
+
+spring.dynamic.thread-pool.executors[1].thread-pool-id = runMessageSendTaskExecutor
+spring.dynamic.thread-pool.executors[1].thread-name-prefix = runMessageSendTaskExecutor
+spring.dynamic.thread-pool.executors[1].core-pool-size = 3
+spring.dynamic.thread-pool.executors[1].maximum-pool-size = 4
+spring.dynamic.thread-pool.executors[1].queue-capacity = 1024
+spring.dynamic.thread-pool.executors[1].blocking-queue = ResizableCapacityLinkedBlockingQueue
+spring.dynamic.thread-pool.executors[1].execute-time-out = 800
+spring.dynamic.thread-pool.executors[1].rejected-handler = AbortPolicy
+spring.dynamic.thread-pool.executors[1].keep-alive-time = 6691
+spring.dynamic.thread-pool.executors[1].allow-core-thread-time-out = true
+spring.dynamic.thread-pool.executors[1].active-alarm = 80
+spring.dynamic.thread-pool.executors[1].capacity-alarm = 80
+spring.dynamic.thread-pool.executors[1].notify.interval = 8
+spring.dynamic.thread-pool.executors[1].notify.receives = nobodyiam
diff --git a/examples/threadpool-example/agent/config-nacos-spring-boot-1x/pom.xml b/examples/threadpool-example/agent/config-nacos-spring-boot-1x/pom.xml
new file mode 100644
index 00000000..011342c8
--- /dev/null
+++ b/examples/threadpool-example/agent/config-nacos-spring-boot-1x/pom.xml
@@ -0,0 +1,74 @@
+
+
+ 4.0.0
+
+ cn.hippo4j
+ hippo4j-threadpool-agent-example
+ ${revision}
+
+
+ hippo4j-threadpool-agent-config-nacos-spring-boot-1x
+
+
+ true
+ 1.5.22.RELEASE
+
+
+
+
+ cn.hippo4j
+ hippo4j-agent-example-core
+ ${revision}
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-config
+ 1.5.1.RELEASE
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.cloud
+ spring-cloud-context
+ 1.3.0.RELEASE
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot.version}
+ pom
+ import
+
+
+
+
+
+ ${project.artifactId}
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/threadpool-example/agent/config-nacos-spring-boot-1x/src/main/java/cn/hippo4j/example/agent/config/nacos/v1/AgentConfigNacosSpringBoot1xExampleApplication.java b/examples/threadpool-example/agent/config-nacos-spring-boot-1x/src/main/java/cn/hippo4j/example/agent/config/nacos/v1/AgentConfigNacosSpringBoot1xExampleApplication.java
new file mode 100644
index 00000000..eceee1b4
--- /dev/null
+++ b/examples/threadpool-example/agent/config-nacos-spring-boot-1x/src/main/java/cn/hippo4j/example/agent/config/nacos/v1/AgentConfigNacosSpringBoot1xExampleApplication.java
@@ -0,0 +1,33 @@
+/*
+ * 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.example.agent.config.nacos.v1;
+
+import com.alibaba.nacos.api.exception.NacosException;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * Agent config Nacos example application.
+ */
+@SpringBootApplication(scanBasePackages = "cn.hippo4j.example.agent.core")
+public class AgentConfigNacosSpringBoot1xExampleApplication {
+
+ public static void main(String[] args) throws NacosException {
+ SpringApplication.run(AgentConfigNacosSpringBoot1xExampleApplication.class, args);
+ }
+}
diff --git a/examples/threadpool-example/agent/config-nacos-spring-boot-1x/src/main/resources/bootstrap.properties b/examples/threadpool-example/agent/config-nacos-spring-boot-1x/src/main/resources/bootstrap.properties
new file mode 100644
index 00000000..a16da5c4
--- /dev/null
+++ b/examples/threadpool-example/agent/config-nacos-spring-boot-1x/src/main/resources/bootstrap.properties
@@ -0,0 +1,63 @@
+server.port=8092
+server.servlet.context-path=/example
+spring.profiles.active=dev
+spring.application.name=hippo4j-config-nacos-spring-boot-starter-example
+
+# The following parameters are used for testing
+spring.cloud.nacos.config.server-addr=127.0.0.1:8848
+spring.cloud.nacos.config.name=dynamic-threadpool-example-config
+spring.cloud.nacos.config.file-extension=properties
+spring.cloud.nacos.config.refresh.enabled=true
+
+spring.dynamic.thread-pool.enable=true
+spring.dynamic.thread-pool.banner=true
+spring.dynamic.thread-pool.check-state-interval=3
+spring.dynamic.thread-pool.monitor.enable=true
+spring.dynamic.thread-pool.monitor.collect-types=micrometer
+spring.dynamic.thread-pool.monitor.thread-pool-types=dynamic
+spring.dynamic.thread-pool.monitor.agent-micrometer-port=29999
+
+spring.dynamic.thread-pool.monitor.initial-delay=3000
+spring.dynamic.thread-pool.monitor.collect-interval=3000
+spring.dynamic.thread-pool.notify-platforms[0].platform=LARK
+spring.dynamic.thread-pool.notify-platforms[0].token=6de41bdc-0799-45be-b128-7cddb9e777f0
+#spring.dynamic.thread-pool.notify-platforms[1].platform=WECHAT
+#spring.dynamic.thread-pool.notify-platforms[1].token=ac0426a5-c712-474c-9bff-72b8b8f5caff
+#spring.dynamic.thread-pool.notify-platforms[2].platform=DING
+#spring.dynamic.thread-pool.notify-platforms[2].token=56417ebba6a27ca352f0de77a2ae9da66d01f39610b5ee8a6033c60ef9071c55
+
+spring.dynamic.thread-pool.nacos.data-id=dynamic-threadpool-example-config
+spring.dynamic.thread-pool.nacos.group=DEFAULT_GROUP
+spring.dynamic.thread-pool.nacos.namespace=public
+
+spring.dynamic.thread-pool.config-file-type=properties
+
+spring.dynamic.thread-pool.executors[0].thread-name-prefix = DynamicThreadPoolConfig#FIELD1
+spring.dynamic.thread-pool.executors[0].core-pool-size = 2
+spring.dynamic.thread-pool.executors[0].thread-pool-id = cn.hippo4j.example.agent.core.config.ThreadPoolConfiguration#AGENT_RUN_MESSAGE_SEND_TASK_EXECUTOR
+spring.dynamic.thread-pool.executors[0].maximum-pool-size = 20
+spring.dynamic.thread-pool.executors[0].queue-capacity = 1024
+spring.dynamic.thread-pool.executors[0].blocking-queue = ResizableCapacityLinkedBlockingQueue
+spring.dynamic.thread-pool.executors[0].execute-time-out = 800
+spring.dynamic.thread-pool.executors[0].rejected-handler = AbortPolicy
+spring.dynamic.thread-pool.executors[0].keep-alive-time = 6691
+spring.dynamic.thread-pool.executors[0].allow-core-thread-time-out = true
+spring.dynamic.thread-pool.executors[0].alarm = true
+spring.dynamic.thread-pool.executors[0].active-alarm = 80
+spring.dynamic.thread-pool.executors[0].capacity-alarm = 80
+spring.dynamic.thread-pool.executors[0].notify.interval = 8
+spring.dynamic.thread-pool.executors[0].notify.receives = nobodyiam
+spring.dynamic.thread-pool.executors[1].thread-pool-id = runMessageSendTaskExecutor
+spring.dynamic.thread-pool.executors[1].thread-name-prefix = runMessageSendTaskExecutor
+spring.dynamic.thread-pool.executors[1].core-pool-size = 3
+spring.dynamic.thread-pool.executors[1].maximum-pool-size = 4
+spring.dynamic.thread-pool.executors[1].queue-capacity = 1024
+spring.dynamic.thread-pool.executors[1].blocking-queue = ResizableCapacityLinkedBlockingQueue
+spring.dynamic.thread-pool.executors[1].execute-time-out = 800
+spring.dynamic.thread-pool.executors[1].rejected-handler = AbortPolicy
+spring.dynamic.thread-pool.executors[1].keep-alive-time = 6691
+spring.dynamic.thread-pool.executors[1].allow-core-thread-time-out = true
+spring.dynamic.thread-pool.executors[1].active-alarm = 80
+spring.dynamic.thread-pool.executors[1].capacity-alarm = 80
+spring.dynamic.thread-pool.executors[1].notify.interval = 8
+spring.dynamic.thread-pool.executors[1].notify.receives = nobodyiam
diff --git a/examples/threadpool-example/agent/config-nacos/pom.xml b/examples/threadpool-example/agent/config-nacos/pom.xml
new file mode 100644
index 00000000..068ff153
--- /dev/null
+++ b/examples/threadpool-example/agent/config-nacos/pom.xml
@@ -0,0 +1,70 @@
+
+
+ 4.0.0
+
+ cn.hippo4j
+ hippo4j-threadpool-agent-example
+ ${revision}
+
+
+ hippo4j-threadpool-agent-config-nacos-example
+
+
+ true
+
+
+
+
+ cn.hippo4j
+ hippo4j-agent-example-core
+ ${revision}
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-config
+ 2.2.5.RELEASE
+
+
+ org.springframework.cloud
+ spring-cloud-context
+ 2.2.5.RELEASE
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.21
+
+
+
+
+ ${project.artifactId}
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/interceptor/DefaultConfigConstructorInterceptor.java b/examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/AgentConfigNacosExampleApplication.java
similarity index 64%
rename from agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/interceptor/DefaultConfigConstructorInterceptor.java
rename to examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/AgentConfigNacosExampleApplication.java
index e21049a6..1b315207 100644
--- a/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/interceptor/DefaultConfigConstructorInterceptor.java
+++ b/examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/AgentConfigNacosExampleApplication.java
@@ -15,17 +15,18 @@
* limitations under the License.
*/
-package cn.hippo4j.agent.plugin.apollo.interceptor;
+package cn.hippo4j.example.agent.config.nacos;
-import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance;
-import cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
- * Default config constructor interceptor
+ * Agent config Nacos example application.
*/
-public class DefaultConfigConstructorInterceptor implements InstanceConstructorInterceptor {
+@SpringBootApplication(scanBasePackages = "cn.hippo4j.example.agent.core")
+public class AgentConfigNacosExampleApplication {
- @Override
- public void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable {
+ public static void main(String[] args) {
+ SpringApplication.run(AgentConfigNacosExampleApplication.class, args);
}
}
diff --git a/examples/threadpool-example/agent/config-nacos/src/main/resources/bootstrap.properties b/examples/threadpool-example/agent/config-nacos/src/main/resources/bootstrap.properties
new file mode 100644
index 00000000..24869387
--- /dev/null
+++ b/examples/threadpool-example/agent/config-nacos/src/main/resources/bootstrap.properties
@@ -0,0 +1,70 @@
+server.port=8092
+server.servlet.context-path=/example
+
+nacos.config.auto-refresh=true
+nacos.config.bootstrap.enable=true
+# The following parameters are used for testing
+
+spring.cloud.nacos.config.server-addr=127.0.0.1:8848
+spring.cloud.nacos.config.extension-configs[0].data-id=dynamic-threadpool-example-config
+spring.cloud.nacos.config.extension-configs[0].group=DEFAULT_GROUP
+spring.cloud.nacos.config.extension-configs[0].refresh=true
+
+spring.profiles.active=dev
+spring.application.name=hippo4j-config-nacos-spring-boot-starter-example
+management.metrics.export.prometheus.enabled=true
+management.server.port=29998
+management.endpoints.web.exposure.include=*
+spring.dynamic.thread-pool.enable=true
+spring.dynamic.thread-pool.banner=true
+spring.dynamic.thread-pool.check-state-interval=10
+#spring.dynamic.thread-pool.monitor.enable=true
+#spring.dynamic.thread-pool.monitor.collect-types=micrometer
+#spring.dynamic.thread-pool.monitor.thread-pool-types=dynamic,web
+spring.dynamic.thread-pool.monitor.agent-micrometer-port=29999
+
+spring.dynamic.thread-pool.monitor.initial-delay=3000
+spring.dynamic.thread-pool.monitor.collect-interval=3000
+spring.dynamic.thread-pool.notify-platforms[0].platform=LARK
+spring.dynamic.thread-pool.notify-platforms[0].token=6de41bdc-0799-45be-b128-7cddb9e777f0
+#spring.dynamic.thread-pool.notify-platforms[1].platform=WECHAT
+#spring.dynamic.thread-pool.notify-platforms[1].token=ac0426a5-c712-474c-9bff-72b8b8f5caff
+#spring.dynamic.thread-pool.notify-platforms[2].platform=DING
+#spring.dynamic.thread-pool.notify-platforms[2].token=56417ebba6a27ca352f0de77a2ae9da66d01f39610b5ee8a6033c60ef9071c55
+
+spring.dynamic.thread-pool.nacos.data-id=dynamic-threadpool-example-config
+spring.dynamic.thread-pool.nacos.group=DEFAULT_GROUP
+spring.dynamic.thread-pool.nacos.namespace=public
+
+spring.dynamic.thread-pool.config-file-type=properties
+
+spring.dynamic.thread-pool.executors[0].thread-name-prefix = DynamicThreadPoolConfig#FIELD1
+spring.dynamic.thread-pool.executors[0].core-pool-size = 2
+spring.dynamic.thread-pool.executors[0].thread-pool-id = cn.hippo4j.example.agent.core.config.ThreadPoolConfiguration#AGENT_RUN_MESSAGE_SEND_TASK_EXECUTOR
+spring.dynamic.thread-pool.executors[0].maximum-pool-size = 20
+spring.dynamic.thread-pool.executors[0].queue-capacity = 1024
+spring.dynamic.thread-pool.executors[0].blocking-queue = ResizableCapacityLinkedBlockingQueue
+spring.dynamic.thread-pool.executors[0].execute-time-out = 800
+spring.dynamic.thread-pool.executors[0].rejected-handler = AbortPolicy
+spring.dynamic.thread-pool.executors[0].keep-alive-time = 6691
+spring.dynamic.thread-pool.executors[0].allow-core-thread-time-out = true
+spring.dynamic.thread-pool.executors[0].alarm = true
+spring.dynamic.thread-pool.executors[0].active-alarm = 80
+spring.dynamic.thread-pool.executors[0].capacity-alarm = 80
+spring.dynamic.thread-pool.executors[0].notify.interval = 8
+spring.dynamic.thread-pool.executors[0].notify.receives = nobodyiam
+
+spring.dynamic.thread-pool.executors[1].thread-pool-id = runMessageSendTaskExecutor
+spring.dynamic.thread-pool.executors[1].thread-name-prefix = runMessageSendTaskExecutor
+spring.dynamic.thread-pool.executors[1].core-pool-size = 3
+spring.dynamic.thread-pool.executors[1].maximum-pool-size = 4
+spring.dynamic.thread-pool.executors[1].queue-capacity = 1024
+spring.dynamic.thread-pool.executors[1].blocking-queue = ResizableCapacityLinkedBlockingQueue
+spring.dynamic.thread-pool.executors[1].execute-time-out = 800
+spring.dynamic.thread-pool.executors[1].rejected-handler = AbortPolicy
+spring.dynamic.thread-pool.executors[1].keep-alive-time = 6691
+spring.dynamic.thread-pool.executors[1].allow-core-thread-time-out = true
+spring.dynamic.thread-pool.executors[1].active-alarm = 80
+spring.dynamic.thread-pool.executors[1].capacity-alarm = 80
+spring.dynamic.thread-pool.executors[1].notify.interval = 8
+spring.dynamic.thread-pool.executors[1].notify.receives = nobodyiam
diff --git a/examples/threadpool-example/agent/pom.xml b/examples/threadpool-example/agent/pom.xml
index 11fbc894..fcbe2ce2 100644
--- a/examples/threadpool-example/agent/pom.xml
+++ b/examples/threadpool-example/agent/pom.xml
@@ -18,5 +18,9 @@
config-apollo
+ config-nacos
+ agent-example-core
+ config-nacos-spring-boot-1x
+ config-apollo-spring-boot-1x
\ No newline at end of file
diff --git a/examples/threadpool-example/config/config-nacos/src/main/java/cn/hippo4j/example/config/nacos/ConfigNacosExampleApplication.java b/examples/threadpool-example/config/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/ConfigNacosExampleApplication.java
similarity index 96%
rename from examples/threadpool-example/config/config-nacos/src/main/java/cn/hippo4j/example/config/nacos/ConfigNacosExampleApplication.java
rename to examples/threadpool-example/config/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/ConfigNacosExampleApplication.java
index fda32d1e..db2f8ea9 100644
--- a/examples/threadpool-example/config/config-nacos/src/main/java/cn/hippo4j/example/config/nacos/ConfigNacosExampleApplication.java
+++ b/examples/threadpool-example/config/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/ConfigNacosExampleApplication.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package cn.hippo4j.example.config.nacos;
+package cn.hippo4j.example.agent.config.nacos;
import cn.hippo4j.core.enable.EnableDynamicThreadPool;
import org.springframework.boot.SpringApplication;
diff --git a/infra/common/src/main/java/cn/hippo4j/common/constant/Constants.java b/infra/common/src/main/java/cn/hippo4j/common/constant/Constants.java
index 9eeb865e..71577018 100644
--- a/infra/common/src/main/java/cn/hippo4j/common/constant/Constants.java
+++ b/infra/common/src/main/java/cn/hippo4j/common/constant/Constants.java
@@ -56,6 +56,8 @@ public class Constants {
public static final String GENERAL_SPLIT_SYMBOL = ",";
+ public static final String DOT_SPLIT_SYMBOL = ".";
+
public static final String IDENTIFY_SLICER_SYMBOL = "_";
public static final String LONG_POLLING_LINE_SEPARATOR = "\r\n";
@@ -128,5 +130,12 @@ public class Constants {
public static final String CONFIGURATION_PROPERTIES_PREFIX = "spring.dynamic.thread-pool";
+ public static final String EXECUTORS = "executors";
+
public static final long NO_REJECT_COUNT_NUM = -1L;
+
+ public static final String DYNAMIC_THREAD_POOL_EXECUTOR = "cn.hippo4j.core.executor.DynamicThreadPoolExecutor";
+
+ public static final int DEFAULT_INTERVAL = 5;
+
}
diff --git a/infra/common/src/main/java/cn/hippo4j/common/extension/design/AbstractSubjectCenter.java b/infra/common/src/main/java/cn/hippo4j/common/extension/design/AbstractSubjectCenter.java
index be8d4242..07d58a89 100644
--- a/infra/common/src/main/java/cn/hippo4j/common/extension/design/AbstractSubjectCenter.java
+++ b/infra/common/src/main/java/cn/hippo4j/common/extension/design/AbstractSubjectCenter.java
@@ -96,6 +96,15 @@ public class AbstractSubjectCenter {
observers.remove(observer);
}
+ /**
+ * get observer by subject.
+ *
+ * @param subject
+ */
+ public static List get(SubjectType subjectType) {
+ return OBSERVERS_MAP.get(subjectType.name());
+ }
+
/**
* Notify.
*
@@ -145,6 +154,11 @@ public class AbstractSubjectCenter {
/**
* Thread-pool dynamic refresh.
*/
- THREAD_POOL_DYNAMIC_REFRESH
+ THREAD_POOL_DYNAMIC_REFRESH,
+
+ /**
+ * Agent Spring Properties Loader Completed.
+ */
+ AGENT_SPRING_PROPERTIES_LOADER_COMPLETED
}
}
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/infra/common/src/main/java/cn/hippo4j/common/toolkit/http/HttpUtil.java b/infra/common/src/main/java/cn/hippo4j/common/toolkit/http/HttpUtil.java
index 2f8a475e..fdd14c9b 100644
--- a/infra/common/src/main/java/cn/hippo4j/common/toolkit/http/HttpUtil.java
+++ b/infra/common/src/main/java/cn/hippo4j/common/toolkit/http/HttpUtil.java
@@ -31,10 +31,13 @@ import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
+import java.io.BufferedWriter;
import java.io.OutputStream;
+import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Optional;
@@ -198,6 +201,18 @@ public class HttpUtil {
return executeJson(url, HttpMethod.POST, json, null);
}
+ /**
+ * Send a post network request.
+ *
+ * @param url target url
+ * @param json json data
+ * @param headers headers
+ * @return
+ */
+ public static String postJson(String url, String json, Map headers) {
+ return executeJson(url, HttpMethod.POST, json, headers);
+ }
+
/**
* Send a put network request.
*
@@ -310,8 +325,10 @@ public class HttpUtil {
byte[] b = bodyString.getBytes();
connection.setRequestProperty(CONTENT_LENGTH, String.valueOf(b.length));
OutputStream outputStream = connection.getOutputStream();
- outputStream.write(b, 0, b.length);
- outputStream.flush();
+ OutputStreamWriter osw = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
+ BufferedWriter writer = new BufferedWriter(osw);
+ writer.write(bodyString);
+ writer.flush();
IoUtil.closeQuietly(outputStream);
}
connection.connect();
diff --git a/kernel/alarm/src/main/java/cn/hippo4j/threadpool/alarm/handler/DefaultThreadPoolCheckAlarmHandler.java b/kernel/alarm/src/main/java/cn/hippo4j/threadpool/alarm/handler/DefaultThreadPoolCheckAlarmHandler.java
index f4b7710f..98947939 100644
--- a/kernel/alarm/src/main/java/cn/hippo4j/threadpool/alarm/handler/DefaultThreadPoolCheckAlarmHandler.java
+++ b/kernel/alarm/src/main/java/cn/hippo4j/threadpool/alarm/handler/DefaultThreadPoolCheckAlarmHandler.java
@@ -235,4 +235,14 @@ public class DefaultThreadPoolCheckAlarmHandler implements Runnable, ThreadPoolC
.rejectCountNum(rejectCount)
.build();
}
+
+ /**
+ * Terminates the scheduled tasks and asynchronous alarm notifications by
+ * forcefully shutting down the respective thread pools.
+ */
+ public void destroyScheduleExecute() {
+ alarmNotifyExecutor.shutdownNow();
+ asyncAlarmNotifyExecutor.shutdownNow();
+ }
+
}
diff --git a/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/parser/JsonConfigParser.java b/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/parser/JsonConfigParser.java
index c0acf1d9..bb247068 100644
--- a/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/parser/JsonConfigParser.java
+++ b/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/parser/JsonConfigParser.java
@@ -34,6 +34,7 @@ import java.util.List;
* Json config parser.
*/
public class JsonConfigParser extends AbstractConfigParser {
+
private static final ObjectMapper MAPPER;
private static final String DOT = ".";
private static final String LEFT_BRACE = "{";
@@ -91,7 +92,7 @@ public class JsonConfigParser extends AbstractConfigParser {
return new HashMap<>(1);
}
- return doParse(content,"");
+ return doParse(content, "");
}
@Override
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;
}
diff --git a/kernel/message/core/src/main/java/cn/hippo4j/threadpool/message/core/platform/LarkSendMessageHandler.java b/kernel/message/core/src/main/java/cn/hippo4j/threadpool/message/core/platform/LarkSendMessageHandler.java
index 4e43da9f..ff1e8a0b 100644
--- a/kernel/message/core/src/main/java/cn/hippo4j/threadpool/message/core/platform/LarkSendMessageHandler.java
+++ b/kernel/message/core/src/main/java/cn/hippo4j/threadpool/message/core/platform/LarkSendMessageHandler.java
@@ -43,6 +43,8 @@ import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
@@ -154,7 +156,9 @@ public class LarkSendMessageHandler implements SendMessageHandler {
private void execute(String secretKey, String text) {
String serverUrl = LarkAlarmConstants.LARK_BOT_URL + secretKey;
try {
- String responseBody = HttpUtil.postJson(serverUrl, text);
+ Map headers = new HashMap<>();
+ headers.put("Content-Type", "application/json; charset=UTF-8");
+ String responseBody = HttpUtil.postJson(serverUrl, text, headers);
LarkRobotResponse response = JSONUtil.parseObject(responseBody, LarkRobotResponse.class);
Assert.isTrue(response != null, "Response is null.");
if (response.getCode() != 0) {
diff --git a/kernel/message/core/src/main/java/cn/hippo4j/threadpool/message/core/service/SendMessageHandler.java b/kernel/message/core/src/main/java/cn/hippo4j/threadpool/message/core/service/SendMessageHandler.java
index 5a8b0630..d5619b9d 100644
--- a/kernel/message/core/src/main/java/cn/hippo4j/threadpool/message/core/service/SendMessageHandler.java
+++ b/kernel/message/core/src/main/java/cn/hippo4j/threadpool/message/core/service/SendMessageHandler.java
@@ -17,12 +17,11 @@
package cn.hippo4j.threadpool.message.core.service;
+import cn.hippo4j.threadpool.message.api.NotifyConfigDTO;
import cn.hippo4j.threadpool.message.core.request.AlarmNotifyRequest;
import cn.hippo4j.threadpool.message.core.request.ChangeParameterNotifyRequest;
import cn.hippo4j.threadpool.message.core.request.WebChangeParameterNotifyRequest;
-import cn.hippo4j.threadpool.message.api.NotifyConfigDTO;
import lombok.SneakyThrows;
-import org.springframework.core.io.ClassPathResource;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
@@ -70,9 +69,9 @@ public interface SendMessageHandler {
default String readUtf8String(String path) {
int endFlagCode = -1;
String resultReadStr;
- ClassPathResource classPathResource = new ClassPathResource(path);
+ ClassLoader classLoader = this.getClass().getClassLoader();
try (
- InputStream inputStream = classPathResource.getInputStream();
+ InputStream inputStream = classLoader.getResourceAsStream(path);
BufferedInputStream bis = new BufferedInputStream(inputStream);
ByteArrayOutputStream buf = new ByteArrayOutputStream()) {
int result = bis.read();
diff --git a/threadpool/core/src/test/java/cn/hippo4j/core/adapter/ZipkinExecutorAdapterTest.java b/threadpool/core/src/test/java/cn/hippo4j/core/adapter/ZipkinExecutorAdapterTest.java
index eb27eec4..f34f3207 100644
--- a/threadpool/core/src/test/java/cn/hippo4j/core/adapter/ZipkinExecutorAdapterTest.java
+++ b/threadpool/core/src/test/java/cn/hippo4j/core/adapter/ZipkinExecutorAdapterTest.java
@@ -68,7 +68,7 @@ public class ZipkinExecutorAdapterTest {
@Test
public void testReplace() {
Object executor = new CustomWrappingExecutorService(Executors.newCachedThreadPool());
- CustomWrappingExecutorService executorChange = (CustomWrappingExecutorService)executor;
+ CustomWrappingExecutorService executorChange = (CustomWrappingExecutorService) executor;
ExecutorService beforeReplace = executorChange.delegate();
zipkinExecutorAdapter.replace(executor, dynamicThreadPool);
ExecutorService afterReplace = executorChange.delegate();