diff --git a/agent/hippo4j-agent-plugin/nacos-plugin/pom.xml b/agent/hippo4j-agent-plugin/nacos-plugin/pom.xml new file mode 100644 index 00000000..f048923f --- /dev/null +++ b/agent/hippo4j-agent-plugin/nacos-plugin/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + cn.hippo4j + hippo4j-agent-plugin + ${revision} + + + hippo4j-agent-nacos-plugin + + + 2.2.1 + + + + + cn.hippo4j + hippo4j-agent-spring-plugin-common + ${project.version} + + + cn.hippo4j + hippo4j-threadpool-dynamic-mode-config + ${project.version} + + + com.alibaba.nacos + nacos-client + ${nacos.version} + provided + + + + \ No newline at end of file diff --git a/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/NacosDynamicThreadPoolChangeHandler.java b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/NacosDynamicThreadPoolChangeHandler.java new file mode 100644 index 00000000..866f3ccc --- /dev/null +++ b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/NacosDynamicThreadPoolChangeHandler.java @@ -0,0 +1,129 @@ +/* + * 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.nacos; + +import cn.hippo4j.agent.plugin.spring.common.conf.SpringBootConfig; +import cn.hippo4j.agent.plugin.spring.common.toolkit.SpringPropertyBinder; +import cn.hippo4j.common.executor.ThreadFactoryBuilder; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; +import cn.hippo4j.threadpool.dynamic.mode.config.parser.ConfigParserHandler; +import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties; +import cn.hippo4j.threadpool.dynamic.mode.config.refresher.AbstractConfigThreadPoolDynamicRefresh; +import com.alibaba.nacos.api.NacosFactory; +import com.alibaba.nacos.api.PropertyKeyConst; +import com.alibaba.nacos.api.config.ConfigService; +import com.alibaba.nacos.api.config.listener.Listener; +import org.springframework.boot.context.properties.bind.Binder; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executor; +import java.util.concurrent.ScheduledThreadPoolExecutor; + +import static cn.hippo4j.common.constant.Constants.DEFAULT_NAMESPACE_ID; + +/** + * NacosDynamicThreadPoolChangeHandler is responsible for handling dynamic thread pool + * configuration changes in a Spring environment by listening to configuration updates from Nacos. + *

+ * This class extends {@link AbstractConfigThreadPoolDynamicRefresh} and implements the logic + * to register a Nacos listener, handle configuration changes, and dynamically refresh the thread pool + * properties based on the new configuration. + *

+ */ +public class NacosDynamicThreadPoolChangeHandler extends AbstractConfigThreadPoolDynamicRefresh { + + private static final ILog LOGGER = LogManager.getLogger(NacosDynamicThreadPoolChangeHandler.class); + + /** + * Registers a listener with Nacos to monitor for changes in the thread pool configuration. + *

+ * This method sets up the Nacos {@link ConfigService} with the server address and namespace + * from the Spring Boot configuration. It then adds a listener that will receive and process + * configuration updates, triggering a dynamic refresh of thread pool settings. + */ + @Override + public void registerListener() { + // Retrieve necessary configuration properties + String configFileType = SpringBootConfig.Spring.Dynamic.Thread_Pool.CONFIG_FILE_TYPE; + String serverAddr = SpringBootConfig.Spring.Dynamic.Thread_Pool.Nacos.SERVER_ADDR; + String dataId = SpringBootConfig.Spring.Dynamic.Thread_Pool.Nacos.DATA_ID; + String group = SpringBootConfig.Spring.Dynamic.Thread_Pool.Nacos.GROUP; + String namespace = SpringBootConfig.Spring.Dynamic.Thread_Pool.Nacos.NAMESPACE.get(0); + namespace = namespace.equals(DEFAULT_NAMESPACE_ID) ? "" : namespace; + try { + // Initialize Nacos ConfigService with the provided properties + Properties properties = new Properties(); + properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr); + properties.put(PropertyKeyConst.NAMESPACE, namespace); + ConfigService configService = NacosFactory.createConfigService(properties); + + // Define the listener to handle configuration changes + Listener configChangeListener = new Listener() { + + @Override + public void receiveConfigInfo(String configInfo) { + LOGGER.debug("Received configuration: " + configInfo); + Map changeValueMap = new HashMap<>(); + try { + // Parse the configuration and map the values to the appropriate keys + Map configInfoMap = ConfigParserHandler.getInstance().parseConfig(configInfo, configFileType); + configInfoMap.forEach((key, value) -> { + if (key instanceof String) { + changeValueMap.put((String) key, value); + } + }); + } catch (IOException e) { + LOGGER.error(e, "[Hippo4j-Agent] Dynamic thread pool refresher, Failed to resolve configuration. configFileType: {} configInfo: {} ", configFileType, configInfo); + } + // Trigger the dynamic refresh with the parsed configuration + dynamicRefresh(configFileType, configInfo, changeValueMap); + } + + @Override + public Executor getExecutor() { + return new ScheduledThreadPoolExecutor(1, ThreadFactoryBuilder.builder().daemon(true).prefix("client.dynamic.refresh.agent").build()); + } + }; + // Add the listener to the Nacos ConfigService + configService.addListener(dataId, group, configChangeListener); + LOGGER.info("[Hippo4j-Agent] Dynamic thread pool refresher, add Nacos listener successfully. namespace: {} data-id: {} group: {}", namespace, dataId, group); + } catch (Exception e) { + LOGGER.error(e, "[Hippo4j-Agent] Dynamic thread pool refresher, add Nacos listener failure. namespace: {} data-id: {} group: {}", namespace, dataId, group); + } + } + + /** + * Builds and binds the {@link BootstrapConfigProperties} from the given configuration map. + *

+ * 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 configInfo) { + BootstrapConfigProperties bindableBootstrapConfigProperties = SpringPropertyBinder.bindProperties(configInfo, BootstrapConfigProperties.PREFIX, BootstrapConfigProperties.class); + return bindableBootstrapConfigProperties; + } +} diff --git a/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/boot/NacosPluginBootService.java b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/boot/NacosPluginBootService.java new file mode 100644 index 00000000..65fe2a47 --- /dev/null +++ b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/boot/NacosPluginBootService.java @@ -0,0 +1,48 @@ +/* + * 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.nacos.boot; + +import cn.hippo4j.agent.core.boot.BootService; +import cn.hippo4j.agent.core.boot.DefaultImplementor; + +/** + * Nacos plugin boot service + */ +@DefaultImplementor +public class NacosPluginBootService implements BootService { + + @Override + public void prepare() throws Throwable { + + } + + @Override + public void boot() throws Throwable { + + } + + @Override + public void onComplete() throws Throwable { + + } + + @Override + public void shutdown() throws Throwable { + + } +} diff --git a/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/define/NacosCloudAdapterInstrumentation.java b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/define/NacosCloudAdapterInstrumentation.java new file mode 100644 index 00000000..6fe99477 --- /dev/null +++ b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/define/NacosCloudAdapterInstrumentation.java @@ -0,0 +1,75 @@ +/* + * 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.nacos.define; + +import cn.hippo4j.agent.core.plugin.WitnessMethod; +import cn.hippo4j.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import cn.hippo4j.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import cn.hippo4j.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import cn.hippo4j.agent.core.plugin.match.ClassMatch; +import cn.hippo4j.agent.core.plugin.match.NameMatch; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; + +import java.util.Collections; +import java.util.List; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +public class NacosCloudAdapterInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration"; + + private static final String INSTANCE_METHODS_INTERCEPT_CLASS = "cn.hippo4j.agent.plugin.nacos.interceptor.NacosCloudAdapterConfigInstanceMethodInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{new InstanceMethodsInterceptPoint() { + + @Override + public ElementMatcher getMethodsMatcher() { + return named("initialize"); + } + + @Override + public String getMethodsInterceptor() { + return INSTANCE_METHODS_INTERCEPT_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }}; + } + + @Override + protected List witnessMethods() { + return Collections.singletonList(new WitnessMethod("com.alibaba.cloud.nacos.client.NacosPropertySourceLocator", named("locate"))); + } +} diff --git a/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/define/NacosInstrumentation.java b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/define/NacosInstrumentation.java new file mode 100644 index 00000000..d33c6031 --- /dev/null +++ b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/define/NacosInstrumentation.java @@ -0,0 +1,66 @@ +/* + * 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.nacos.define; + +import cn.hippo4j.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import cn.hippo4j.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import cn.hippo4j.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import cn.hippo4j.agent.core.plugin.match.ClassMatch; +import cn.hippo4j.agent.core.plugin.match.NameMatch; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +/** + * Nacos instrumentation + */ +public class NacosInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.alibaba.nacos.client.config.NacosConfigService"; + + private static final String CONSTRUCTOR_INTERCEPT_CLASS = "cn.hippo4j.agent.plugin.nacos.interceptor.NacosConfigConstructorInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[]{ + new ConstructorInterceptPoint() { + + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return CONSTRUCTOR_INTERCEPT_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } +} diff --git a/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/interceptor/NacosCloudAdapterConfigInstanceMethodInterceptor.java b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/interceptor/NacosCloudAdapterConfigInstanceMethodInterceptor.java new file mode 100644 index 00000000..7a7cf841 --- /dev/null +++ b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/interceptor/NacosCloudAdapterConfigInstanceMethodInterceptor.java @@ -0,0 +1,68 @@ +/* + * 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.nacos.interceptor; + +import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import cn.hippo4j.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader; +import cn.hippo4j.agent.plugin.spring.common.support.SpringThreadPoolRegisterSupport; +import cn.hippo4j.core.config.ApplicationContextHolder; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; + +import java.lang.reflect.Method; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Nacos Cloud config constructor interceptor + */ +public class NacosCloudAdapterConfigInstanceMethodInterceptor implements InstanceMethodsAroundInterceptor { + + private static final AtomicBoolean isExecuted = new AtomicBoolean(false); + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + + } + + /** + * + */ + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + // This logic will only be executed once + if (isExecuted.compareAndSet(false, true)) { + // Get the configurable Application Context + ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) allArguments[0]; + ConfigurableEnvironment environment = configurableApplicationContext.getEnvironment(); + + // Remote Nacos configuration swiped into SpringPropertiesLoader + SpringPropertiesLoader.loadSpringProperties(environment); + // Refresh thread pool instances through configuration + SpringThreadPoolRegisterSupport.registerThreadPoolInstances(ApplicationContextHolder.getInstance()); + + } + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + + } +} diff --git a/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/interceptor/NacosConfigConstructorInterceptor.java b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/interceptor/NacosConfigConstructorInterceptor.java new file mode 100644 index 00000000..7a629319 --- /dev/null +++ b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/interceptor/NacosConfigConstructorInterceptor.java @@ -0,0 +1,58 @@ +/* + * 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.nacos.interceptor; + +import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import cn.hippo4j.agent.plugin.nacos.NacosDynamicThreadPoolChangeHandler; +import cn.hippo4j.agent.plugin.nacos.listeners.NacosConfigPropertiesLoaderCompletedListener; +import cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader; +import cn.hippo4j.common.extension.design.AbstractSubjectCenter; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Nacos config constructor interceptor + */ +public class NacosConfigConstructorInterceptor implements InstanceConstructorInterceptor { + + private static final AtomicBoolean isExecuted = new AtomicBoolean(false); + + @Override + public void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable { + // This logic will only be executed once + if (isExecuted.compareAndSet(false, true)) { + + // 判断 SpringPropertiesLoader 是否初始化 + AtomicBoolean active = SpringPropertiesLoader.getActive(); + + // For Nacos-Cloud, the SpringPropertiesLoader environment initialization is triggered first, and then the logic to register listeners is triggered + // For Nacos-Boot, the listener is registered first, and the SpringPropertiesLoader environment is initialized + if (Boolean.TRUE.equals(active.get())) { + new NacosDynamicThreadPoolChangeHandler().registerListener(); + return; + } + + // The Nacos plugin triggers before the Spring configuration plug-in. + // This means that when the Apollo plug-in executes, Spring's Environment is not yet ready, + // so the configuration cannot be read + // After listening to the AGENT_SPRING_PROPERTIES_LOADER_COMPLETED event, register the listener for Apollo + AbstractSubjectCenter.register(AbstractSubjectCenter.SubjectType.AGENT_SPRING_PROPERTIES_LOADER_COMPLETED, new NacosConfigPropertiesLoaderCompletedListener()); + } + } +} diff --git a/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/listeners/NacosConfigPropertiesLoaderCompletedListener.java b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/listeners/NacosConfigPropertiesLoaderCompletedListener.java new file mode 100644 index 00000000..fa7a795b --- /dev/null +++ b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/listeners/NacosConfigPropertiesLoaderCompletedListener.java @@ -0,0 +1,35 @@ +/* + * 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.nacos.listeners; + +import cn.hippo4j.agent.plugin.nacos.NacosDynamicThreadPoolChangeHandler; +import cn.hippo4j.common.extension.design.Observer; +import cn.hippo4j.common.extension.design.ObserverMessage; +import cn.hippo4j.threadpool.dynamic.api.ThreadPoolDynamicRefresh; + +/** + * Nacos Config Properties Loader Completed Listener + */ +public class NacosConfigPropertiesLoaderCompletedListener implements Observer { + + @Override + public void accept(ObserverMessage observerMessage) { + ThreadPoolDynamicRefresh dynamicRefresh = new NacosDynamicThreadPoolChangeHandler(); + dynamicRefresh.registerListener(); + } +} diff --git a/agent/hippo4j-agent-plugin/nacos-plugin/src/main/resources/META-INF/services/cn.hippo4j.agent.core.boot.BootService b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/resources/META-INF/services/cn.hippo4j.agent.core.boot.BootService new file mode 100644 index 00000000..5337b3b0 --- /dev/null +++ b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/resources/META-INF/services/cn.hippo4j.agent.core.boot.BootService @@ -0,0 +1,17 @@ +# 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. + +cn.hippo4j.agent.plugin.nacos.boot.NacosPluginBootService \ No newline at end of file diff --git a/agent/hippo4j-agent-plugin/nacos-plugin/src/main/resources/hippo4j-plugin.def b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/resources/hippo4j-plugin.def new file mode 100644 index 00000000..fb175826 --- /dev/null +++ b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/resources/hippo4j-plugin.def @@ -0,0 +1,18 @@ +# 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. + +nacos-plugin=cn.hippo4j.agent.plugin.nacos.define.NacosInstrumentation +nacos-cloud-adapter-plugin=cn.hippo4j.agent.plugin.nacos.define.NacosCloudAdapterInstrumentation \ No newline at end of file diff --git a/agent/hippo4j-agent-plugin/pom.xml b/agent/hippo4j-agent-plugin/pom.xml index 4a3f7954..0217b17b 100644 --- a/agent/hippo4j-agent-plugin/pom.xml +++ b/agent/hippo4j-agent-plugin/pom.xml @@ -16,6 +16,7 @@ threadpool-plugin adapter-plugins apollo-plugin + nacos-plugin diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/define/EventPublishingRunListenerInstrumentation.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/define/ApplicationContextInstrumentation.java similarity index 51% rename from agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/define/EventPublishingRunListenerInstrumentation.java rename to agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/define/ApplicationContextInstrumentation.java index 06c93998..315f3a96 100644 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/define/EventPublishingRunListenerInstrumentation.java +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/define/ApplicationContextInstrumentation.java @@ -28,18 +28,17 @@ import net.bytebuddy.matcher.ElementMatcher; import java.util.Collections; import java.util.List; -import static net.bytebuddy.matcher.ElementMatchers.named; import static cn.hippo4j.agent.core.plugin.match.NameMatch.byName; +import static net.bytebuddy.matcher.ElementMatchers.named; /** - * Event publishing run listener instrumentation + * Application Context Refresh instrumentation */ -public class EventPublishingRunListenerInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { +public class ApplicationContextInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { - private static final String ENHANCE_CLASS = "org.springframework.boot.context.event.EventPublishingRunListener"; + private static final String ENHANCE_CLASS = "org.springframework.context.support.AbstractApplicationContext"; - private static final String EVENT_PUBLISHING_FINISHED_INTERCEPTOR = "cn.hippo4j.agent.plugin.spring.boot.v1.interceptor.EventPublishingFinishedInterceptor"; - private static final String EVENT_PUBLISHING_ENVIRONMENT_PREPARED_INTERCEPTOR = "cn.hippo4j.agent.plugin.spring.common.interceptor.EventPublishingRunListenerEnvironmentPreparedInterceptor"; + private static final String APPLICATION_CONTEXT_REFRESH_INTERCEPTOR = "cn.hippo4j.agent.plugin.spring.boot.v1.interceptor.ApplicationContextInterceptor"; @Override protected ClassMatch enhanceClass() { @@ -53,47 +52,27 @@ public class EventPublishingRunListenerInstrumentation extends ClassInstanceMeth @Override public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { - return new InstanceMethodsInterceptPoint[]{ - new InstanceMethodsInterceptPoint() { - - @Override - public ElementMatcher getMethodsMatcher() { - return named("finished"); - } - - @Override - public String getMethodsInterceptor() { - return EVENT_PUBLISHING_FINISHED_INTERCEPTOR; - } - - @Override - public boolean isOverrideArgs() { - return false; - } - }, - new InstanceMethodsInterceptPoint() { - - @Override - public ElementMatcher getMethodsMatcher() { - return named("environmentPrepared"); - } - - @Override - public String getMethodsInterceptor() { - return EVENT_PUBLISHING_ENVIRONMENT_PREPARED_INTERCEPTOR; - } - - @Override - public boolean isOverrideArgs() { - return false; - } - } - }; + return new InstanceMethodsInterceptPoint[]{new InstanceMethodsInterceptPoint() { + + @Override + public ElementMatcher getMethodsMatcher() { + return named("refresh"); + } + + @Override + public String getMethodsInterceptor() { + return APPLICATION_CONTEXT_REFRESH_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }}; } @Override protected List witnessMethods() { - return Collections.singletonList(new WitnessMethod("org.springframework.boot.context.event.EventPublishingRunListener", - named("finished"))); + return Collections.singletonList(new WitnessMethod("org.springframework.boot.context.event.EventPublishingRunListener", named("finished"))); } } diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/interceptor/EventPublishingFinishedInterceptor.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/interceptor/ApplicationContextInterceptor.java similarity index 64% rename from agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/interceptor/EventPublishingFinishedInterceptor.java rename to agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/interceptor/ApplicationContextInterceptor.java index dcee8726..8297bfb9 100644 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/interceptor/EventPublishingFinishedInterceptor.java +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/interceptor/ApplicationContextInterceptor.java @@ -17,29 +17,25 @@ package cn.hippo4j.agent.plugin.spring.boot.v1.interceptor; -import cn.hippo4j.agent.adapter.dubbo.DubboThreadPoolAdapter; -import cn.hippo4j.common.logging.api.ILog; -import cn.hippo4j.common.logging.api.LogManager; import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance; import cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; import cn.hippo4j.agent.core.plugin.interceptor.enhance.MethodInterceptResult; -import cn.hippo4j.agent.plugin.spring.boot.v1.DynamicThreadPoolChangeHandlerSpring1x; +import cn.hippo4j.agent.plugin.spring.common.event.DynamicThreadPoolRefreshListener; import cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader; import cn.hippo4j.agent.plugin.spring.common.support.SpringThreadPoolRegisterSupport; -import cn.hippo4j.threadpool.dynamic.api.ThreadPoolDynamicRefresh; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import cn.hippo4j.common.extension.design.AbstractSubjectCenter; +import cn.hippo4j.core.config.ApplicationContextHolder; import org.springframework.context.ConfigurableApplicationContext; import java.lang.reflect.Method; +import java.util.concurrent.atomic.AtomicBoolean; /** - * Event publishing finished interceptor + * Application Context Refresh interceptor */ -public class EventPublishingFinishedInterceptor implements InstanceMethodsAroundInterceptor { +public class ApplicationContextInterceptor implements InstanceMethodsAroundInterceptor { - private static final ILog FILE_LOGGER = LogManager.getLogger(EventPublishingFinishedInterceptor.class); - private static final Logger LOGGER = LoggerFactory.getLogger(EventPublishingFinishedInterceptor.class); + private static final AtomicBoolean isExecuted = new AtomicBoolean(false); @Override public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { @@ -48,16 +44,22 @@ public class EventPublishingFinishedInterceptor implements InstanceMethodsAround @Override public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { - ConfigurableApplicationContext context = (ConfigurableApplicationContext) allArguments[0]; + // Since the refresh() method is a method of the AbstractApplicationContext class, + // the AbstractApplicationContext itself is an implementation class of the ApplicationContext. + // Therefore, can treat the class instance itself as an ApplicationContext object. + ConfigurableApplicationContext context = (ConfigurableApplicationContext) objInst; if (context.getParent() != null) { // After the child container is started, the thread pool registration will be carried out SpringThreadPoolRegisterSupport.registerThreadPoolInstances(context); return ret; } - SpringPropertiesLoader.loadSpringProperties(context.getEnvironment()); - ThreadPoolDynamicRefresh dynamicRefreshSpring1x = new DynamicThreadPoolChangeHandlerSpring1x(context); - dynamicRefreshSpring1x.registerListener(); - DubboThreadPoolAdapter.registerExecutors(); + // This logic will only be executed once + if (isExecuted.compareAndSet(false, true)) { + ApplicationContextHolder contextHolder = new ApplicationContextHolder(); + contextHolder.setApplicationContext(context); + SpringPropertiesLoader.loadSpringProperties(context.getEnvironment()); + AbstractSubjectCenter.register(AbstractSubjectCenter.SubjectType.THREAD_POOL_DYNAMIC_REFRESH, new DynamicThreadPoolRefreshListener()); + } return ret; } diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/resources/hippo4j-plugin.def b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/resources/hippo4j-plugin.def index 8bb37e3a..403de5ce 100644 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/resources/hippo4j-plugin.def +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/resources/hippo4j-plugin.def @@ -14,4 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -spring-boot-1.x=cn.hippo4j.agent.plugin.spring.boot.v1.define.EventPublishingRunListenerInstrumentation \ No newline at end of file +spring-boot-1.x=cn.hippo4j.agent.plugin.spring.boot.v1.define.ApplicationContextInstrumentation diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/interceptor/EventPublishingStartedInterceptor.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/interceptor/EventPublishingStartedInterceptor.java index 45423564..74faef32 100644 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/interceptor/EventPublishingStartedInterceptor.java +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/interceptor/EventPublishingStartedInterceptor.java @@ -17,29 +17,28 @@ package cn.hippo4j.agent.plugin.spring.boot.v2.interceptor; -import cn.hippo4j.agent.plugin.spring.boot.v2.NacosDynamicThreadPoolChangeHandlerSpring2x; -import cn.hippo4j.common.logging.api.ILog; -import cn.hippo4j.common.logging.api.LogManager; import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance; import cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; import cn.hippo4j.agent.core.plugin.interceptor.enhance.MethodInterceptResult; -import cn.hippo4j.agent.plugin.spring.boot.v2.DynamicThreadPoolChangeHandlerSpring2x; +import cn.hippo4j.agent.plugin.spring.common.event.DynamicThreadPoolRefreshListener; import cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader; import cn.hippo4j.agent.plugin.spring.common.support.SpringThreadPoolRegisterSupport; import cn.hippo4j.common.extension.design.AbstractSubjectCenter; -import cn.hippo4j.threadpool.dynamic.api.ThreadPoolDynamicRefresh; -import cn.hippo4j.threadpool.dynamic.mode.config.refresher.event.DynamicThreadPoolRefreshListener; -import lombok.extern.slf4j.Slf4j; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; +import cn.hippo4j.core.config.ApplicationContextHolder; import org.springframework.context.ConfigurableApplicationContext; import java.lang.reflect.Method; +import java.util.concurrent.atomic.AtomicBoolean; /** * Event publishing started interceptor */ -@Slf4j public class EventPublishingStartedInterceptor implements InstanceMethodsAroundInterceptor { + private static final AtomicBoolean isExecuted = new AtomicBoolean(false); + private static final ILog LOGGER = LogManager.getLogger(EventPublishingStartedInterceptor.class); @Override @@ -55,13 +54,15 @@ public class EventPublishingStartedInterceptor implements InstanceMethodsAroundI SpringThreadPoolRegisterSupport.registerThreadPoolInstances(context); return ret; } - SpringPropertiesLoader.loadSpringProperties(context.getEnvironment()); - // ThreadPoolDynamicRefresh dynamicRefresh = new DynamicThreadPoolChangeHandlerSpring2x(); - // TODO Nacos配置 - ThreadPoolDynamicRefresh dynamicRefresh = new NacosDynamicThreadPoolChangeHandlerSpring2x(); - dynamicRefresh.registerListener(); - AbstractSubjectCenter.register(AbstractSubjectCenter.SubjectType.THREAD_POOL_DYNAMIC_REFRESH, - new DynamicThreadPoolRefreshListener()); + // This logic will only be executed once + if (isExecuted.compareAndSet(false, true)) { + ApplicationContextHolder contextHolder = new ApplicationContextHolder(); + contextHolder.setApplicationContext(context); + // Load Spring Properties + SpringPropertiesLoader.loadSpringProperties(context.getEnvironment()); + // register Dynamic ThreadPool Refresh Listener + AbstractSubjectCenter.register(AbstractSubjectCenter.SubjectType.THREAD_POOL_DYNAMIC_REFRESH, new DynamicThreadPoolRefreshListener()); + } return ret; } diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml index c472c7b3..853e0706 100644 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml @@ -55,6 +55,11 @@ ${project.version} provided + + cn.hippo4j + hippo4j-threadpool-infra-common + ${project.version} + cn.hippo4j hippo4j-threadpool-monitor-elasticsearch 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 d9fbfac7..a73c800e 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 @@ -19,19 +19,24 @@ 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.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; /** @@ -41,6 +46,13 @@ 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(); @@ -54,13 +66,11 @@ public class SpringPropertiesLoader { } // Sort to ensure that the configuration in the configuration center is after the array // To get the latest configuration information - propertySourceList.sort((o1, o2) -> { - boolean o1Contains = o1.getName().toLowerCase().contains("apollo") || o1.getName().toLowerCase().contains("nacos"); - boolean o2Contains = (o2.getName().toLowerCase().contains("apollo") || o2.getName().toLowerCase().contains("nacos")); - return Boolean.compare(o1Contains, o2Contains); - }); + 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 = 0; i <= propertySourceList.size() - 1; i++) { + for (int i = propertySourceList.size() - 1; i >= 0; i--) { PropertySource propertySource = propertySourceList.get(i); if (!(propertySource instanceof EnumerablePropertySource)) { LOGGER.warn("Skip propertySource[{}] because {} not enumerable.", propertySource.getName(), propertySource.getClass()); @@ -87,6 +97,11 @@ public class SpringPropertiesLoader { // 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 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 index a5d0334a..4cbd54e3 100644 --- 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 @@ -17,11 +17,9 @@ package cn.hippo4j.agent.plugin.spring.common.support; -import cn.hippo4j.adapter.web.jetty.DefaultJettyWebThreadPoolHandler; 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.api.ThreadPoolConfigChange; import cn.hippo4j.common.propertie.EnvironmentProperties; import cn.hippo4j.core.config.ApplicationContextHolder; import cn.hippo4j.core.toolkit.IdentifyUtil; @@ -36,7 +34,6 @@ 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 cn.hippo4j.threadpool.message.core.service.ThreadPoolSendMessageService; import lombok.Getter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -65,6 +62,8 @@ public class ThreadPoolCheckAlarmSupport { @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}. *

@@ -92,11 +91,8 @@ public class ThreadPoolCheckAlarmSupport { // Initialize the alarm platform information initializeSendMessageHandlers(threadPoolBaseSendMessageService, alarmControlHandler); - // Initialize the thread pool check alarm handler with necessary services - DefaultThreadPoolCheckAlarmHandler checkAlarmHandler = new DefaultThreadPoolCheckAlarmHandler(threadPoolBaseSendMessageService); - - // Run the check alarm handler to start monitoring the thread pool - checkAlarmHandler.scheduleExecute(); + // Execute scheduled task to check an alarm + scheduleExecute(threadPoolBaseSendMessageService); } } @@ -134,7 +130,7 @@ public class ThreadPoolCheckAlarmSupport { * 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. + * @param alarmControlHandler The {@link AlarmControlHandler} used to handle alarms and notifications. */ private static void initializeSendMessageHandlers(ThreadPoolBaseSendMessageService threadPoolBaseSendMessageService, AlarmControlHandler alarmControlHandler) { // Initialize message handlers @@ -153,4 +149,17 @@ public class ThreadPoolCheckAlarmSupport { 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 index 3af1e1e6..d44dbf3d 100644 --- 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 @@ -19,7 +19,6 @@ package cn.hippo4j.agent.plugin.spring.common.support; import cn.hippo4j.agent.plugin.spring.common.monitor.MonitorHandlersConfigurator; import cn.hippo4j.agent.plugin.spring.common.monitor.MonitorMetricEndpoint; -import cn.hippo4j.common.executor.ThreadFactoryBuilder; import cn.hippo4j.common.executor.ThreadPoolExecutorRegistry; import cn.hippo4j.common.extension.spi.ServiceLoaderRegistry; import cn.hippo4j.common.monitor.MonitorCollectTypeEnum; @@ -27,6 +26,7 @@ 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; @@ -36,10 +36,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; -import java.util.Optional; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import static cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader.BOOTSTRAP_CONFIG_PROPERTIES; @@ -52,7 +52,16 @@ public class ThreadPoolMonitorSupport { private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolMonitorSupport.class); - private static List threadPoolMonitors; + /** + * 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 @@ -78,22 +87,19 @@ public class ThreadPoolMonitorSupport { 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())) { + if (Objects.isNull(monitor) || !monitor.getEnable() || StringUtil.isBlank(monitor.getThreadPoolTypes()) || StringUtil.isBlank(monitor.getCollectTypes())) { return; } LOGGER.info("[Hippo4j-Agent] Start monitoring the running status of dynamic thread pools."); - threadPoolMonitors = new ArrayList<>(); - ScheduledExecutorService collectScheduledExecutor = new ScheduledThreadPoolExecutor( - 1, - r -> new Thread(r, "client.agent.scheduled.collect.data")); // Initialize monitoring components for the dynamic thread pools MonitorHandlersConfigurator.initializeMonitorHandlers(monitor, (ConfigurableEnvironment) environment, threadPoolMonitors); + // 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())) { @@ -101,12 +107,9 @@ public class ThreadPoolMonitorSupport { } // Schedule periodic collection of metrics from the thread pools - collectScheduledExecutor.scheduleWithFixedDelay( - scheduleRunnable(), - monitor.getInitialDelay(), - monitor.getCollectInterval(), - TimeUnit.MILLISECONDS); + collectScheduledExecutor.scheduleWithFixedDelay(scheduleRunnable(), 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()); } 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 index 4491eb70..22a6bc62 100644 --- 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 @@ -72,6 +72,39 @@ public class SpringPropertyBinder { } } + /** + * 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. * 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..7a691206 --- /dev/null +++ b/examples/threadpool-example/agent/config-apollo-spring-boot-1x/src/main/resources/bootstrap.properties @@ -0,0 +1,53 @@ +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=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.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 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..7a691206 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,11 +1,13 @@ 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 @@ -14,26 +16,29 @@ 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.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 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..4291d582 --- /dev/null +++ b/examples/threadpool-example/agent/config-nacos-spring-boot-1x/src/main/resources/bootstrap.properties @@ -0,0 +1,60 @@ +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=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.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.server-addr=127.0.0.1:8848 +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 diff --git a/examples/threadpool-example/agent/config-nacos/pom.xml b/examples/threadpool-example/agent/config-nacos/pom.xml index 8134d488..068ff153 100644 --- a/examples/threadpool-example/agent/config-nacos/pom.xml +++ b/examples/threadpool-example/agent/config-nacos/pom.xml @@ -29,10 +29,15 @@ org.springframework.boot spring-boot-starter-web - + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + 2.2.5.RELEASE org.springframework.cloud diff --git a/examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/AgentConfigNacosExampleApplication.java b/examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/AgentConfigNacosExampleApplication.java index e5010fbd..1b315207 100644 --- a/examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/AgentConfigNacosExampleApplication.java +++ b/examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/AgentConfigNacosExampleApplication.java @@ -17,8 +17,6 @@ package cn.hippo4j.example.agent.config.nacos; -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.spring.context.annotation.config.EnableNacosConfig; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -26,10 +24,9 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; * Agent config Nacos example application. */ @SpringBootApplication(scanBasePackages = "cn.hippo4j.example.agent.core") -@EnableNacosConfig public class AgentConfigNacosExampleApplication { - public static void main(String[] args) throws NacosException { + 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 index f145868b..c9b6820e 100644 --- a/examples/threadpool-example/agent/config-nacos/src/main/resources/bootstrap.properties +++ b/examples/threadpool-example/agent/config-nacos/src/main/resources/bootstrap.properties @@ -4,8 +4,12 @@ server.servlet.context-path=/example nacos.config.auto-refresh=true nacos.config.bootstrap.enable=true # The following parameters are used for testing -nacos.config.server-addr=127.0.0.1:8848 -nacos.config.data-id=dynamic-threadpool-example-config + +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 @@ -13,18 +17,21 @@ 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.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.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.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.server-addr=127.0.0.1:8848 spring.dynamic.thread-pool.nacos.data-id=dynamic-threadpool-example-config spring.dynamic.thread-pool.nacos.group=DEFAULT_GROUP @@ -34,7 +41,7 @@ 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.nacos.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 diff --git a/examples/threadpool-example/agent/pom.xml b/examples/threadpool-example/agent/pom.xml index d215c979..fcbe2ce2 100644 --- a/examples/threadpool-example/agent/pom.xml +++ b/examples/threadpool-example/agent/pom.xml @@ -20,5 +20,7 @@ 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/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..0f6cba86 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,17 @@ 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. + * + * @see alarmNotifyExecutor + * @see asyncAlarmNotifyExecutor + */ + public void destroyScheduleExecute() { + alarmNotifyExecutor.shutdownNow(); + asyncAlarmNotifyExecutor.shutdownNow(); + } + }