From 4a31057ff794f5b3d2b924130e563d5ec9b7c007 Mon Sep 17 00:00:00 2001 From: Pan-YuJie <646836760@qq.com> Date: Sun, 15 Sep 2024 21:32:13 +0800 Subject: [PATCH] feat:Apollo Configuration Center Plugin Logic Adaptation --- .../apollo-plugin/pom.xml | 10 +++ .../ApolloDynamicThreadPoolChangeHandler.java | 87 +++++++++++++++++++ .../apollo/define/ApolloInstrumentation.java | 2 +- .../ApolloConfigConstructorInterceptor.java | 47 ++++++++++ ...nfigPropertiesLoaderCompletedListener.java | 35 ++++++++ ...ynamicThreadPoolChangeHandlerSpring1x.java | 71 --------------- .../agent/agent-example-core/pom.xml | 4 - .../config-apollo-spring-boot-1x/pom.xml | 83 ++++++++++++++++++ ...gApolloSpringBoot1xExampleApplication.java | 15 ++-- .../design/AbstractSubjectCenter.java | 16 +++- 10 files changed, 286 insertions(+), 84 deletions(-) create mode 100644 agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/ApolloDynamicThreadPoolChangeHandler.java create mode 100644 agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/interceptor/ApolloConfigConstructorInterceptor.java create mode 100644 agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/listeners/ApolloConfigPropertiesLoaderCompletedListener.java delete mode 100644 agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/DynamicThreadPoolChangeHandlerSpring1x.java create mode 100644 examples/threadpool-example/agent/config-apollo-spring-boot-1x/pom.xml rename agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/interceptor/DefaultConfigConstructorInterceptor.java => examples/threadpool-example/agent/config-apollo-spring-boot-1x/src/main/java/cn/hippo4j/example/agent/config/apollo/v1/AgentConfigApolloSpringBoot1xExampleApplication.java (62%) diff --git a/agent/hippo4j-agent-plugin/apollo-plugin/pom.xml b/agent/hippo4j-agent-plugin/apollo-plugin/pom.xml index 90f71b3d..67ddd9c6 100644 --- a/agent/hippo4j-agent-plugin/apollo-plugin/pom.xml +++ b/agent/hippo4j-agent-plugin/apollo-plugin/pom.xml @@ -16,6 +16,16 @@ + + cn.hippo4j + hippo4j-agent-spring-plugin-common + ${project.version} + + + cn.hippo4j + hippo4j-threadpool-dynamic-mode-config + ${project.version} + com.ctrip.framework.apollo apollo-client diff --git a/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/ApolloDynamicThreadPoolChangeHandler.java b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/ApolloDynamicThreadPoolChangeHandler.java new file mode 100644 index 00000000..d6e035ff --- /dev/null +++ b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/ApolloDynamicThreadPoolChangeHandler.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.agent.plugin.apollo; + +import cn.hippo4j.agent.plugin.spring.common.conf.SpringBootConfig; +import cn.hippo4j.agent.plugin.spring.common.toolkit.SpringPropertyBinder; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; +import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties; +import cn.hippo4j.threadpool.dynamic.mode.config.refresher.AbstractConfigThreadPoolDynamicRefresh; +import com.ctrip.framework.apollo.Config; +import com.ctrip.framework.apollo.ConfigChangeListener; +import com.ctrip.framework.apollo.ConfigFile; +import com.ctrip.framework.apollo.ConfigService; +import com.ctrip.framework.apollo.core.enums.ConfigFileFormat; +import com.ctrip.framework.apollo.model.ConfigChange; +import org.springframework.boot.context.properties.bind.Binder; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static cn.hippo4j.agent.core.conf.Constants.SPRING_BOOT_CONFIG_PREFIX; + +/** + * Dynamic thread pool change handler + */ +public class ApolloDynamicThreadPoolChangeHandler extends AbstractConfigThreadPoolDynamicRefresh { + + private static final ILog LOGGER = LogManager.getLogger(ApolloDynamicThreadPoolChangeHandler.class); + + /** + * Registers a listener with Apollo to monitor for changes in the thread pool configuration. + */ + @Override + public void registerListener() { + List apolloNamespaces = SpringBootConfig.Spring.Dynamic.Thread_Pool.Apollo.NAMESPACE; + String namespace = apolloNamespaces.get(0); + String configFileType = SpringBootConfig.Spring.Dynamic.Thread_Pool.CONFIG_FILE_TYPE; + Config config = ConfigService.getConfig(String.format("%s.%s", namespace, configFileType)); + ConfigChangeListener configChangeListener = configChangeEvent -> { + String replacedNamespace = namespace.replaceAll("." + configFileType, ""); + ConfigFileFormat configFileFormat = ConfigFileFormat.fromString(configFileType); + ConfigFile configFile = ConfigService.getConfigFile(replacedNamespace, configFileFormat); + Map newChangeValueMap = new HashMap<>(); + configChangeEvent.changedKeys().stream().filter(each -> each.contains(SPRING_BOOT_CONFIG_PREFIX)).forEach(each -> { + ConfigChange change = configChangeEvent.getChange(each); + String newValue = change.getNewValue(); + newChangeValueMap.put(each, newValue); + }); + dynamicRefresh(configFileType, configFile.getContent(), newChangeValueMap); + }; + config.addChangeListener(configChangeListener); + LOGGER.info("[Hippo4j-Agent] Dynamic thread pool refresher, add apollo listener success. namespace: {}", namespace); + } + + /** + * 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/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/define/ApolloInstrumentation.java b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/define/ApolloInstrumentation.java index bdf9caaa..76579b47 100644 --- a/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/define/ApolloInstrumentation.java +++ b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/define/ApolloInstrumentation.java @@ -34,7 +34,7 @@ public class ApolloInstrumentation extends ClassInstanceMethodsEnhancePluginDefi private static final String ENHANCE_CLASS = "com.ctrip.framework.apollo.internals.DefaultConfig"; - private static final String CONSTRUCTOR_INTERCEPT_CLASS = "cn.hippo4j.agent.plugin.apollo.interceptor.DefaultConfigConstructorInterceptor"; + private static final String CONSTRUCTOR_INTERCEPT_CLASS = "cn.hippo4j.agent.plugin.apollo.interceptor.ApolloConfigConstructorInterceptor"; @Override protected ClassMatch enhanceClass() { diff --git a/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/interceptor/ApolloConfigConstructorInterceptor.java b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/interceptor/ApolloConfigConstructorInterceptor.java new file mode 100644 index 00000000..16c3dced --- /dev/null +++ b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/interceptor/ApolloConfigConstructorInterceptor.java @@ -0,0 +1,47 @@ +/* + * 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.apollo.interceptor; + +import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import cn.hippo4j.agent.plugin.apollo.listeners.ApolloConfigPropertiesLoaderCompletedListener; +import cn.hippo4j.common.extension.design.AbstractSubjectCenter; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Default config constructor interceptor + */ +public class ApolloConfigConstructorInterceptor 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)) { + // The Apollo 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 ApolloConfigPropertiesLoaderCompletedListener()); + } + } +} diff --git a/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/listeners/ApolloConfigPropertiesLoaderCompletedListener.java b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/listeners/ApolloConfigPropertiesLoaderCompletedListener.java new file mode 100644 index 00000000..8bef9193 --- /dev/null +++ b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/listeners/ApolloConfigPropertiesLoaderCompletedListener.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.apollo.listeners; + +import cn.hippo4j.agent.plugin.apollo.ApolloDynamicThreadPoolChangeHandler; +import cn.hippo4j.common.extension.design.Observer; +import cn.hippo4j.common.extension.design.ObserverMessage; +import cn.hippo4j.threadpool.dynamic.api.ThreadPoolDynamicRefresh; + +/** + * Apollo Config Properties Loader Completed Listener + */ +public class ApolloConfigPropertiesLoaderCompletedListener implements Observer { + + @Override + public void accept(ObserverMessage observerMessage) { + ThreadPoolDynamicRefresh dynamicRefresh = new ApolloDynamicThreadPoolChangeHandler(); + dynamicRefresh.registerListener(); + } +} \ No newline at end of file diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/DynamicThreadPoolChangeHandlerSpring1x.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/DynamicThreadPoolChangeHandlerSpring1x.java deleted file mode 100644 index dc97aaa8..00000000 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/DynamicThreadPoolChangeHandlerSpring1x.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package cn.hippo4j.agent.plugin.spring.boot.v1; - -import cn.hippo4j.common.toolkit.MapUtil; -import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties; -import cn.hippo4j.threadpool.dynamic.mode.config.refresher.AbstractConfigThreadPoolDynamicRefresh; -import lombok.RequiredArgsConstructor; -import org.springframework.beans.PropertyValues; -import org.springframework.beans.support.ResourceEditorRegistrar; -import org.springframework.boot.bind.CustomPropertyNamePatternsMatcher; -import org.springframework.boot.bind.RelaxedDataBinder; -import org.springframework.boot.bind.RelaxedNames; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.core.env.MapPropertySource; -import org.springframework.core.env.MutablePropertySources; - -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import static cn.hippo4j.config.springboot1x.starter.refresher.SpringBoot1xBootstrapConfigPropertiesBinderAdapter.getNames; - -/** - * Dynamic thread pool change handler spring 1x - */ -@RequiredArgsConstructor -public class DynamicThreadPoolChangeHandlerSpring1x extends AbstractConfigThreadPoolDynamicRefresh { - - private final ConfigurableApplicationContext applicationContext; - - @Override - public BootstrapConfigProperties buildBootstrapProperties(Map configInfo) { - BootstrapConfigProperties bindableCoreProperties = new BootstrapConfigProperties(); - if (MapUtil.isEmpty(configInfo)) { - return bindableCoreProperties; - } - RelaxedNames relaxedNames = new RelaxedNames(BootstrapConfigProperties.PREFIX); - Set names = getNames(bindableCoreProperties, relaxedNames); - Map stringConfigInfo = new HashMap<>(configInfo.size()); - configInfo.forEach((key, value) -> stringConfigInfo.put(key.toString(), value)); - MapPropertySource test = new MapPropertySource("Hippo4j", stringConfigInfo); - MutablePropertySources propertySources = new MutablePropertySources(); - propertySources.addFirst(test); - PropertyValues propertyValues = CustomPropertyNamePatternsMatcher.getPropertySourcesPropertyValues(names, propertySources); - RelaxedDataBinder dataBinder = new RelaxedDataBinder(bindableCoreProperties, BootstrapConfigProperties.PREFIX); - dataBinder.setAutoGrowCollectionLimit(Integer.MAX_VALUE); - dataBinder.setIgnoreNestedProperties(false); - dataBinder.setIgnoreInvalidFields(false); - dataBinder.setIgnoreUnknownFields(true); - ResourceEditorRegistrar resourceEditorRegistrar = new ResourceEditorRegistrar(applicationContext, applicationContext.getEnvironment()); - resourceEditorRegistrar.registerCustomEditors(dataBinder); - dataBinder.bind(propertyValues); - return bindableCoreProperties; - } -} diff --git a/examples/threadpool-example/agent/agent-example-core/pom.xml b/examples/threadpool-example/agent/agent-example-core/pom.xml index abc4d722..3feb4a56 100644 --- a/examples/threadpool-example/agent/agent-example-core/pom.xml +++ b/examples/threadpool-example/agent/agent-example-core/pom.xml @@ -16,10 +16,6 @@ - - org.springframework.boot - spring-boot-starter - org.springframework.boot spring-boot-starter-test 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/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/interceptor/DefaultConfigConstructorInterceptor.java b/examples/threadpool-example/agent/config-apollo-spring-boot-1x/src/main/java/cn/hippo4j/example/agent/config/apollo/v1/AgentConfigApolloSpringBoot1xExampleApplication.java similarity index 62% 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-apollo-spring-boot-1x/src/main/java/cn/hippo4j/example/agent/config/apollo/v1/AgentConfigApolloSpringBoot1xExampleApplication.java index e21049a6..26896afd 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-apollo-spring-boot-1x/src/main/java/cn/hippo4j/example/agent/config/apollo/v1/AgentConfigApolloSpringBoot1xExampleApplication.java @@ -15,17 +15,18 @@ * limitations under the License. */ -package cn.hippo4j.agent.plugin.apollo.interceptor; +package cn.hippo4j.example.agent.config.apollo.v1; -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 AgentConfigApolloSpringBoot1xExampleApplication { - @Override - public void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable { + public static void main(String[] args) { + SpringApplication.run(AgentConfigApolloSpringBoot1xExampleApplication.class, args); } } 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 } }