From 4c62f6ce95efe7b63dbee98c705259cbffea79ea Mon Sep 17 00:00:00 2001 From: "chen.ma" Date: Tue, 1 Nov 2022 22:29:09 +0800 Subject: [PATCH 01/18] Adjust the conditions for nominating core developers --- docs/docs/community/developer.md | 2 +- .../current/community/developer.md | 2 +- .../version-1.4.2/community/developer.md | 2 +- docs/versioned_docs/version-1.4.2/community/developer.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/docs/community/developer.md b/docs/docs/community/developer.md index 3979b12f..03cdd4e3 100644 --- a/docs/docs/community/developer.md +++ b/docs/docs/community/developer.md @@ -65,7 +65,7 @@ sidebar_position: 2 ## 成为核心开发者 -持续对 Hippo-4J 进行贡献, 粗略评估,完成 10 次 PR 贡献即可成为核心开发者。 其中包括完成 2 个 [good pro issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+pro+issue%22) 或以上,以及 6 个 [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) 或以上。 +持续对 Hippo-4J 进行贡献, 粗略评估,完成 10 次 PR 贡献即可成为核心开发者。 其中包括完成 2 个 [good pro issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+pro+issue%22) 或以上,以及 若干个 [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)。 :::note 会根据 PR 质量提供个性化评估,有可能一个或两个质量较高 PR 即可成为核心开发者。参考:[重构 DynamicThreadPoolExecutor 功能扩展逻辑](https://github.com/opengoofy/hippo4j/pull/854) diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/community/developer.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/community/developer.md index 3979b12f..03cdd4e3 100644 --- a/docs/i18n/zh/docusaurus-plugin-content-docs/current/community/developer.md +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/community/developer.md @@ -65,7 +65,7 @@ sidebar_position: 2 ## 成为核心开发者 -持续对 Hippo-4J 进行贡献, 粗略评估,完成 10 次 PR 贡献即可成为核心开发者。 其中包括完成 2 个 [good pro issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+pro+issue%22) 或以上,以及 6 个 [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) 或以上。 +持续对 Hippo-4J 进行贡献, 粗略评估,完成 10 次 PR 贡献即可成为核心开发者。 其中包括完成 2 个 [good pro issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+pro+issue%22) 或以上,以及 若干个 [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)。 :::note 会根据 PR 质量提供个性化评估,有可能一个或两个质量较高 PR 即可成为核心开发者。参考:[重构 DynamicThreadPoolExecutor 功能扩展逻辑](https://github.com/opengoofy/hippo4j/pull/854) diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/community/developer.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/community/developer.md index 3979b12f..03cdd4e3 100644 --- a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/community/developer.md +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/community/developer.md @@ -65,7 +65,7 @@ sidebar_position: 2 ## 成为核心开发者 -持续对 Hippo-4J 进行贡献, 粗略评估,完成 10 次 PR 贡献即可成为核心开发者。 其中包括完成 2 个 [good pro issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+pro+issue%22) 或以上,以及 6 个 [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) 或以上。 +持续对 Hippo-4J 进行贡献, 粗略评估,完成 10 次 PR 贡献即可成为核心开发者。 其中包括完成 2 个 [good pro issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+pro+issue%22) 或以上,以及 若干个 [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)。 :::note 会根据 PR 质量提供个性化评估,有可能一个或两个质量较高 PR 即可成为核心开发者。参考:[重构 DynamicThreadPoolExecutor 功能扩展逻辑](https://github.com/opengoofy/hippo4j/pull/854) diff --git a/docs/versioned_docs/version-1.4.2/community/developer.md b/docs/versioned_docs/version-1.4.2/community/developer.md index 3979b12f..03cdd4e3 100644 --- a/docs/versioned_docs/version-1.4.2/community/developer.md +++ b/docs/versioned_docs/version-1.4.2/community/developer.md @@ -65,7 +65,7 @@ sidebar_position: 2 ## 成为核心开发者 -持续对 Hippo-4J 进行贡献, 粗略评估,完成 10 次 PR 贡献即可成为核心开发者。 其中包括完成 2 个 [good pro issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+pro+issue%22) 或以上,以及 6 个 [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) 或以上。 +持续对 Hippo-4J 进行贡献, 粗略评估,完成 10 次 PR 贡献即可成为核心开发者。 其中包括完成 2 个 [good pro issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+pro+issue%22) 或以上,以及 若干个 [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)。 :::note 会根据 PR 质量提供个性化评估,有可能一个或两个质量较高 PR 即可成为核心开发者。参考:[重构 DynamicThreadPoolExecutor 功能扩展逻辑](https://github.com/opengoofy/hippo4j/pull/854) From c423ffa4483f89d381fe1764fa43ed31494643e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E7=A7=B0=20Ma=20Chen?= Date: Tue, 1 Nov 2022 23:18:31 +0800 Subject: [PATCH 02/18] Client instance registration information code refactoring (#888) (#889) --- .../config/DiscoveryConfiguration.java | 49 +---------- .../provider/InstanceInfoProviderFactory.java | 83 +++++++++++++++++++ 2 files changed, 85 insertions(+), 47 deletions(-) create mode 100644 hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/provider/InstanceInfoProviderFactory.java diff --git a/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/config/DiscoveryConfiguration.java b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/config/DiscoveryConfiguration.java index 0449b241..5a5c47a1 100644 --- a/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/config/DiscoveryConfiguration.java +++ b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/config/DiscoveryConfiguration.java @@ -17,26 +17,15 @@ package cn.hippo4j.springboot.starter.config; -import cn.hippo4j.common.api.ClientNetworkService; import cn.hippo4j.common.model.InstanceInfo; -import cn.hippo4j.common.spi.DynamicThreadPoolServiceLoader; -import cn.hippo4j.common.toolkit.ContentUtil; -import cn.hippo4j.core.toolkit.IdentifyUtil; import cn.hippo4j.core.toolkit.inet.InetUtils; import cn.hippo4j.springboot.starter.core.DiscoveryClient; +import cn.hippo4j.springboot.starter.provider.InstanceInfoProviderFactory; import cn.hippo4j.springboot.starter.remote.HttpAgent; -import cn.hippo4j.springboot.starter.toolkit.CloudCommonIdUtil; import lombok.AllArgsConstructor; -import lombok.SneakyThrows; import org.springframework.context.annotation.Bean; import org.springframework.core.env.ConfigurableEnvironment; -import java.net.InetAddress; -import java.util.Optional; - -import static cn.hippo4j.common.constant.Constants.IDENTIFY_SLICER_SYMBOL; -import static cn.hippo4j.core.toolkit.IdentifyUtil.CLIENT_IDENTIFICATION_VALUE; - /** * Dynamic thread-pool discovery config. */ @@ -49,43 +38,9 @@ public class DiscoveryConfiguration { private final InetUtils hippo4JInetUtils; - static { - DynamicThreadPoolServiceLoader.register(ClientNetworkService.class); - } - @Bean - @SneakyThrows public InstanceInfo instanceConfig() { - String namespace = bootstrapProperties.getNamespace(); - String itemId = bootstrapProperties.getItemId(); - String port = environment.getProperty("server.port", "8080"); - String applicationName = environment.getProperty("spring.dynamic.thread-pool.item-id"); - String active = environment.getProperty("spring.profiles.active", "UNKNOWN"); - InstanceInfo instanceInfo = new InstanceInfo(); - String instanceId = CloudCommonIdUtil.getDefaultInstanceId(environment, hippo4JInetUtils); - instanceId = new StringBuilder() - .append(instanceId) - .append(IDENTIFY_SLICER_SYMBOL) - .append(CLIENT_IDENTIFICATION_VALUE) - .toString(); - String contextPath = environment.getProperty("server.servlet.context-path", ""); - instanceInfo.setInstanceId(instanceId) - .setIpApplicationName(CloudCommonIdUtil.getIpApplicationName(environment, hippo4JInetUtils)) - .setHostName(InetAddress.getLocalHost().getHostAddress()) - .setAppName(applicationName) - .setPort(port) - .setClientBasePath(contextPath) - .setGroupKey(ContentUtil.getGroupKey(itemId, namespace)); - String[] customerNetwork = DynamicThreadPoolServiceLoader.getSingletonServiceInstances(ClientNetworkService.class) - .stream().findFirst().map(each -> each.getNetworkIpPort(environment)).orElse(null); - String callBackUrl = new StringBuilder().append(Optional.ofNullable(customerNetwork).map(each -> each[0]).orElse(instanceInfo.getHostName())).append(":") - .append(Optional.ofNullable(customerNetwork).map(each -> each[1]).orElse(port)).append(instanceInfo.getClientBasePath()) - .toString(); - instanceInfo.setCallBackUrl(callBackUrl); - String identify = IdentifyUtil.generate(environment, hippo4JInetUtils); - instanceInfo.setIdentify(identify); - instanceInfo.setActive(active.toUpperCase()); - return instanceInfo; + return InstanceInfoProviderFactory.getInstance(environment, bootstrapProperties, hippo4JInetUtils); } @Bean diff --git a/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/provider/InstanceInfoProviderFactory.java b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/provider/InstanceInfoProviderFactory.java new file mode 100644 index 00000000..41a20e9e --- /dev/null +++ b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/provider/InstanceInfoProviderFactory.java @@ -0,0 +1,83 @@ +/* + * 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.springboot.starter.provider; + +import cn.hippo4j.common.api.ClientNetworkService; +import cn.hippo4j.common.model.InstanceInfo; +import cn.hippo4j.common.spi.DynamicThreadPoolServiceLoader; +import cn.hippo4j.common.toolkit.ContentUtil; +import cn.hippo4j.core.toolkit.IdentifyUtil; +import cn.hippo4j.core.toolkit.inet.InetUtils; +import cn.hippo4j.springboot.starter.config.BootstrapProperties; +import cn.hippo4j.springboot.starter.toolkit.CloudCommonIdUtil; +import lombok.SneakyThrows; +import org.springframework.core.env.ConfigurableEnvironment; + +import java.net.InetAddress; +import java.util.Optional; + +import static cn.hippo4j.common.constant.Constants.IDENTIFY_SLICER_SYMBOL; +import static cn.hippo4j.core.toolkit.IdentifyUtil.CLIENT_IDENTIFICATION_VALUE; + +/** + * Instance info provider factory. + */ +public final class InstanceInfoProviderFactory { + + static { + DynamicThreadPoolServiceLoader.register(ClientNetworkService.class); + } + + /** + * Create client registration instance information. + * + * @param environment configurable environment + * @param bootstrapProperties bootstrap properties + * @param inetUtils inet utils + * @return + */ + @SneakyThrows + public static InstanceInfo getInstance(final ConfigurableEnvironment environment, + final BootstrapProperties bootstrapProperties, + final InetUtils inetUtils) { + String namespace = bootstrapProperties.getNamespace(); + String itemId = bootstrapProperties.getItemId(); + String port = environment.getProperty("server.port", "8080"); + String applicationName = environment.getProperty("spring.dynamic.thread-pool.item-id"); + String active = environment.getProperty("spring.profiles.active", "UNKNOWN"); + InstanceInfo instanceInfo = new InstanceInfo(); + String instanceId = CloudCommonIdUtil.getDefaultInstanceId(environment, inetUtils); + instanceId = new StringBuilder() + .append(instanceId).append(IDENTIFY_SLICER_SYMBOL).append(CLIENT_IDENTIFICATION_VALUE).toString(); + String contextPath = environment.getProperty("server.servlet.context-path", ""); + instanceInfo.setInstanceId(instanceId) + .setIpApplicationName(CloudCommonIdUtil.getIpApplicationName(environment, inetUtils)) + .setHostName(InetAddress.getLocalHost().getHostAddress()).setAppName(applicationName) + .setPort(port).setClientBasePath(contextPath).setGroupKey(ContentUtil.getGroupKey(itemId, namespace)); + String[] customerNetwork = DynamicThreadPoolServiceLoader.getSingletonServiceInstances(ClientNetworkService.class) + .stream().findFirst().map(each -> each.getNetworkIpPort(environment)).orElse(null); + String callBackUrl = new StringBuilder().append(Optional.ofNullable(customerNetwork).map(each -> each[0]).orElse(instanceInfo.getHostName())).append(":") + .append(Optional.ofNullable(customerNetwork).map(each -> each[1]).orElse(port)).append(instanceInfo.getClientBasePath()) + .toString(); + instanceInfo.setCallBackUrl(callBackUrl); + String identify = IdentifyUtil.generate(environment, inetUtils); + instanceInfo.setIdentify(identify); + instanceInfo.setActive(active.toUpperCase()); + return instanceInfo; + } +} From 563d50978746582c5a40fe00c89e3fc22f6bfdc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E7=A7=B0=20Ma=20Chen?= Date: Tue, 1 Nov 2022 23:25:05 +0800 Subject: [PATCH 03/18] Add method and field annotations (#890) --- .../starter/toolkit/CloudCommonIdUtil.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/toolkit/CloudCommonIdUtil.java b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/toolkit/CloudCommonIdUtil.java index 90de193d..53c79ff4 100644 --- a/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/toolkit/CloudCommonIdUtil.java +++ b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/toolkit/CloudCommonIdUtil.java @@ -26,14 +26,31 @@ import org.springframework.core.env.PropertyResolver; */ public class CloudCommonIdUtil { + /** + * Splice target information separator + */ private static final String SEPARATOR = ":"; + /** + * Get client ip port. + * + * @param resolver resolver + * @param inetUtils inet utils + * @return ip and port + */ public static String getClientIpPort(PropertyResolver resolver, InetUtils inetUtils) { String hostname = inetUtils.findFirstNonLoopBackHostInfo().getIpAddress(); String port = resolver.getProperty("server.port", "8080"); return combineParts(hostname, SEPARATOR, port); } + /** + * Get default instance id. + * + * @param resolver resolver + * @param inetUtils inet utils + * @return default instance id + */ @SneakyThrows public static String getDefaultInstanceId(PropertyResolver resolver, InetUtils inetUtils) { String namePart = getIpApplicationName(resolver, inetUtils); @@ -41,6 +58,13 @@ public class CloudCommonIdUtil { return combineParts(namePart, SEPARATOR, indexPart); } + /** + * Get ip application name. + * + * @param resolver resolver + * @param inetUtils inet utils + * @return ip application name + */ @SneakyThrows public static String getIpApplicationName(PropertyResolver resolver, InetUtils inetUtils) { String hostname = inetUtils.findFirstNonLoopBackHostInfo().getIpAddress(); @@ -48,6 +72,14 @@ public class CloudCommonIdUtil { return combineParts(hostname, SEPARATOR, appName); } + /** + * Combine parts. + * + * @param firstPart first part + * @param separator separator + * @param secondPart second part + * @return combined + */ public static String combineParts(String firstPart, String separator, String secondPart) { String combined = null; if (firstPart != null && secondPart != null) { From b6eda7b93e197cd97d50033fb696c31020218a61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E7=A7=B0=20Ma=20Chen?= Date: Wed, 2 Nov 2022 01:19:21 +0800 Subject: [PATCH 04/18] Supplementary Methods Notes (#891) * Add method and field annotations * Supplementary Methods Notes * Change the thread pool running data assignment --- .../common/model/ThreadPoolRunStateInfo.java | 6 ++ .../executor/DynamicThreadPoolWrapper.java | 4 +- .../state/AbstractThreadPoolRuntime.java | 84 +++++++------------ .../state/ThreadPoolRunStateHandler.java | 4 +- .../state/ThreadPoolStatusHandler.java | 6 +- .../AbstractBuildThreadPoolTemplate.java | 35 +++++++- .../adpter/DynamicThreadPoolAdapter.java | 22 +++-- .../DynamicThreadPoolAdapterChoose.java | 21 +++-- .../AbstractDynamicThreadPoolService.java | 4 +- .../service/DynamicThreadPoolService.java | 4 +- ...mmonDynamicThreadPoolProviderFactory.java} | 13 ++- .../DynamicThreadPoolPostProcessor.java | 4 +- .../DynamicThreadPoolPostProcessor.java | 10 ++- 13 files changed, 125 insertions(+), 92 deletions(-) rename hippo4j-core/src/main/java/cn/hippo4j/core/{executor/support/CommonDynamicThreadPool.java => provider/CommonDynamicThreadPoolProviderFactory.java} (80%) diff --git a/hippo4j-common/src/main/java/cn/hippo4j/common/model/ThreadPoolRunStateInfo.java b/hippo4j-common/src/main/java/cn/hippo4j/common/model/ThreadPoolRunStateInfo.java index f1b04ae3..77535aca 100644 --- a/hippo4j-common/src/main/java/cn/hippo4j/common/model/ThreadPoolRunStateInfo.java +++ b/hippo4j-common/src/main/java/cn/hippo4j/common/model/ThreadPoolRunStateInfo.java @@ -17,7 +17,10 @@ package cn.hippo4j.common.model; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; import java.io.Serializable; @@ -27,6 +30,9 @@ import java.io.Serializable; */ @Getter @Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor public class ThreadPoolRunStateInfo extends ThreadPoolBaseInfo implements Serializable { /** diff --git a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/DynamicThreadPoolWrapper.java b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/DynamicThreadPoolWrapper.java index 8f4d2941..964d6318 100644 --- a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/DynamicThreadPoolWrapper.java +++ b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/DynamicThreadPoolWrapper.java @@ -17,7 +17,7 @@ package cn.hippo4j.core.executor; -import cn.hippo4j.core.executor.support.CommonDynamicThreadPool; +import cn.hippo4j.core.provider.CommonDynamicThreadPoolProviderFactory; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -63,7 +63,7 @@ public class DynamicThreadPoolWrapper implements DisposableBean { private ThreadPoolExecutor executor; public DynamicThreadPoolWrapper(String threadPoolId) { - this(threadPoolId, CommonDynamicThreadPool.getInstance(threadPoolId)); + this(threadPoolId, CommonDynamicThreadPoolProviderFactory.getInstance(threadPoolId)); } public DynamicThreadPoolWrapper(String threadPoolId, ThreadPoolExecutor threadPoolExecutor) { diff --git a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/state/AbstractThreadPoolRuntime.java b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/state/AbstractThreadPoolRuntime.java index 6287b24a..8d9ca68f 100644 --- a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/state/AbstractThreadPoolRuntime.java +++ b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/state/AbstractThreadPoolRuntime.java @@ -35,18 +35,18 @@ import java.util.concurrent.ThreadPoolExecutor; public abstract class AbstractThreadPoolRuntime { /** - * Supplement. + * Supplemental thread pool runtime information. * - * @param threadPoolRunStateInfo - * @return + * @param threadPoolRunStateInfo thread-pool run state info + * @return thread-pool run state info */ public abstract ThreadPoolRunStateInfo supplement(ThreadPoolRunStateInfo threadPoolRunStateInfo); /** * Get pool run state. * - * @param threadPoolId - * @return + * @param threadPoolId thread-pool id + * @return thread-pool run state info */ public ThreadPoolRunStateInfo getPoolRunState(String threadPoolId) { DynamicThreadPoolWrapper executorService = GlobalThreadPoolManage.getExecutorService(threadPoolId); @@ -57,56 +57,34 @@ public abstract class AbstractThreadPoolRuntime { /** * Get pool run state. * - * @param threadPoolId - * @param executor - * @return + * @param threadPoolId thread-pool id + * @param executor executor + * @return thread-pool run state info */ public ThreadPoolRunStateInfo getPoolRunState(String threadPoolId, Executor executor) { - ThreadPoolRunStateInfo stateInfo = new ThreadPoolRunStateInfo(); - ThreadPoolExecutor pool = (ThreadPoolExecutor) executor; - // 核心线程数 - int corePoolSize = pool.getCorePoolSize(); - // 最大线程数 - int maximumPoolSize = pool.getMaximumPoolSize(); - // 线程池当前线程数 (有锁) - int poolSize = pool.getPoolSize(); - // 活跃线程数 (有锁) - int activeCount = pool.getActiveCount(); - // 同时进入池中的最大线程数 (有锁) - int largestPoolSize = pool.getLargestPoolSize(); - // 线程池中执行任务总数量 (有锁) - long completedTaskCount = pool.getCompletedTaskCount(); - // 当前负载 - String currentLoad = CalculateUtil.divide(activeCount, maximumPoolSize) + ""; - // 峰值负载 - String peakLoad = CalculateUtil.divide(largestPoolSize, maximumPoolSize) + ""; - BlockingQueue queue = pool.getQueue(); - // 队列元素个数 - int queueSize = queue.size(); - // 队列类型 - String queueType = queue.getClass().getSimpleName(); - // 队列剩余容量 - int remainingCapacity = queue.remainingCapacity(); - // 队列容量 - int queueCapacity = queueSize + remainingCapacity; - stateInfo.setCoreSize(corePoolSize); - stateInfo.setTpId(threadPoolId); - stateInfo.setPoolSize(poolSize); - stateInfo.setMaximumSize(maximumPoolSize); - stateInfo.setActiveSize(activeCount); - stateInfo.setCurrentLoad(currentLoad); - stateInfo.setPeakLoad(peakLoad); - stateInfo.setQueueType(queueType); - stateInfo.setQueueSize(queueSize); - stateInfo.setQueueCapacity(queueCapacity); - stateInfo.setQueueRemainingCapacity(remainingCapacity); - stateInfo.setLargestPoolSize(largestPoolSize); - stateInfo.setCompletedTaskCount(completedTaskCount); - long rejectCount = - pool instanceof DynamicThreadPoolExecutor ? ((DynamicThreadPoolExecutor) pool).getRejectCountNum() : -1L; - stateInfo.setRejectCount(rejectCount); - stateInfo.setClientLastRefreshTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); - stateInfo.setTimestamp(System.currentTimeMillis()); + ThreadPoolExecutor actualExecutor = (ThreadPoolExecutor) executor; + int activeCount = actualExecutor.getActiveCount(); + int largestPoolSize = actualExecutor.getLargestPoolSize(); + BlockingQueue blockingQueue = actualExecutor.getQueue(); + long rejectCount = actualExecutor instanceof DynamicThreadPoolExecutor ? ((DynamicThreadPoolExecutor) actualExecutor).getRejectCountNum() : -1L; + ThreadPoolRunStateInfo stateInfo = ThreadPoolRunStateInfo.builder() + .tpId(threadPoolId) + .activeSize(activeCount) + .poolSize(actualExecutor.getPoolSize()) + .completedTaskCount(actualExecutor.getCompletedTaskCount()) + .largestPoolSize(largestPoolSize) + .currentLoad(CalculateUtil.divide(activeCount, actualExecutor.getMaximumPoolSize()) + "") + .clientLastRefreshTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))) + .peakLoad(CalculateUtil.divide(largestPoolSize, actualExecutor.getMaximumPoolSize()) + "") + .queueSize(blockingQueue.size()) + .queueRemainingCapacity(blockingQueue.remainingCapacity()) + .rejectCount(rejectCount) + .timestamp(System.currentTimeMillis()) + .build(); + stateInfo.setCoreSize(actualExecutor.getCorePoolSize()); + stateInfo.setMaximumSize(actualExecutor.getMaximumPoolSize()); + stateInfo.setQueueType(blockingQueue.getClass().getSimpleName()); + stateInfo.setQueueCapacity(blockingQueue.size() + blockingQueue.remainingCapacity()); return supplement(stateInfo); } } diff --git a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/state/ThreadPoolRunStateHandler.java b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/state/ThreadPoolRunStateHandler.java index a0405a6e..9e4ab577 100644 --- a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/state/ThreadPoolRunStateHandler.java +++ b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/state/ThreadPoolRunStateHandler.java @@ -50,9 +50,9 @@ public class ThreadPoolRunStateHandler extends AbstractThreadPoolRuntime { long used = MemoryUtil.heapMemoryUsed(); long max = MemoryUtil.heapMemoryMax(); String memoryProportion = StringUtil.newBuilder( - "已分配: ", + "Allocation: ", ByteConvertUtil.getPrintSize(used), - " / 最大可用: ", + " / Maximum available: ", ByteConvertUtil.getPrintSize(max)); poolRunStateInfo.setCurrentLoad(poolRunStateInfo.getCurrentLoad() + "%"); poolRunStateInfo.setPeakLoad(poolRunStateInfo.getPeakLoad() + "%"); diff --git a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/state/ThreadPoolStatusHandler.java b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/state/ThreadPoolStatusHandler.java index fc9c8f2e..30bad4a2 100644 --- a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/state/ThreadPoolStatusHandler.java +++ b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/state/ThreadPoolStatusHandler.java @@ -40,10 +40,10 @@ public class ThreadPoolStatusHandler { private static final AtomicBoolean EXCEPTION_FLAG = new AtomicBoolean(Boolean.TRUE); /** - * Get thread pool state. + * Get thread-pool state. * - * @param executor - * @return + * @param executor executor + * @return thread-pool state */ public static String getThreadPoolState(ThreadPoolExecutor executor) { if (EXCEPTION_FLAG.get()) { diff --git a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/AbstractBuildThreadPoolTemplate.java b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/AbstractBuildThreadPoolTemplate.java index 4d002f8c..8f463319 100644 --- a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/AbstractBuildThreadPoolTemplate.java +++ b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/AbstractBuildThreadPoolTemplate.java @@ -34,19 +34,30 @@ import java.util.concurrent.*; public class AbstractBuildThreadPoolTemplate { /** - * Thread pool construction initialization parameters. + * Thread-pool construction initialization parameters. * - * @return + * @return thread-pool init param */ protected static ThreadPoolInitParam initParam() { throw new UnsupportedOperationException(); } + /** + * Build pool. + * + * @return thread-pool executor + */ public static ThreadPoolExecutor buildPool() { ThreadPoolInitParam initParam = initParam(); return buildPool(initParam); } + /** + * Build pool. + * + * @param initParam init param + * @return thread-pool executor + */ public static ThreadPoolExecutor buildPool(ThreadPoolInitParam initParam) { Assert.notNull(initParam); ThreadPoolExecutor executorService; @@ -65,11 +76,22 @@ public class AbstractBuildThreadPoolTemplate { return executorService; } + /** + * Build a fast-consuming task thread pool. + * + * @return fast thread-pool executor + */ public static ThreadPoolExecutor buildFastPool() { ThreadPoolInitParam initParam = initParam(); return buildFastPool(initParam); } + /** + * Build a fast-consuming task thread pool. + * + * @param initParam init param + * @return fast thread-pool executor + */ public static ThreadPoolExecutor buildFastPool(ThreadPoolInitParam initParam) { TaskQueue taskQueue = new TaskQueue(initParam.getCapacity()); FastThreadPoolExecutor fastThreadPoolExecutor; @@ -89,6 +111,12 @@ public class AbstractBuildThreadPoolTemplate { return fastThreadPoolExecutor; } + /** + * Build a dynamic monitor thread-pool. + * + * @param initParam init param + * @return dynamic monitor thread-pool + */ public static DynamicThreadPoolExecutor buildDynamicPool(ThreadPoolInitParam initParam) { Assert.notNull(initParam); DynamicThreadPoolExecutor dynamicThreadPoolExecutor; @@ -113,6 +141,9 @@ public class AbstractBuildThreadPoolTemplate { return dynamicThreadPoolExecutor; } + /** + * Thread-pool init param. + */ @Data @Accessors(chain = true) public static class ThreadPoolInitParam { diff --git a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/adpter/DynamicThreadPoolAdapter.java b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/adpter/DynamicThreadPoolAdapter.java index b16f3d74..54f97449 100644 --- a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/adpter/DynamicThreadPoolAdapter.java +++ b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/adpter/DynamicThreadPoolAdapter.java @@ -27,26 +27,30 @@ import java.util.concurrent.Executor; public interface DynamicThreadPoolAdapter { /** - * Match. + * Check if the object contains thread pool information. * - * @param executor - * @return + * @param executor objects where there may be instances + * of dynamic thread pools + * @return matching results */ boolean match(Object executor); /** - * Unwrap. + * Get the dynamic thread pool reference in the object. * - * @param executor - * @return + * @param executor objects where there may be instances + * of dynamic thread pools + * @return get the real dynamic thread pool instance */ DynamicThreadPoolExecutor unwrap(Object executor); /** - * Replace. + * If the {@link DynamicThreadPoolAdapter#match(Object)} conditions are met, + * the thread pool is replaced with a dynamic thread pool. * - * @param executor - * @param dynamicThreadPoolExecutor + * @param executor objects where there may be instances + * of dynamic thread pools + * @param dynamicThreadPoolExecutor dynamic thread-pool executor */ void replace(Object executor, Executor dynamicThreadPoolExecutor); } diff --git a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/adpter/DynamicThreadPoolAdapterChoose.java b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/adpter/DynamicThreadPoolAdapterChoose.java index 1b613483..64ac174c 100644 --- a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/adpter/DynamicThreadPoolAdapterChoose.java +++ b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/adpter/DynamicThreadPoolAdapterChoose.java @@ -38,20 +38,22 @@ public class DynamicThreadPoolAdapterChoose { } /** - * Match. + * Check if the object contains thread pool information. * - * @param executor - * @return + * @param executor objects where there may be instances + * of dynamic thread pools + * @return matching results */ public static boolean match(Object executor) { return DYNAMIC_THREAD_POOL_ADAPTERS.stream().anyMatch(each -> each.match(executor)); } /** - * Unwrap. + * Get the dynamic thread pool reference in the object. * - * @param executor - * @return + * @param executor objects where there may be instances + * of dynamic thread pools + * @return get the real dynamic thread pool instance */ public static DynamicThreadPoolExecutor unwrap(Object executor) { Optional dynamicThreadPoolAdapterOptional = DYNAMIC_THREAD_POOL_ADAPTERS.stream().filter(each -> each.match(executor)).findFirst(); @@ -59,9 +61,12 @@ public class DynamicThreadPoolAdapterChoose { } /** - * Replace. + * If the {@link DynamicThreadPoolAdapter#match(Object)} conditions are met, + * the thread pool is replaced with a dynamic thread pool. * - * @param executor + * @param executor objects where there may be instances + * of dynamic thread pools + * @param dynamicThreadPoolExecutor dynamic thread-pool executor */ public static void replace(Object executor, Executor dynamicThreadPoolExecutor) { Optional dynamicThreadPoolAdapterOptional = DYNAMIC_THREAD_POOL_ADAPTERS.stream().filter(each -> each.match(executor)).findFirst(); diff --git a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/service/AbstractDynamicThreadPoolService.java b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/service/AbstractDynamicThreadPoolService.java index 4348c78a..2cde0551 100644 --- a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/service/AbstractDynamicThreadPoolService.java +++ b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/service/AbstractDynamicThreadPoolService.java @@ -33,8 +33,8 @@ public abstract class AbstractDynamicThreadPoolService implements DynamicThreadP /** * Build dynamic thread-pool executor. * - * @param registerParameter - * @return + * @param registerParameter register parameter + * @return dynamic thread-pool executor */ public ThreadPoolExecutor buildDynamicThreadPoolExecutor(DynamicThreadPoolRegisterParameter registerParameter) { ThreadPoolExecutor dynamicThreadPoolExecutor = ThreadPoolBuilder.builder() diff --git a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/service/DynamicThreadPoolService.java b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/service/DynamicThreadPoolService.java index dbb6fd5d..0028e0ef 100644 --- a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/service/DynamicThreadPoolService.java +++ b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/service/DynamicThreadPoolService.java @@ -29,8 +29,8 @@ public interface DynamicThreadPoolService { /** * Registering dynamic thread pools at runtime. * - * @param registerWrapper - * @return + * @param registerWrapper register wrapper + * @return dynamic thread-pool executor */ ThreadPoolExecutor registerDynamicThreadPool(DynamicThreadPoolRegisterWrapper registerWrapper); } diff --git a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/CommonDynamicThreadPool.java b/hippo4j-core/src/main/java/cn/hippo4j/core/provider/CommonDynamicThreadPoolProviderFactory.java similarity index 80% rename from hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/CommonDynamicThreadPool.java rename to hippo4j-core/src/main/java/cn/hippo4j/core/provider/CommonDynamicThreadPoolProviderFactory.java index 9da3b6b7..c7282947 100644 --- a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/CommonDynamicThreadPool.java +++ b/hippo4j-core/src/main/java/cn/hippo4j/core/provider/CommonDynamicThreadPoolProviderFactory.java @@ -15,18 +15,25 @@ * limitations under the License. */ -package cn.hippo4j.core.executor.support; +package cn.hippo4j.core.provider; import cn.hippo4j.common.executor.support.BlockingQueueTypeEnum; import cn.hippo4j.core.executor.DynamicThreadPoolExecutor; +import cn.hippo4j.core.executor.support.ThreadPoolBuilder; import java.util.concurrent.TimeUnit; /** - * Common dynamic thread-pool. + * Common dynamic thread-pool provider factory. */ -public class CommonDynamicThreadPool { +public class CommonDynamicThreadPoolProviderFactory { + /** + * Get the public dynamic thread pool instance. + * + * @param threadPoolId thread-pool id + * @return dynamic thread-pool executor + */ public static DynamicThreadPoolExecutor getInstance(String threadPoolId) { DynamicThreadPoolExecutor dynamicThreadPoolExecutor = (DynamicThreadPoolExecutor) ThreadPoolBuilder.builder() .dynamicPool() diff --git a/hippo4j-spring-boot/hippo4j-config-spring-boot-starter/src/main/java/cn/hippo4j/config/springboot/starter/support/DynamicThreadPoolPostProcessor.java b/hippo4j-spring-boot/hippo4j-config-spring-boot-starter/src/main/java/cn/hippo4j/config/springboot/starter/support/DynamicThreadPoolPostProcessor.java index bc12c259..2bce698f 100644 --- a/hippo4j-spring-boot/hippo4j-config-spring-boot-starter/src/main/java/cn/hippo4j/config/springboot/starter/support/DynamicThreadPoolPostProcessor.java +++ b/hippo4j-spring-boot/hippo4j-config-spring-boot-starter/src/main/java/cn/hippo4j/config/springboot/starter/support/DynamicThreadPoolPostProcessor.java @@ -29,9 +29,9 @@ import cn.hippo4j.core.executor.DynamicThreadPoolExecutor; import cn.hippo4j.core.executor.DynamicThreadPoolWrapper; import cn.hippo4j.core.executor.manage.GlobalNotifyAlarmManage; import cn.hippo4j.core.executor.manage.GlobalThreadPoolManage; -import cn.hippo4j.core.executor.support.CommonDynamicThreadPool; import cn.hippo4j.core.executor.support.ThreadPoolBuilder; import cn.hippo4j.core.executor.support.adpter.DynamicThreadPoolAdapterChoose; +import cn.hippo4j.core.provider.CommonDynamicThreadPoolProviderFactory; import cn.hippo4j.core.toolkit.DynamicThreadPoolAnnotationUtil; import cn.hippo4j.message.service.ThreadPoolNotifyAlarm; import lombok.AllArgsConstructor; @@ -114,7 +114,7 @@ public final class DynamicThreadPoolPostProcessor implements BeanPostProcessor { log.error("Failed to initialize thread pool configuration. error: {}", ex); } finally { if (Objects.isNull(dynamicThreadPoolWrapper.getExecutor())) { - dynamicThreadPoolWrapper.setExecutor(CommonDynamicThreadPool.getInstance(threadPoolId)); + dynamicThreadPoolWrapper.setExecutor(CommonDynamicThreadPoolProviderFactory.getInstance(threadPoolId)); } dynamicThreadPoolWrapper.setInitFlag(Boolean.TRUE); } diff --git a/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/support/DynamicThreadPoolPostProcessor.java b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/support/DynamicThreadPoolPostProcessor.java index 82339e31..0d3006d8 100644 --- a/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/support/DynamicThreadPoolPostProcessor.java +++ b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/support/DynamicThreadPoolPostProcessor.java @@ -33,9 +33,9 @@ import cn.hippo4j.core.executor.DynamicThreadPoolExecutor; import cn.hippo4j.core.executor.DynamicThreadPoolWrapper; import cn.hippo4j.core.executor.manage.GlobalNotifyAlarmManage; import cn.hippo4j.core.executor.manage.GlobalThreadPoolManage; -import cn.hippo4j.core.executor.support.CommonDynamicThreadPool; import cn.hippo4j.core.executor.support.ThreadPoolBuilder; import cn.hippo4j.core.executor.support.adpter.DynamicThreadPoolAdapterChoose; +import cn.hippo4j.core.provider.CommonDynamicThreadPoolProviderFactory; import cn.hippo4j.core.toolkit.DynamicThreadPoolAnnotationUtil; import cn.hippo4j.message.service.ThreadPoolNotifyAlarm; import cn.hippo4j.springboot.starter.config.BootstrapProperties; @@ -55,7 +55,9 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import static cn.hippo4j.common.constant.Constants.*; +import static cn.hippo4j.common.constant.Constants.ITEM_ID; +import static cn.hippo4j.common.constant.Constants.NAMESPACE; +import static cn.hippo4j.common.constant.Constants.TP_ID; /** * Dynamic thread-pool post processor. @@ -189,12 +191,12 @@ public final class DynamicThreadPoolPostProcessor implements BeanPostProcessor { GlobalThreadPoolManage.dynamicRegister(registerWrapper); } } catch (Exception ex) { - newDynamicThreadPoolExecutor = executor != null ? executor : CommonDynamicThreadPool.getInstance(threadPoolId); + newDynamicThreadPoolExecutor = executor != null ? executor : CommonDynamicThreadPoolProviderFactory.getInstance(threadPoolId); dynamicThreadPoolWrapper.setExecutor(newDynamicThreadPoolExecutor); log.error("Failed to initialize thread pool configuration. error message: {}", ex.getMessage()); } finally { if (Objects.isNull(executor)) { - dynamicThreadPoolWrapper.setExecutor(CommonDynamicThreadPool.getInstance(threadPoolId)); + dynamicThreadPoolWrapper.setExecutor(CommonDynamicThreadPoolProviderFactory.getInstance(threadPoolId)); } } GlobalThreadPoolManage.register(dynamicThreadPoolWrapper.getThreadPoolId(), threadPoolParameterInfo, dynamicThreadPoolWrapper); From 4dcee4a371273f3179f3b288b8ae2940f25e55f5 Mon Sep 17 00:00:00 2001 From: "chen.ma" Date: Wed, 2 Nov 2022 01:26:10 +0800 Subject: [PATCH 05/18] Change post log location --- docs/docs/{user_docs/other => community}/update-log.md | 2 +- .../current/{user_docs/other => community}/update-log.md | 2 +- .../version-1.4.2/{user_docs/other => community}/update-log.md | 2 +- .../version-1.4.2/{user_docs/other => community}/update-log.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename docs/docs/{user_docs/other => community}/update-log.md (99%) rename docs/i18n/zh/docusaurus-plugin-content-docs/current/{user_docs/other => community}/update-log.md (99%) rename docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/{user_docs/other => community}/update-log.md (99%) rename docs/versioned_docs/version-1.4.2/{user_docs/other => community}/update-log.md (99%) diff --git a/docs/docs/user_docs/other/update-log.md b/docs/docs/community/update-log.md similarity index 99% rename from docs/docs/user_docs/other/update-log.md rename to docs/docs/community/update-log.md index fc7b2fda..aa4adc20 100644 --- a/docs/docs/user_docs/other/update-log.md +++ b/docs/docs/community/update-log.md @@ -1,5 +1,5 @@ --- -sidebar_position: 4 +sidebar_position: 5 --- # 更新日志 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/other/update-log.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/community/update-log.md similarity index 99% rename from docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/other/update-log.md rename to docs/i18n/zh/docusaurus-plugin-content-docs/current/community/update-log.md index fc7b2fda..aa4adc20 100644 --- a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/other/update-log.md +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/community/update-log.md @@ -1,5 +1,5 @@ --- -sidebar_position: 4 +sidebar_position: 5 --- # 更新日志 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/other/update-log.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/community/update-log.md similarity index 99% rename from docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/other/update-log.md rename to docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/community/update-log.md index fc7b2fda..aa4adc20 100644 --- a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/other/update-log.md +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/community/update-log.md @@ -1,5 +1,5 @@ --- -sidebar_position: 4 +sidebar_position: 5 --- # 更新日志 diff --git a/docs/versioned_docs/version-1.4.2/user_docs/other/update-log.md b/docs/versioned_docs/version-1.4.2/community/update-log.md similarity index 99% rename from docs/versioned_docs/version-1.4.2/user_docs/other/update-log.md rename to docs/versioned_docs/version-1.4.2/community/update-log.md index fc7b2fda..aa4adc20 100644 --- a/docs/versioned_docs/version-1.4.2/user_docs/other/update-log.md +++ b/docs/versioned_docs/version-1.4.2/community/update-log.md @@ -1,5 +1,5 @@ --- -sidebar_position: 4 +sidebar_position: 5 --- # 更新日志 From d2f8a5e3ab035641f79a3fce0d70356b23acf1ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=88=90=E5=85=B4?= <49221670+Createsequence@users.noreply.github.com> Date: Wed, 2 Nov 2022 12:23:47 +0800 Subject: [PATCH 06/18] doc: Update developer (#893) --- docs/docs/community/developer.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/docs/community/developer.md b/docs/docs/community/developer.md index 03cdd4e3..45d9afa5 100644 --- a/docs/docs/community/developer.md +++ b/docs/docs/community/developer.md @@ -61,6 +61,13 @@ sidebar_position: 2 - 17855368071@163.com + + + 黄成兴 + Createsequence + Createsequence's Blog + 841396397@qq.com + ## 成为核心开发者 From f7ce392dbc4f7cb818cc4470380f37ab7d635fce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=88=90=E5=85=B4?= <49221670+Createsequence@users.noreply.github.com> Date: Wed, 2 Nov 2022 12:23:59 +0800 Subject: [PATCH 07/18] test: Add unit test about plugin manager (#892) --- .../DefaultThreadPoolPluginManagerTest.java | 163 +++++++++++++++ .../DefaultThreadPoolPluginRegistrarTest.java | 57 ++++++ .../EmptyThreadPoolPluginManagerTest.java | 110 ++++++++++ .../manager/ThreadPoolPluginSupportTest.java | 189 ++++++++++++++++++ 4 files changed, 519 insertions(+) create mode 100644 hippo4j-core/src/test/java/cn/hippo4j/core/plugin/manager/DefaultThreadPoolPluginManagerTest.java create mode 100644 hippo4j-core/src/test/java/cn/hippo4j/core/plugin/manager/DefaultThreadPoolPluginRegistrarTest.java create mode 100644 hippo4j-core/src/test/java/cn/hippo4j/core/plugin/manager/EmptyThreadPoolPluginManagerTest.java create mode 100644 hippo4j-core/src/test/java/cn/hippo4j/core/plugin/manager/ThreadPoolPluginSupportTest.java diff --git a/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/manager/DefaultThreadPoolPluginManagerTest.java b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/manager/DefaultThreadPoolPluginManagerTest.java new file mode 100644 index 00000000..627e65aa --- /dev/null +++ b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/manager/DefaultThreadPoolPluginManagerTest.java @@ -0,0 +1,163 @@ +/* + * 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.core.plugin.manager; + +import cn.hippo4j.core.plugin.*; +import lombok.Getter; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * test for {@link DefaultThreadPoolPluginManager} + */ +public class DefaultThreadPoolPluginManagerTest { + + private DefaultThreadPoolPluginManager manager; + + @Before + public void initRegistry() { + manager = new DefaultThreadPoolPluginManager(); + } + + @Test + public void testRegister() { + manager.register(new TestShutdownAwarePlugin()); + Assert.assertEquals(1, manager.getAllPlugins().size()); + } + + @Test + public void testGetAllPlugins() { + manager.register(new TestExecuteAwarePlugin()); + manager.register(new TestRejectedAwarePlugin()); + Assert.assertEquals(2, manager.getAllPlugins().size()); + } + + @Test + public void testClear() { + manager.register(new TestExecuteAwarePlugin()); + manager.clear(); + Assert.assertTrue(manager.getAllPlugins().isEmpty()); + } + + @Test + public void testTryRegister() { + Assert.assertTrue(manager.tryRegister(new TestExecuteAwarePlugin())); + Assert.assertFalse(manager.tryRegister(new TestExecuteAwarePlugin())); + } + + @Test + public void testIsRegistered() { + Assert.assertFalse(manager.isRegistered(TestExecuteAwarePlugin.class.getSimpleName())); + manager.register(new TestExecuteAwarePlugin()); + Assert.assertTrue(manager.isRegistered(TestExecuteAwarePlugin.class.getSimpleName())); + } + + @Test + public void testUnregister() { + manager.register(new TestExecuteAwarePlugin()); + manager.unregister(TestExecuteAwarePlugin.class.getSimpleName()); + Assert.assertFalse(manager.isRegistered(TestExecuteAwarePlugin.class.getSimpleName())); + } + + @Test + public void testGetPlugin() { + ThreadPoolPlugin plugin = new TestExecuteAwarePlugin(); + manager.register(plugin); + Assert.assertSame(plugin, manager.getPlugin(plugin.getId()).orElse(null)); + } + + @Test + public void testGetRejectedAwarePluginList() { + manager.register(new TestRejectedAwarePlugin()); + Assert.assertEquals(1, manager.getRejectedAwarePluginList().size()); + } + + @Test + public void testGetShutdownAwarePluginList() { + manager.register(new TestShutdownAwarePlugin()); + Assert.assertEquals(1, manager.getShutdownAwarePluginList().size()); + } + + @Test + public void testGetTaskAwarePluginList() { + manager.register(new TestTaskAwarePlugin()); + Assert.assertEquals(1, manager.getTaskAwarePluginList().size()); + } + + @Test + public void testGetExecuteAwarePluginList() { + manager.register(new TestExecuteAwarePlugin()); + Assert.assertEquals(1, manager.getExecuteAwarePluginList().size()); + } + + @Test + public void testGetAllPluginsOfType() { + manager.register(new TestExecuteAwarePlugin()); + manager.register(new TestRejectedAwarePlugin()); + Assert.assertEquals(1, manager.getAllPluginsOfType(TestExecuteAwarePlugin.class).size()); + Assert.assertEquals(1, manager.getAllPluginsOfType(TestRejectedAwarePlugin.class).size()); + Assert.assertEquals(2, manager.getAllPluginsOfType(ThreadPoolPlugin.class).size()); + } + + @Test + public void testGetAllPluginRuntimes() { + manager.register(new TestExecuteAwarePlugin()); + manager.register(new TestRejectedAwarePlugin()); + Assert.assertEquals(2, manager.getAllPluginRuntimes().size()); + } + + @Test + public void testGetPluginRuntime() { + manager.register(new TestExecuteAwarePlugin()); + Assert.assertTrue(manager.getRuntime(TestExecuteAwarePlugin.class.getSimpleName()).isPresent()); + } + + @Test + public void testGetPluginOfType() { + manager.register(new TestExecuteAwarePlugin()); + Assert.assertTrue(manager.getPluginOfType(TestExecuteAwarePlugin.class.getSimpleName(), TestExecuteAwarePlugin.class).isPresent()); + Assert.assertTrue(manager.getPluginOfType(TestExecuteAwarePlugin.class.getSimpleName(), ThreadPoolPlugin.class).isPresent()); + Assert.assertFalse(manager.getPluginOfType(TestExecuteAwarePlugin.class.getSimpleName(), RejectedAwarePlugin.class).isPresent()); + } + + @Getter + private final static class TestTaskAwarePlugin implements TaskAwarePlugin { + + private final String id = this.getClass().getSimpleName(); + } + + @Getter + private final static class TestExecuteAwarePlugin implements ExecuteAwarePlugin { + + private final String id = this.getClass().getSimpleName(); + } + + @Getter + private final static class TestRejectedAwarePlugin implements RejectedAwarePlugin { + + private final String id = this.getClass().getSimpleName(); + } + + @Getter + private final static class TestShutdownAwarePlugin implements ShutdownAwarePlugin { + + private final String id = this.getClass().getSimpleName(); + } + +} diff --git a/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/manager/DefaultThreadPoolPluginRegistrarTest.java b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/manager/DefaultThreadPoolPluginRegistrarTest.java new file mode 100644 index 00000000..435a8c11 --- /dev/null +++ b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/manager/DefaultThreadPoolPluginRegistrarTest.java @@ -0,0 +1,57 @@ +/* + * 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.core.plugin.manager; + +import cn.hippo4j.core.executor.ExtensibleThreadPoolExecutor; +import cn.hippo4j.core.plugin.impl.*; +import org.junit.Assert; +import org.junit.Test; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * test for {@link DefaultThreadPoolPluginRegistrar} + */ +public class DefaultThreadPoolPluginRegistrarTest { + + @Test + public void testGetId() { + ThreadPoolPluginRegistrar registrar = new DefaultThreadPoolPluginRegistrar(); + Assert.assertEquals(registrar.getClass().getSimpleName(), registrar.getId()); + } + + @Test + public void testDoRegister() { + ThreadPoolPluginRegistrar registrar = new DefaultThreadPoolPluginRegistrar(100L, 100L); + ThreadPoolPluginManager manager = new DefaultThreadPoolPluginManager(); + ExtensibleThreadPoolExecutor executor = new ExtensibleThreadPoolExecutor( + "test", manager, + 5, 5, 1000L, TimeUnit.MILLISECONDS, + new ArrayBlockingQueue<>(1), Thread::new, new ThreadPoolExecutor.AbortPolicy()); + registrar.doRegister(executor); + + Assert.assertTrue(manager.getPlugin(TaskDecoratorPlugin.PLUGIN_NAME).isPresent()); + Assert.assertTrue(manager.getPlugin(TaskTimeoutNotifyAlarmPlugin.PLUGIN_NAME).isPresent()); + Assert.assertTrue(manager.getPlugin(TaskRejectCountRecordPlugin.PLUGIN_NAME).isPresent()); + Assert.assertTrue(manager.getPlugin(TaskRejectNotifyAlarmPlugin.PLUGIN_NAME).isPresent()); + Assert.assertTrue(manager.getPlugin(ThreadPoolExecutorShutdownPlugin.PLUGIN_NAME).isPresent()); + } + +} diff --git a/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/manager/EmptyThreadPoolPluginManagerTest.java b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/manager/EmptyThreadPoolPluginManagerTest.java new file mode 100644 index 00000000..f5e0ac26 --- /dev/null +++ b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/manager/EmptyThreadPoolPluginManagerTest.java @@ -0,0 +1,110 @@ +/* + * 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.core.plugin.manager; + +import cn.hippo4j.core.plugin.ThreadPoolPlugin; +import lombok.Getter; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Collections; +import java.util.Optional; + +/** + * test for {@link cn.hippo4j.core.plugin.manager.EmptyThreadPoolPluginManager} + */ +public class EmptyThreadPoolPluginManagerTest { + + private final ThreadPoolPluginManager manager = EmptyThreadPoolPluginManager.INSTANCE; + + @Test + public void testEmpty() { + Assert.assertSame(manager, ThreadPoolPluginManager.empty()); + } + + @Test + public void testGetAllPlugins() { + Assert.assertEquals(Collections.emptyList(), manager.getAllPluginRuntimes()); + } + + @Test + public void testClear() { + manager.clear(); + Assert.assertTrue(isEmpty(manager)); + } + + @Test + public void testRegister() { + manager.register(new TestPlugin()); + Assert.assertTrue(isEmpty(manager)); + } + + @Test + public void testTryRegister() { + Assert.assertFalse(manager.tryRegister(new TestPlugin())); + } + + @Test + public void testIsRegistered() { + manager.register(new TestPlugin()); + Assert.assertFalse(manager.isRegistered(TestPlugin.class.getSimpleName())); + } + + @Test + public void testUnregister() { + manager.register(new TestPlugin()); + manager.unregister(TestPlugin.class.getSimpleName()); + Assert.assertTrue(isEmpty(manager)); + } + + @Test + public void testGetPlugin() { + Assert.assertSame(Optional.empty(), manager.getPlugin("")); + } + + @Test + public void testGetRejectedAwarePluginList() { + Assert.assertEquals(Collections.emptyList(), manager.getRejectedAwarePluginList()); + } + + @Test + public void testGetShutdownAwarePluginList() { + Assert.assertEquals(Collections.emptyList(), manager.getShutdownAwarePluginList()); + } + + @Test + public void testGetTaskAwarePluginList() { + Assert.assertEquals(Collections.emptyList(), manager.getTaskAwarePluginList()); + } + + @Test + public void testGetExecuteAwarePluginList() { + Assert.assertEquals(Collections.emptyList(), manager.getExecuteAwarePluginList()); + } + + private static boolean isEmpty(ThreadPoolPluginManager manager) { + return manager.getAllPlugins().isEmpty(); + } + + @Getter + private static class TestPlugin implements ThreadPoolPlugin { + + private final String id = TestPlugin.class.getSimpleName(); + } + +} diff --git a/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/manager/ThreadPoolPluginSupportTest.java b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/manager/ThreadPoolPluginSupportTest.java new file mode 100644 index 00000000..e92ce58b --- /dev/null +++ b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/manager/ThreadPoolPluginSupportTest.java @@ -0,0 +1,189 @@ +/* + * 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.core.plugin.manager; + +import cn.hippo4j.core.executor.ExtensibleThreadPoolExecutor; +import cn.hippo4j.core.plugin.*; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.junit.Assert; +import org.junit.Test; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * test for default method of {@link ThreadPoolPluginSupport} + */ +public class ThreadPoolPluginSupportTest { + + private final ThreadPoolPluginManager manager = new DefaultThreadPoolPluginManager(); + private final ExtensibleThreadPoolExecutor executor = new ExtensibleThreadPoolExecutor( + "test", manager, + 5, 5, 1000L, TimeUnit.MILLISECONDS, + new ArrayBlockingQueue<>(1), Thread::new, new ThreadPoolExecutor.AbortPolicy()); + private final ThreadPoolPluginSupport support = new TestSupport(executor.getThreadPoolId(), executor, manager); + + @Test + public void testGetThreadPoolId() { + Assert.assertEquals(executor.getThreadPoolId(), support.getThreadPoolId()); + } + + @Test + public void testGetThreadPoolPluginManager() { + Assert.assertEquals(manager, support.getThreadPoolPluginManager()); + } + + @Getter + @RequiredArgsConstructor + private static class TestSupport implements ThreadPoolPluginSupport { + + private final String threadPoolId; + private final ExtensibleThreadPoolExecutor threadPoolExecutor; + private final ThreadPoolPluginManager threadPoolPluginManager; + } + + // ================ default delegate method ================ + + @Test + public void testRegister() { + support.register(new TestShutdownAwarePlugin()); + Assert.assertEquals(1, support.getAllPlugins().size()); + } + + @Test + public void testGetAllPlugins() { + support.register(new TestExecuteAwarePlugin()); + support.register(new TestRejectedAwarePlugin()); + Assert.assertEquals(2, support.getAllPlugins().size()); + } + + @Test + public void testClear() { + support.register(new TestExecuteAwarePlugin()); + support.clear(); + Assert.assertTrue(support.getAllPlugins().isEmpty()); + } + + @Test + public void testTryRegister() { + Assert.assertTrue(support.tryRegister(new TestExecuteAwarePlugin())); + Assert.assertFalse(support.tryRegister(new TestExecuteAwarePlugin())); + } + + @Test + public void testIsRegistered() { + Assert.assertFalse(support.isRegistered(TestExecuteAwarePlugin.class.getSimpleName())); + support.register(new TestExecuteAwarePlugin()); + Assert.assertTrue(support.isRegistered(TestExecuteAwarePlugin.class.getSimpleName())); + } + + @Test + public void testUnregister() { + support.register(new TestExecuteAwarePlugin()); + support.unregister(TestExecuteAwarePlugin.class.getSimpleName()); + Assert.assertFalse(support.isRegistered(TestExecuteAwarePlugin.class.getSimpleName())); + } + + @Test + public void testGetPlugin() { + ThreadPoolPlugin plugin = new TestExecuteAwarePlugin(); + support.register(plugin); + Assert.assertSame(plugin, support.getPlugin(plugin.getId()).orElse(null)); + } + + @Test + public void testGetRejectedAwarePluginList() { + support.register(new TestRejectedAwarePlugin()); + Assert.assertEquals(1, support.getRejectedAwarePluginList().size()); + } + + @Test + public void testGetShutdownAwarePluginList() { + support.register(new TestShutdownAwarePlugin()); + Assert.assertEquals(1, support.getShutdownAwarePluginList().size()); + } + + @Test + public void testGetTaskAwarePluginList() { + support.register(new TestTaskAwarePlugin()); + Assert.assertEquals(1, support.getTaskAwarePluginList().size()); + } + + @Test + public void testGetExecuteAwarePluginList() { + support.register(new TestExecuteAwarePlugin()); + Assert.assertEquals(1, support.getExecuteAwarePluginList().size()); + } + + @Test + public void testGetAllPluginsOfType() { + support.register(new TestExecuteAwarePlugin()); + support.register(new TestRejectedAwarePlugin()); + Assert.assertEquals(1, support.getAllPluginsOfType(TestExecuteAwarePlugin.class).size()); + Assert.assertEquals(1, support.getAllPluginsOfType(TestRejectedAwarePlugin.class).size()); + Assert.assertEquals(2, support.getAllPluginsOfType(ThreadPoolPlugin.class).size()); + } + + @Test + public void testGetAllPluginRuntimes() { + support.register(new TestExecuteAwarePlugin()); + support.register(new TestRejectedAwarePlugin()); + Assert.assertEquals(2, support.getAllPluginRuntimes().size()); + } + + @Test + public void testGetPluginRuntime() { + support.register(new TestExecuteAwarePlugin()); + Assert.assertTrue(support.getRuntime(TestExecuteAwarePlugin.class.getSimpleName()).isPresent()); + } + + @Test + public void testGetPluginOfType() { + support.register(new TestExecuteAwarePlugin()); + Assert.assertTrue(support.getPluginOfType(TestExecuteAwarePlugin.class.getSimpleName(), TestExecuteAwarePlugin.class).isPresent()); + Assert.assertTrue(support.getPluginOfType(TestExecuteAwarePlugin.class.getSimpleName(), ThreadPoolPlugin.class).isPresent()); + Assert.assertFalse(support.getPluginOfType(TestExecuteAwarePlugin.class.getSimpleName(), RejectedAwarePlugin.class).isPresent()); + } + + @Getter + private final static class TestTaskAwarePlugin implements TaskAwarePlugin { + + private final String id = this.getClass().getSimpleName(); + } + + @Getter + private final static class TestExecuteAwarePlugin implements ExecuteAwarePlugin { + + private final String id = this.getClass().getSimpleName(); + } + + @Getter + private final static class TestRejectedAwarePlugin implements RejectedAwarePlugin { + + private final String id = this.getClass().getSimpleName(); + } + + @Getter + private final static class TestShutdownAwarePlugin implements ShutdownAwarePlugin { + + private final String id = this.getClass().getSimpleName(); + } + +} From 59a3e7118048b38987549578d9bfc2eff5abc41d Mon Sep 17 00:00:00 2001 From: "chen.ma" Date: Wed, 2 Nov 2022 12:29:33 +0800 Subject: [PATCH 08/18] Update core developer list --- docs/docs/community/developer.md | 2 +- .../current/community/developer.md | 9 ++++++++- .../version-1.4.2/community/developer.md | 9 ++++++++- docs/versioned_docs/version-1.4.2/community/developer.md | 9 ++++++++- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/docs/docs/community/developer.md b/docs/docs/community/developer.md index 45d9afa5..89b57b88 100644 --- a/docs/docs/community/developer.md +++ b/docs/docs/community/developer.md @@ -72,7 +72,7 @@ sidebar_position: 2 ## 成为核心开发者 -持续对 Hippo-4J 进行贡献, 粗略评估,完成 10 次 PR 贡献即可成为核心开发者。 其中包括完成 2 个 [good pro issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+pro+issue%22) 或以上,以及 若干个 [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)。 +持续对 Hippo-4J 进行贡献, 粗略评估,完成 10 次 PR 贡献即可成为核心开发者。 其中包括完成 2 个 [good pro issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+pro+issue%22) 或以上,以及若干个 [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)。 :::note 会根据 PR 质量提供个性化评估,有可能一个或两个质量较高 PR 即可成为核心开发者。参考:[重构 DynamicThreadPoolExecutor 功能扩展逻辑](https://github.com/opengoofy/hippo4j/pull/854) diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/community/developer.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/community/developer.md index 03cdd4e3..89b57b88 100644 --- a/docs/i18n/zh/docusaurus-plugin-content-docs/current/community/developer.md +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/community/developer.md @@ -61,11 +61,18 @@ sidebar_position: 2 - 17855368071@163.com + + + 黄成兴 + Createsequence + Createsequence's Blog + 841396397@qq.com + ## 成为核心开发者 -持续对 Hippo-4J 进行贡献, 粗略评估,完成 10 次 PR 贡献即可成为核心开发者。 其中包括完成 2 个 [good pro issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+pro+issue%22) 或以上,以及 若干个 [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)。 +持续对 Hippo-4J 进行贡献, 粗略评估,完成 10 次 PR 贡献即可成为核心开发者。 其中包括完成 2 个 [good pro issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+pro+issue%22) 或以上,以及若干个 [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)。 :::note 会根据 PR 质量提供个性化评估,有可能一个或两个质量较高 PR 即可成为核心开发者。参考:[重构 DynamicThreadPoolExecutor 功能扩展逻辑](https://github.com/opengoofy/hippo4j/pull/854) diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/community/developer.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/community/developer.md index 03cdd4e3..89b57b88 100644 --- a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/community/developer.md +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/community/developer.md @@ -61,11 +61,18 @@ sidebar_position: 2 - 17855368071@163.com + + + 黄成兴 + Createsequence + Createsequence's Blog + 841396397@qq.com + ## 成为核心开发者 -持续对 Hippo-4J 进行贡献, 粗略评估,完成 10 次 PR 贡献即可成为核心开发者。 其中包括完成 2 个 [good pro issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+pro+issue%22) 或以上,以及 若干个 [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)。 +持续对 Hippo-4J 进行贡献, 粗略评估,完成 10 次 PR 贡献即可成为核心开发者。 其中包括完成 2 个 [good pro issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+pro+issue%22) 或以上,以及若干个 [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)。 :::note 会根据 PR 质量提供个性化评估,有可能一个或两个质量较高 PR 即可成为核心开发者。参考:[重构 DynamicThreadPoolExecutor 功能扩展逻辑](https://github.com/opengoofy/hippo4j/pull/854) diff --git a/docs/versioned_docs/version-1.4.2/community/developer.md b/docs/versioned_docs/version-1.4.2/community/developer.md index 03cdd4e3..89b57b88 100644 --- a/docs/versioned_docs/version-1.4.2/community/developer.md +++ b/docs/versioned_docs/version-1.4.2/community/developer.md @@ -61,11 +61,18 @@ sidebar_position: 2 - 17855368071@163.com + + + 黄成兴 + Createsequence + Createsequence's Blog + 841396397@qq.com + ## 成为核心开发者 -持续对 Hippo-4J 进行贡献, 粗略评估,完成 10 次 PR 贡献即可成为核心开发者。 其中包括完成 2 个 [good pro issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+pro+issue%22) 或以上,以及 若干个 [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)。 +持续对 Hippo-4J 进行贡献, 粗略评估,完成 10 次 PR 贡献即可成为核心开发者。 其中包括完成 2 个 [good pro issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+pro+issue%22) 或以上,以及若干个 [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)。 :::note 会根据 PR 质量提供个性化评估,有可能一个或两个质量较高 PR 即可成为核心开发者。参考:[重构 DynamicThreadPoolExecutor 功能扩展逻辑](https://github.com/opengoofy/hippo4j/pull/854) From 024f694734aff70c3ce0761d191385462db26eac Mon Sep 17 00:00:00 2001 From: baymax55 <35788491+baymax55@users.noreply.github.com> Date: Wed, 2 Nov 2022 18:13:48 +0800 Subject: [PATCH 09/18] revert Hippo4jBaseSendMessageService implement CommandLineRunner interface; (#895) notifyConfigs filed will not be init when use InitializingBean interface --- .../message/service/Hippo4jBaseSendMessageService.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hippo4j-message/src/main/java/cn/hippo4j/message/service/Hippo4jBaseSendMessageService.java b/hippo4j-message/src/main/java/cn/hippo4j/message/service/Hippo4jBaseSendMessageService.java index 0cb9a0e9..2f5eedbd 100644 --- a/hippo4j-message/src/main/java/cn/hippo4j/message/service/Hippo4jBaseSendMessageService.java +++ b/hippo4j-message/src/main/java/cn/hippo4j/message/service/Hippo4jBaseSendMessageService.java @@ -17,9 +17,9 @@ package cn.hippo4j.message.service; -import cn.hippo4j.common.config.ApplicationContextHolder; import cn.hippo4j.common.toolkit.CollectionUtil; import cn.hippo4j.message.api.NotifyConfigBuilder; +import cn.hippo4j.common.config.ApplicationContextHolder; import cn.hippo4j.message.dto.AlarmControlDTO; import cn.hippo4j.message.dto.NotifyConfigDTO; import cn.hippo4j.message.enums.NotifyTypeEnum; @@ -28,7 +28,7 @@ import cn.hippo4j.message.request.ChangeParameterNotifyRequest; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.InitializingBean; +import org.springframework.boot.CommandLineRunner; import java.util.HashMap; import java.util.List; @@ -39,7 +39,7 @@ import java.util.Map; */ @Slf4j @RequiredArgsConstructor -public class Hippo4jBaseSendMessageService implements Hippo4jSendMessageService, InitializingBean { +public class Hippo4jBaseSendMessageService implements Hippo4jSendMessageService, CommandLineRunner { private final NotifyConfigBuilder notifyConfigBuilder; @@ -133,7 +133,7 @@ public class Hippo4jBaseSendMessageService implements Hippo4jSendMessageService, } @Override - public void afterPropertiesSet() throws Exception { + public void run(String... args) throws Exception { Map sendMessageHandlerMap = ApplicationContextHolder.getBeansOfType(SendMessageHandler.class); sendMessageHandlerMap.values().forEach(each -> sendMessageHandlers.put(each.getType(), each)); From d02e989c5474392db645c24470591e9fc6952f0f Mon Sep 17 00:00:00 2001 From: WuLang <48200100+wulangcode@users.noreply.github.com> Date: Wed, 2 Nov 2022 21:10:21 +0800 Subject: [PATCH 10/18] Adapt to delayed loading (#886) * feat:Adapt to delayed loading * feat:Field Rename --- .../hippo4j/common/toolkit/ContentUtil.java | 2 ++ .../DynamicThreadPoolAutoConfiguration.java | 3 +-- .../springboot/starter/core/ClientWorker.java | 24 +++++++++++++++---- .../ApplicationContentPostProcessor.java | 22 ++++++++++------- ...nt.java => ApplicationRefreshedEvent.java} | 4 ++-- .../starter/remote/AbstractHealthCheck.java | 8 +++---- .../DynamicThreadPoolConfigService.java | 11 ++------- 7 files changed, 44 insertions(+), 30 deletions(-) rename hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/event/{ApplicationCompleteEvent.java => ApplicationRefreshedEvent.java} (91%) diff --git a/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/ContentUtil.java b/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/ContentUtil.java index 13e3151d..68e19378 100644 --- a/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/ContentUtil.java +++ b/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/ContentUtil.java @@ -21,6 +21,8 @@ import cn.hippo4j.common.constant.Constants; import cn.hippo4j.common.model.ThreadPoolParameter; import cn.hippo4j.common.model.ThreadPoolParameterInfo; +import java.util.Objects; + /** * Content util. */ diff --git a/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/config/DynamicThreadPoolAutoConfiguration.java b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/config/DynamicThreadPoolAutoConfiguration.java index 5d39572b..b950b423 100644 --- a/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/config/DynamicThreadPoolAutoConfiguration.java +++ b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/config/DynamicThreadPoolAutoConfiguration.java @@ -106,12 +106,11 @@ public class DynamicThreadPoolAutoConfiguration { @Bean @SuppressWarnings("all") public DynamicThreadPoolService dynamicThreadPoolConfigService(HttpAgent httpAgent, - ClientWorker clientWorker, ServerHealthCheck serverHealthCheck, ServerNotifyConfigBuilder notifyConfigBuilder, Hippo4jBaseSendMessageService hippo4jBaseSendMessageService, DynamicThreadPoolSubscribeConfig dynamicThreadPoolSubscribeConfig) { - return new DynamicThreadPoolConfigService(httpAgent, clientWorker, properties, notifyConfigBuilder, hippo4jBaseSendMessageService, dynamicThreadPoolSubscribeConfig); + return new DynamicThreadPoolConfigService(httpAgent, properties, notifyConfigBuilder, hippo4jBaseSendMessageService, dynamicThreadPoolSubscribeConfig); } @Bean diff --git a/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/core/ClientWorker.java b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/core/ClientWorker.java index 0e30e6a2..60506c44 100644 --- a/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/core/ClientWorker.java +++ b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/core/ClientWorker.java @@ -18,7 +18,6 @@ package cn.hippo4j.springboot.starter.core; import cn.hippo4j.common.model.ThreadPoolParameterInfo; -import cn.hippo4j.common.toolkit.CollectionUtil; import cn.hippo4j.common.toolkit.ContentUtil; import cn.hippo4j.common.toolkit.GroupKey; import cn.hippo4j.common.toolkit.IdUtil; @@ -72,6 +71,8 @@ public class ClientWorker { private final CountDownLatch awaitApplicationComplete = new CountDownLatch(1); + private final CountDownLatch cacheCondition = new CountDownLatch(1); + private final ConcurrentHashMap cacheMap = new ConcurrentHashMap(16); @SuppressWarnings("all") @@ -92,9 +93,7 @@ public class ClientWorker { this.executor.schedule(() -> { try { awaitApplicationComplete.await(); - if (CollectionUtil.isNotEmpty(cacheMap)) { - executorService.execute(new LongPollingRunnable()); - } + executorService.execute(new LongPollingRunnable(cacheMap.isEmpty(), cacheCondition)); } catch (Throwable ex) { log.error("Sub check rotate check error.", ex); } @@ -103,9 +102,22 @@ public class ClientWorker { class LongPollingRunnable implements Runnable { + private boolean cacheMapInitEmptyFlag; + + private final CountDownLatch cacheCondition; + + public LongPollingRunnable(boolean cacheMapInitEmptyFlag, CountDownLatch cacheCondition) { + this.cacheMapInitEmptyFlag = cacheMapInitEmptyFlag; + this.cacheCondition = cacheCondition; + } + @Override @SneakyThrows public void run() { + if (cacheMapInitEmptyFlag) { + cacheCondition.await(); + cacheMapInitEmptyFlag = false; + } serverHealthCheck.isHealthStatus(); List cacheDataList = new ArrayList(); List inInitializingCacheList = new ArrayList(); @@ -227,6 +239,10 @@ public class ClientWorker { for (Listener listener : listeners) { cacheData.addListener(listener); } + // Lazy loading + if (awaitApplicationComplete.getCount() == 0L) { + cacheCondition.countDown(); + } } public CacheData addCacheDataIfAbsent(String namespace, String itemId, String threadPoolId) { diff --git a/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/event/ApplicationContentPostProcessor.java b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/event/ApplicationContentPostProcessor.java index f4e2cc23..3dd1adf2 100644 --- a/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/event/ApplicationContentPostProcessor.java +++ b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/event/ApplicationContentPostProcessor.java @@ -17,29 +17,33 @@ package cn.hippo4j.springboot.starter.event; -import org.springframework.boot.context.event.ApplicationReadyEvent; +import cn.hippo4j.springboot.starter.core.ClientWorker; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; import javax.annotation.Resource; +import java.util.concurrent.atomic.AtomicBoolean; /** * Application content post processor. */ -public class ApplicationContentPostProcessor implements ApplicationListener { +public class ApplicationContentPostProcessor implements ApplicationListener { @Resource private ApplicationContext applicationContext; - private boolean executeOnlyOnce = true; + @Resource + private ClientWorker clientWorker; + + private final AtomicBoolean executeOnlyOnce = new AtomicBoolean(false); @Override - public void onApplicationEvent(ApplicationReadyEvent event) { - synchronized (ApplicationContentPostProcessor.class) { - if (executeOnlyOnce) { - applicationContext.publishEvent(new ApplicationCompleteEvent(this)); - executeOnlyOnce = false; - } + public void onApplicationEvent(ContextRefreshedEvent event) { + if (!executeOnlyOnce.compareAndSet(false, true)) { + return; } + applicationContext.publishEvent(new ApplicationRefreshedEvent(this)); + clientWorker.notifyApplicationComplete(); } } diff --git a/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/event/ApplicationCompleteEvent.java b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/event/ApplicationRefreshedEvent.java similarity index 91% rename from hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/event/ApplicationCompleteEvent.java rename to hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/event/ApplicationRefreshedEvent.java index 9393062c..dbac0303 100644 --- a/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/event/ApplicationCompleteEvent.java +++ b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/event/ApplicationRefreshedEvent.java @@ -22,7 +22,7 @@ import org.springframework.context.ApplicationEvent; /** * Execute after the spring application context is successfully started. */ -public class ApplicationCompleteEvent extends ApplicationEvent { +public class ApplicationRefreshedEvent extends ApplicationEvent { /** * Create a new {@code ApplicationEvent}. @@ -30,7 +30,7 @@ public class ApplicationCompleteEvent extends ApplicationEvent { * @param source the object on which the event initially occurred or with * which the event is associated (never {@code null}) */ - public ApplicationCompleteEvent(Object source) { + public ApplicationRefreshedEvent(Object source) { super(source); } } diff --git a/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/remote/AbstractHealthCheck.java b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/remote/AbstractHealthCheck.java index b79595b5..1fd78118 100644 --- a/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/remote/AbstractHealthCheck.java +++ b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/remote/AbstractHealthCheck.java @@ -18,7 +18,7 @@ package cn.hippo4j.springboot.starter.remote; import cn.hippo4j.common.toolkit.ThreadUtil; -import cn.hippo4j.springboot.starter.event.ApplicationCompleteEvent; +import cn.hippo4j.springboot.starter.event.ApplicationRefreshedEvent; import cn.hippo4j.springboot.starter.core.ShutdownExecuteException; import cn.hippo4j.common.design.builder.ThreadFactoryBuilder; import lombok.SneakyThrows; @@ -38,7 +38,7 @@ import static cn.hippo4j.common.constant.Constants.HEALTH_CHECK_INTERVAL; * Abstract health check. */ @Slf4j -public abstract class AbstractHealthCheck implements ServerHealthCheck, InitializingBean, ApplicationListener { +public abstract class AbstractHealthCheck implements ServerHealthCheck, InitializingBean, ApplicationListener { /** * Health status @@ -157,11 +157,11 @@ public abstract class AbstractHealthCheck implements ServerHealthCheck, Initiali clientShutdownHook = true; signalAllBizThread(); })); - healthCheckExecutor.scheduleWithFixedDelay(() -> healthCheck(), 0, HEALTH_CHECK_INTERVAL, TimeUnit.SECONDS); + healthCheckExecutor.scheduleWithFixedDelay(this::healthCheck, 0, HEALTH_CHECK_INTERVAL, TimeUnit.SECONDS); } @Override - public void onApplicationEvent(ApplicationCompleteEvent event) { + public void onApplicationEvent(ApplicationRefreshedEvent event) { contextInitComplete = true; } } diff --git a/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/support/DynamicThreadPoolConfigService.java b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/support/DynamicThreadPoolConfigService.java index d66765e4..96e8d717 100644 --- a/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/support/DynamicThreadPoolConfigService.java +++ b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/support/DynamicThreadPoolConfigService.java @@ -36,7 +36,7 @@ import cn.hippo4j.message.service.ThreadPoolNotifyAlarm; import cn.hippo4j.springboot.starter.config.BootstrapProperties; import cn.hippo4j.springboot.starter.core.ClientWorker; import cn.hippo4j.springboot.starter.core.DynamicThreadPoolSubscribeConfig; -import cn.hippo4j.springboot.starter.event.ApplicationCompleteEvent; +import cn.hippo4j.springboot.starter.event.ApplicationRefreshedEvent; import cn.hippo4j.springboot.starter.notify.ServerNotifyConfigBuilder; import cn.hippo4j.springboot.starter.remote.HttpAgent; import lombok.RequiredArgsConstructor; @@ -55,12 +55,10 @@ import static cn.hippo4j.common.constant.Constants.REGISTER_DYNAMIC_THREAD_POOL_ */ @Slf4j @RequiredArgsConstructor -public class DynamicThreadPoolConfigService extends AbstractDynamicThreadPoolService implements ApplicationListener { +public class DynamicThreadPoolConfigService extends AbstractDynamicThreadPoolService { private final HttpAgent httpAgent; - private final ClientWorker clientWorker; - private final BootstrapProperties properties; private final ServerNotifyConfigBuilder notifyConfigBuilder; @@ -77,11 +75,6 @@ public class DynamicThreadPoolConfigService extends AbstractDynamicThreadPoolSer return dynamicThreadPoolExecutor; } - @Override - public void onApplicationEvent(ApplicationCompleteEvent event) { - clientWorker.notifyApplicationComplete(); - } - private ThreadPoolExecutor registerExecutor(DynamicThreadPoolRegisterWrapper registerWrapper) { DynamicThreadPoolRegisterParameter registerParameter = registerWrapper.getDynamicThreadPoolRegisterParameter(); checkThreadPoolParameter(registerParameter); From 4831cd6ecbf0f3a3582c4c6e7498724646e1816c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E7=A7=B0=20Ma=20Chen?= Date: Thu, 3 Nov 2022 09:55:46 +0800 Subject: [PATCH 11/18] Supplemental code comments (#898) --- .../core/executor/DynamicThreadPool.java | 25 +- .../support/FastThreadPoolExecutor.java | 8 + .../core/executor/support/TaskQueue.java | 22 +- .../executor/support/ThreadPoolBuilder.java | 226 ++++++++++++++++-- .../support/ThreadPoolExecutorTemplate.java | 28 ++- 5 files changed, 276 insertions(+), 33 deletions(-) diff --git a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/DynamicThreadPool.java b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/DynamicThreadPool.java index b9a5e58f..eb6632ff 100644 --- a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/DynamicThreadPool.java +++ b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/DynamicThreadPool.java @@ -24,7 +24,30 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Dynamic thread pool. + * An annotation that enhances the functionality of the jdk acoustic thread pool, + * with the following list of enhancements. + * + *
    + *
  • Dynamic change at runtime
  • + *
  • Determine whether an alarm is required at runtime
  • + *
  • Provide observable monitoring indicators
  • + *
  • ......
  • + * + * + *

    If you use Server mode, you can view the thread pool operation in the built-in console. + *

    If you use Config mode, you can observe with Prometheus and Grafana. + * + *

    The annotation is normally marked on the + * spring bean defined by {@link java.util.concurrent.ThreadPoolExecutor}. + * + *

    Can also be marked on the following types: + * + * @see java.util.concurrent.Executor + * @see java.util.concurrent.ExecutorService + * @see org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor + * @see com.alibaba.ttl.threadpool.ExecutorTtlWrapper + * @see com.alibaba.ttl.threadpool.ExecutorServiceTtlWrapper + * @since 1.0 */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) diff --git a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/FastThreadPoolExecutor.java b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/FastThreadPoolExecutor.java index 31835191..c4e4f569 100644 --- a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/FastThreadPoolExecutor.java +++ b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/FastThreadPoolExecutor.java @@ -41,8 +41,16 @@ public class FastThreadPoolExecutor extends ThreadPoolExecutorTemplate { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); } + /** + * Statistics on the number of tasks submitted by the fast consumption thread pool + */ private final AtomicInteger submittedTaskCount = new AtomicInteger(0); + /** + * Get submitted task count. + * + * @return submitted task count + */ public int getSubmittedTaskCount() { return submittedTaskCount.get(); } diff --git a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/TaskQueue.java b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/TaskQueue.java index 3d16c439..9eeedcf9 100644 --- a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/TaskQueue.java +++ b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/TaskQueue.java @@ -17,6 +17,8 @@ package cn.hippo4j.core.executor.support; +import lombok.Setter; + import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; @@ -28,16 +30,13 @@ public class TaskQueue extends LinkedBlockingQueue private static final long serialVersionUID = -2635853580887179627L; + @Setter private FastThreadPoolExecutor executor; public TaskQueue(int capacity) { super(capacity); } - public void setExecutor(FastThreadPoolExecutor exec) { - executor = exec; - } - @Override public boolean offer(Runnable runnable) { int currentPoolThreadSize = executor.getPoolSize(); @@ -54,10 +53,21 @@ public class TaskQueue extends LinkedBlockingQueue return super.offer(runnable); } - public boolean retryOffer(Runnable o, long timeout, TimeUnit unit) throws InterruptedException { + /** + * Retry offer. + * + * @param runnable submit thread pool task + * @param timeout how long to wait before giving up, in units of + * {@code unit} + * @param unit a {@code TimeUnit} determining how to interpret the + * {@code timeout} parameter + * @return + * @throws InterruptedException + */ + public boolean retryOffer(Runnable runnable, long timeout, TimeUnit unit) throws InterruptedException { if (executor.isShutdown()) { throw new RejectedExecutionException("Actuator closed!"); } - return super.offer(o, timeout, unit); + return super.offer(runnable, timeout, unit); } } diff --git a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/ThreadPoolBuilder.java b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/ThreadPoolBuilder.java index 7680b9d4..b1a830f9 100644 --- a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/ThreadPoolBuilder.java +++ b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/ThreadPoolBuilder.java @@ -17,6 +17,11 @@ package cn.hippo4j.core.executor.support; +import cn.hippo4j.common.design.builder.Builder; +import cn.hippo4j.common.executor.support.BlockingQueueTypeEnum; +import cn.hippo4j.common.toolkit.Assert; +import org.springframework.core.task.TaskDecorator; + import java.math.BigDecimal; import java.util.Optional; import java.util.concurrent.BlockingQueue; @@ -25,12 +30,6 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import cn.hippo4j.common.design.builder.Builder; -import cn.hippo4j.common.executor.support.BlockingQueueTypeEnum; -import cn.hippo4j.common.toolkit.Assert; - -import org.springframework.core.task.TaskDecorator; - /** * Thread-pool builder. */ @@ -74,42 +73,89 @@ public class ThreadPoolBuilder implements Builder { private Boolean allowCoreThreadTimeOut = false; + /** + * Calculate core num. + * + * @return core num + */ private Integer calculateCoreNum() { int cpuCoreNum = Runtime.getRuntime().availableProcessors(); return new BigDecimal(cpuCoreNum).divide(new BigDecimal("0.2")).intValue(); } + /** + * Is fast pool. + * + * @param isFastPool is fast pool + * @return thread-pool builder + */ public ThreadPoolBuilder isFastPool(Boolean isFastPool) { this.isFastPool = isFastPool; return this; } + /** + * Dynamic pool. + * + * @return thread-pool builder + */ public ThreadPoolBuilder dynamicPool() { this.isDynamicPool = true; return this; } + /** + * Thread factory. + * + * @param threadNamePrefix thread name prefix + * @return thread-pool builder + */ public ThreadPoolBuilder threadFactory(String threadNamePrefix) { this.threadNamePrefix = threadNamePrefix; return this; } + /** + * Thread factory. + * + * @param threadFactory thread factory + * @return thread-pool builder + */ public ThreadPoolBuilder threadFactory(ThreadFactory threadFactory) { this.threadFactory = threadFactory; return this; } + /** + * Thread factory. + * + * @param threadNamePrefix thread name prefix + * @param isDaemon is daemon + * @return thread-pool builder + */ public ThreadPoolBuilder threadFactory(String threadNamePrefix, Boolean isDaemon) { this.threadNamePrefix = threadNamePrefix; this.isDaemon = isDaemon; return this; } + /** + * Core pool size. + * + * @param corePoolSize core pool size + * @return thread-pool builder + */ public ThreadPoolBuilder corePoolSize(int corePoolSize) { this.corePoolSize = corePoolSize; return this; } + /** + * Max pool num. + * + * @param maxPoolSize max pool num + * @return thread-pool builder + */ public ThreadPoolBuilder maxPoolNum(int maxPoolSize) { this.maxPoolSize = maxPoolSize; if (maxPoolSize < this.corePoolSize) { @@ -118,6 +164,11 @@ public class ThreadPoolBuilder implements Builder { return this; } + /** + * Single pool. + * + * @return thread-pool builder + */ public ThreadPoolBuilder singlePool() { int singleNum = 1; this.corePoolSize = singleNum; @@ -125,6 +176,12 @@ public class ThreadPoolBuilder implements Builder { return this; } + /** + * Single pool. + * + * @param threadNamePrefix thread name prefix + * @return thread-pool builder + */ public ThreadPoolBuilder singlePool(String threadNamePrefix) { int singleNum = 1; this.corePoolSize = singleNum; @@ -133,128 +190,245 @@ public class ThreadPoolBuilder implements Builder { return this; } + /** + * Pool thread size. + * + * @param corePoolSize core pool size + * @param maxPoolSize max pool size + * @return thread-pool builder + */ public ThreadPoolBuilder poolThreadSize(int corePoolSize, int maxPoolSize) { this.corePoolSize = corePoolSize; this.maxPoolSize = maxPoolSize; return this; } + /** + * Keep alive time. + * + * @param keepAliveTime keep alive time + * @return thread-pool builder + */ public ThreadPoolBuilder keepAliveTime(long keepAliveTime) { this.keepAliveTime = keepAliveTime; return this; } + /** + * Time unit. + * + * @param timeUnit time unit + * @return thread-pool builder + */ public ThreadPoolBuilder timeUnit(TimeUnit timeUnit) { this.timeUnit = timeUnit; return this; } + /** + * Execute time-out. + * + * @param executeTimeOut execute time-out + * @return thread-pool builder + */ public ThreadPoolBuilder executeTimeOut(long executeTimeOut) { this.executeTimeOut = executeTimeOut; return this; } + /** + * Keep alive time. + * + * @param keepAliveTime keep alive time + * @param timeUnit time unit + * @return thread-pool builder + */ public ThreadPoolBuilder keepAliveTime(long keepAliveTime, TimeUnit timeUnit) { this.keepAliveTime = keepAliveTime; this.timeUnit = timeUnit; return this; } + /** + * Capacity. + * + * @param capacity capacity + * @return thread-pool builder + */ public ThreadPoolBuilder capacity(int capacity) { this.capacity = capacity; return this; } + /** + * Work queue. + * + * @param queueType queue type + * @param capacity capacity + * @return thread-pool builder + */ public ThreadPoolBuilder workQueue(BlockingQueueTypeEnum queueType, int capacity) { this.blockingQueueType = queueType; this.capacity = capacity; return this; } + /** + * Rejected. + * + * @param rejectedExecutionHandler rejected execution handler + * @return thread-pool builder + */ public ThreadPoolBuilder rejected(RejectedExecutionHandler rejectedExecutionHandler) { this.rejectedExecutionHandler = rejectedExecutionHandler; return this; } + /** + * Work queue. + * + * @param blockingQueueType blocking queue type + * @return thread-pool builder + */ public ThreadPoolBuilder workQueue(BlockingQueueTypeEnum blockingQueueType) { this.blockingQueueType = blockingQueueType; return this; } + /** + * Work queue. + * + * @param workQueue work queue + * @return thread-pool builder + */ public ThreadPoolBuilder workQueue(BlockingQueue workQueue) { this.workQueue = workQueue; return this; } + /** + * Thread-pool id. + * + * @param threadPoolId thread-pool id + * @return thread-pool builder + */ public ThreadPoolBuilder threadPoolId(String threadPoolId) { this.threadPoolId = threadPoolId; return this; } + /** + * Task decorator. + * + * @param taskDecorator task decorator + * @return thread-pool builder + */ public ThreadPoolBuilder taskDecorator(TaskDecorator taskDecorator) { this.taskDecorator = taskDecorator; return this; } + /** + * Await termination millis. + * + * @param awaitTerminationMillis await termination millis + * @return thread-pool builder + */ public ThreadPoolBuilder awaitTerminationMillis(long awaitTerminationMillis) { this.awaitTerminationMillis = awaitTerminationMillis; return this; } + /** + * Wait for tasks to complete on shutdown. + * + * @param waitForTasksToCompleteOnShutdown wait for tasks to complete on shutdown + * @return thread-pool builder + */ public ThreadPoolBuilder waitForTasksToCompleteOnShutdown(boolean waitForTasksToCompleteOnShutdown) { this.waitForTasksToCompleteOnShutdown = waitForTasksToCompleteOnShutdown; return this; } + /** + * Dynamic support. + * + * @param waitForTasksToCompleteOnShutdown wait for tasks to complete on shutdown + * @param awaitTerminationMillis await termination millis + * @return thread-pool builder + */ public ThreadPoolBuilder dynamicSupport(boolean waitForTasksToCompleteOnShutdown, long awaitTerminationMillis) { this.awaitTerminationMillis = awaitTerminationMillis; this.waitForTasksToCompleteOnShutdown = waitForTasksToCompleteOnShutdown; return this; } + /** + * Allow core thread time-out. + * + * @param allowCoreThreadTimeOut core thread time-out + * @return thread-pool builder + */ public ThreadPoolBuilder allowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) { this.allowCoreThreadTimeOut = allowCoreThreadTimeOut; return this; } - @Override - public ThreadPoolExecutor build() { - if (isDynamicPool) { - return buildDynamicPool(this); - } - return isFastPool ? buildFastPool(this) : buildPool(this); - } - + /** + * Builder design pattern implementation. + * + * @return thread-pool builder + */ public static ThreadPoolBuilder builder() { return new ThreadPoolBuilder(); } /** - * Create dynamic thread pool by thread pool id + * Create dynamic thread pool by thread pool id. * - * @param threadPoolId threadPoolId - * @return ThreadPoolExecutor + * @param threadPoolId thread-pool id + * @return dynamic thread-pool executor */ public static ThreadPoolExecutor buildDynamicPoolById(String threadPoolId) { - return ThreadPoolBuilder.builder() - .threadFactory(threadPoolId) - .threadPoolId(threadPoolId) - .dynamicPool() - .build(); + return ThreadPoolBuilder.builder().threadFactory(threadPoolId).threadPoolId(threadPoolId).dynamicPool().build(); } + /** + * Build a normal thread-pool with {@code builder}. + * + * @param builder thread-pool builder + * @return normal thread-pool + */ private static ThreadPoolExecutor buildPool(ThreadPoolBuilder builder) { return AbstractBuildThreadPoolTemplate.buildPool(buildInitParam(builder)); } + /** + * Build a fast thread-pool with {@code builder}. + * + * @param builder thread-pool builder + * @return fast thread-pool executor + */ private static ThreadPoolExecutor buildFastPool(ThreadPoolBuilder builder) { return AbstractBuildThreadPoolTemplate.buildFastPool(buildInitParam(builder)); } + /** + * Build a dynamic thread-pool with {@code builder}. + * + * @param builder thread-pool builder + * @return dynamic thread-pool executor + */ private static ThreadPoolExecutor buildDynamicPool(ThreadPoolBuilder builder) { return AbstractBuildThreadPoolTemplate.buildDynamicPool(buildInitParam(builder)); } + /** + * Build thread-pool initialization parameters via {@code builder}. + * + * @param builder thread-pool builder + * @return thread-pool init param + */ private static AbstractBuildThreadPoolTemplate.ThreadPoolInitParam buildInitParam(ThreadPoolBuilder builder) { AbstractBuildThreadPoolTemplate.ThreadPoolInitParam initParam; if (builder.threadFactory == null) { @@ -289,4 +463,12 @@ public class ThreadPoolBuilder implements Builder { } return initParam; } + + @Override + public ThreadPoolExecutor build() { + if (isDynamicPool) { + return buildDynamicPool(this); + } + return isFastPool ? buildFastPool(this) : buildPool(this); + } } diff --git a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/ThreadPoolExecutorTemplate.java b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/ThreadPoolExecutorTemplate.java index c60ecc4d..0a1d1faf 100644 --- a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/ThreadPoolExecutorTemplate.java +++ b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/ThreadPoolExecutorTemplate.java @@ -36,10 +36,6 @@ public class ThreadPoolExecutorTemplate extends ThreadPoolExecutor { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); } - private Exception clientTrace() { - return new Exception("Tread task root stack trace."); - } - @Override public void execute(final Runnable command) { super.execute(wrap(command, clientTrace())); @@ -55,6 +51,22 @@ public class ThreadPoolExecutorTemplate extends ThreadPoolExecutor { return super.submit(wrap(task, clientTrace())); } + /** + * Client trace. + * + * @return exception + */ + private Exception clientTrace() { + return new Exception("Tread task root stack trace."); + } + + /** + * Wrapping thread pool tasks. + * + * @param task task + * @param clientStack client stack + * @return wrapped runnable + */ private Runnable wrap(final Runnable task, final Exception clientStack) { return () -> { try { @@ -66,6 +78,14 @@ public class ThreadPoolExecutorTemplate extends ThreadPoolExecutor { }; } + /** + * Wrapping thread pool tasks. + * + * @param task task + * @param clientStack client stack + * @param computed result + * @return wrapped runnable + */ private Callable wrap(final Callable task, final Exception clientStack) { return () -> { try { From b73725f1591ace8c2b520c0859ea9546271bb6bf Mon Sep 17 00:00:00 2001 From: pizihao <48643103+pizihao@users.noreply.github.com> Date: Thu, 3 Nov 2022 09:56:31 +0800 Subject: [PATCH 12/18] Server-side and client-side model of rpc mode call based on netty (#880) * fix : add toolkit * feat : Implement rpc calls through netty, implement server side and client side respectively, the underlying network connection and pipeline context mechanism depend on netty(#812) * fix : Modifying the comment Format (#812) --- .../cn/hippo4j/common/toolkit/MemoryUtil.java | 2 +- .../hippo4j/common/toolkit/ReflectUtil.java | 14 ++ .../cn/hippo4j/config/rpc/client/Client.java | 34 +++++ .../config/rpc/client/ClientConnection.java | 45 ++++++ .../rpc/client/NettyClientConnection.java | 132 ++++++++++++++++++ .../hippo4j/config/rpc/client/RPCClient.java | 50 +++++++ .../rpc/coder/CompactObjectOutputStream.java | 53 +++++++ .../config/rpc/coder/NettyDecoder.java | 50 +++++++ .../config/rpc/coder/NettyEncoder.java | 52 +++++++ .../rpc/discovery/DiscoveryAdapter.java | 36 +++++ .../config/rpc/exception/CoderException.java | 48 +++++++ .../rpc/exception/ConnectionException.java | 49 +++++++ .../rpc/exception/TimeOutException.java | 48 +++++++ .../config/rpc/handler/ConnectHandler.java | 48 +++++++ .../config/rpc/handler/Connection.java | 27 ++++ .../rpc/handler/NettyClientPoolHandler.java | 55 ++++++++ .../rpc/handler/NettyClientTakeHandler.java | 61 ++++++++ .../rpc/handler/NettyServerTakeHandler.java | 115 +++++++++++++++ .../config/rpc/process/ActivePostProcess.java | 60 ++++++++ .../config/rpc/request/DefaultRequest.java | 121 ++++++++++++++++ .../hippo4j/config/rpc/request/Request.java | 52 +++++++ .../config/rpc/response/DefaultResponse.java | 122 ++++++++++++++++ .../hippo4j/config/rpc/response/Response.java | 57 ++++++++ .../rpc/server/NettyServerConnection.java | 109 +++++++++++++++ .../hippo4j/config/rpc/server/RPCServer.java | 47 +++++++ .../cn/hippo4j/config/rpc/server/Server.java | 33 +++++ .../config/rpc/server/ServerConnection.java | 32 +++++ .../config/rpc/support/ClassRegistry.java | 74 ++++++++++ .../config/rpc/support/DefaultInstance.java | 43 ++++++ .../hippo4j/config/rpc/support/Instance.java | 42 ++++++ .../config/rpc/support/NettyConnectPool.java | 100 +++++++++++++ .../rpc/support/NettyConnectPoolHolder.java | 111 +++++++++++++++ .../config/rpc/support/NettyProxyCenter.java | 88 ++++++++++++ .../config/rpc/support/ResultHolder.java | 89 ++++++++++++ .../rpc/support/SpringContextInstance.java | 37 +++++ .../config/rpc/client/CallManager.java | 26 ++++ .../config/rpc/client/RPCClientTest.java | 68 +++++++++ .../config/rpc/server/RPCServerTest.java | 64 +++++++++ .../config/rpc/support/ClassRegistryTest.java | 67 +++++++++ .../rpc/support/DefaultInstanceTest.java | 55 ++++++++ .../support/NettyConnectPoolHolderTest.java | 65 +++++++++ .../rpc/support/NettyConnectPoolTest.java | 106 ++++++++++++++ .../rpc/support/NettyProxyCenterTest.java | 44 ++++++ .../config/rpc/support/ResultHolderTest.java | 42 ++++++ 44 files changed, 2672 insertions(+), 1 deletion(-) create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/client/Client.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/client/ClientConnection.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/client/NettyClientConnection.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/client/RPCClient.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/coder/CompactObjectOutputStream.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/coder/NettyDecoder.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/coder/NettyEncoder.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/discovery/DiscoveryAdapter.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/exception/CoderException.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/exception/ConnectionException.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/exception/TimeOutException.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/handler/ConnectHandler.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/handler/Connection.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/handler/NettyClientPoolHandler.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/handler/NettyClientTakeHandler.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/handler/NettyServerTakeHandler.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/process/ActivePostProcess.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/request/DefaultRequest.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/request/Request.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/response/DefaultResponse.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/response/Response.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/server/NettyServerConnection.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/server/RPCServer.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/server/Server.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/server/ServerConnection.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/ClassRegistry.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/DefaultInstance.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/Instance.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/NettyConnectPool.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/NettyConnectPoolHolder.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/NettyProxyCenter.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/ResultHolder.java create mode 100644 hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/SpringContextInstance.java create mode 100644 hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/client/CallManager.java create mode 100644 hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/client/RPCClientTest.java create mode 100644 hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/server/RPCServerTest.java create mode 100644 hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/ClassRegistryTest.java create mode 100644 hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/DefaultInstanceTest.java create mode 100644 hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/NettyConnectPoolHolderTest.java create mode 100644 hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/NettyConnectPoolTest.java create mode 100644 hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/NettyProxyCenterTest.java create mode 100644 hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/ResultHolderTest.java diff --git a/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/MemoryUtil.java b/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/MemoryUtil.java index 3b62bab9..ad2b0678 100644 --- a/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/MemoryUtil.java +++ b/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/MemoryUtil.java @@ -26,7 +26,7 @@ import java.lang.management.MemoryUsage; /** * memory util
    - * the obtained information is not invalid, after a long wait, obtain it again + * the obtained information is not real time effective, after a long wait, please get it again * * @author liuwenhao */ diff --git a/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/ReflectUtil.java b/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/ReflectUtil.java index 9a4bfa86..d278ec55 100644 --- a/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/ReflectUtil.java +++ b/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/ReflectUtil.java @@ -250,4 +250,18 @@ public class ReflectUtil { throw new IllegalException(e); } } + + /** + * get instance + * + * @param cls the class + * @return new Instance + */ + public static Object createInstance(Class cls) { + try { + return cls.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new IllegalException(e); + } + } } diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/client/Client.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/client/Client.java new file mode 100644 index 00000000..e2c4a64e --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/client/Client.java @@ -0,0 +1,34 @@ +/* + * 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.config.rpc.client; + +import cn.hippo4j.config.rpc.request.Request; +import cn.hippo4j.config.rpc.response.Response; + +import java.io.Closeable; + +/** + * the client for RPC, Explain the role of the client in the request + */ +public interface Client extends Closeable { + + /** + * Start the client and try to send and receive data + */ + Response connection(Request request); +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/client/ClientConnection.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/client/ClientConnection.java new file mode 100644 index 00000000..f8b22c62 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/client/ClientConnection.java @@ -0,0 +1,45 @@ +/* + * 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.config.rpc.client; + +import cn.hippo4j.config.rpc.handler.Connection; +import cn.hippo4j.config.rpc.request.Request; +import cn.hippo4j.config.rpc.response.Response; + +/** + * Applicable to client connections + */ +public interface ClientConnection extends Connection { + + /** + * Establish a connection and process + * + * @param request Request information + */ + Response connect(Request request); + + /** + * Get timeout, ms + */ + long timeout(); + + /** + * SET timeout, ms + */ + void setTimeout(long timeout); +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/client/NettyClientConnection.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/client/NettyClientConnection.java new file mode 100644 index 00000000..0b02c48c --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/client/NettyClientConnection.java @@ -0,0 +1,132 @@ +/* + * 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.config.rpc.client; + +import cn.hippo4j.config.rpc.exception.TimeOutException; +import cn.hippo4j.config.rpc.process.ActivePostProcess; +import cn.hippo4j.config.rpc.request.Request; +import cn.hippo4j.config.rpc.response.Response; +import cn.hippo4j.config.rpc.support.NettyConnectPool; +import cn.hippo4j.config.rpc.support.NettyConnectPoolHolder; +import cn.hippo4j.config.rpc.support.ResultHolder; +import cn.hippo4j.common.toolkit.Assert; +import cn.hippo4j.common.web.exception.IllegalException; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import lombok.extern.slf4j.Slf4j; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.LockSupport; + +/** + * Client implemented using netty + */ +@Slf4j +public class NettyClientConnection implements ClientConnection { + + String host; + Integer port; + // Obtain the connection timeout period. The default value is 30s + long timeout = 30000L; + Channel channel; + EventLoopGroup worker = new NioEventLoopGroup(); + List activeProcesses; + ChannelFuture future; + NettyConnectPool connectionPool; + + public NettyClientConnection(String host, int port, + List activeProcesses) { + Assert.notNull(worker); + this.host = host; + this.port = port; + this.activeProcesses = activeProcesses; + this.connectionPool = NettyConnectPoolHolder.getPool(host, port, timeout, worker); + } + + public NettyClientConnection(String host, int port) { + this(host, port, new LinkedList<>()); + } + + @Override + public Response connect(Request request) { + preHandlers(request); + this.channel = connectionPool.acquire(timeout); + try { + String key = request.getKey(); + this.future = channel.writeAndFlush(request); + log.info("Call successful, target address is {}:{}, request key is {}", host, port, key); + // Wait for execution to complete + ResultHolder.put(key, Thread.currentThread()); + LockSupport.parkNanos(timeout() * 1000000); + Response response = ResultHolder.get(key); + if (response == null) { + throw new TimeOutException("Timeout waiting for server-side response"); + } + postHandlers(request, response); + log.info("The response from {}:{} was received successfully with the response key {}.", host, port, key); + return response; + } catch (Exception ex) { + afterCompletions(request, null, ex); + throw new IllegalException(ex); + } finally { + connectionPool.release(this.channel); + } + } + + @Override + public long timeout() { + return timeout; + } + + @Override + public void setTimeout(long timeout) { + this.timeout = timeout; + } + + @Override + public void close() { + if (this.channel == null) { + return; + } + worker.shutdownGracefully(); + this.future.channel().close(); + this.channel.close(); + } + + private void preHandlers(Request request) { + for (ActivePostProcess process : activeProcesses) { + process.preHandler(request); + } + } + + private void postHandlers(Request request, Response response) { + for (ActivePostProcess process : activeProcesses) { + process.postHandler(request, response); + } + } + + private void afterCompletions(Request request, Response response, Exception e) { + for (ActivePostProcess process : activeProcesses) { + process.afterCompletion(request, response, e); + } + } + +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/client/RPCClient.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/client/RPCClient.java new file mode 100644 index 00000000..8b8451c8 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/client/RPCClient.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.hippo4j.config.rpc.client; + +import cn.hippo4j.config.rpc.request.Request; +import cn.hippo4j.config.rpc.response.Response; + +import java.io.IOException; + +/** + * The client, which provides a closing mechanism, maintains a persistent connection if not closed + */ +public class RPCClient implements Client { + + ClientConnection clientConnection; + + public RPCClient(ClientConnection clientConnection) { + this.clientConnection = clientConnection; + } + + @Override + public Response connection(Request request) { + return clientConnection.connect(request); + } + + /** + * Close the client and release all connections. + * + * @throws IOException exception + */ + @Override + public void close() throws IOException { + clientConnection.close(); + } +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/coder/CompactObjectOutputStream.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/coder/CompactObjectOutputStream.java new file mode 100644 index 00000000..a8592ef0 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/coder/CompactObjectOutputStream.java @@ -0,0 +1,53 @@ +/* + * 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.config.rpc.coder; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamClass; +import java.io.OutputStream; + +/** + * object OutputStream + */ +public class CompactObjectOutputStream extends ObjectOutputStream { + + static final int TYPE_FAT_DESCRIPTOR = 0; + static final int TYPE_THIN_DESCRIPTOR = 1; + + public CompactObjectOutputStream(OutputStream out) throws IOException { + super(out); + } + + @Override + protected void writeStreamHeader() throws IOException { + writeByte(STREAM_VERSION); + } + + @Override + protected void writeClassDescriptor(ObjectStreamClass desc) throws IOException { + Class clazz = desc.forClass(); + if (clazz.isPrimitive() || clazz.isArray() || clazz.isInterface() || desc.getSerialVersionUID() == 0) { + write(TYPE_FAT_DESCRIPTOR); + super.writeClassDescriptor(desc); + } else { + write(TYPE_THIN_DESCRIPTOR); + writeUTF(desc.getName()); + } + } +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/coder/NettyDecoder.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/coder/NettyDecoder.java new file mode 100644 index 00000000..334fa4c5 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/coder/NettyDecoder.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.hippo4j.config.rpc.coder; + +import cn.hippo4j.config.rpc.exception.CoderException; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.serialization.ClassResolver; +import io.netty.handler.codec.serialization.ObjectDecoder; + +/** + * According to the decoder for java objects implemented by ObjectDecoder, + * it is necessary to ensure that the transmitted objects can be serialized + */ +public class NettyDecoder extends ObjectDecoder { + + public NettyDecoder(ClassResolver classResolver) { + super(classResolver); + } + + @Override + protected Object decode(ChannelHandlerContext ctx, ByteBuf in) { + ByteBuf byteBuf = in.retainedDuplicate(); + try { + Object o = super.decode(ctx, in); + if (o == null) { + return byteBuf; + } else { + return o; + } + } catch (Exception e) { + throw new CoderException("The encoding is abnormal, which may be caused by the failure of the transfer object to be deserialized"); + } + } +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/coder/NettyEncoder.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/coder/NettyEncoder.java new file mode 100644 index 00000000..48ebe628 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/coder/NettyEncoder.java @@ -0,0 +1,52 @@ +/* + * 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.config.rpc.coder; + +import cn.hippo4j.config.rpc.exception.CoderException; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufOutputStream; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; + +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * this is a encoder, For custom gluing and unpacking
    + * {@link io.netty.handler.codec.serialization.ObjectEncoder} + */ +public class NettyEncoder extends MessageToByteEncoder { + + private static final byte[] BYTE = new byte[4]; + + @Override + protected void encode(ChannelHandlerContext ctx, Serializable msg, ByteBuf out) throws Exception { + int startIndex = out.writerIndex(); + try (ByteBufOutputStream outPut = new ByteBufOutputStream(out)) { + outPut.write(BYTE); + try (ObjectOutputStream outputStream = new CompactObjectOutputStream(outPut)) { + outputStream.writeObject(msg); + outputStream.flush(); + } + } catch (Exception e) { + throw new CoderException("The encoding is abnormal, which may be caused by the transfer object being unable to be serialized"); + } + int endIndex = out.writerIndex(); + out.setInt(startIndex, endIndex - startIndex - 4); + } +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/discovery/DiscoveryAdapter.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/discovery/DiscoveryAdapter.java new file mode 100644 index 00000000..d91fe15f --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/discovery/DiscoveryAdapter.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.hippo4j.config.rpc.discovery; + +import java.net.InetSocketAddress; + +/** + * The adaptation layer of different service centers is used to know + * the host of different services through the registration center + */ +public interface DiscoveryAdapter { + + /** + * get InetSocketAddress served in the registry + * + * @param name server name + * @return InetSocketAddress + */ + InetSocketAddress getSocketAddress(String name); + +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/exception/CoderException.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/exception/CoderException.java new file mode 100644 index 00000000..aa0e107a --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/exception/CoderException.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.config.rpc.exception; + +/** + * During decoding and encoding, if an exception occurs, an exception of type {@link CoderException} is thrown, + * which is not different from a {@link RuntimeException}, but is more explicit about the type of exception + */ +public class CoderException extends RuntimeException { + + private static final long serialVersionUID = 8247610319171014183L; + + public CoderException() { + super(); + } + + public CoderException(String message) { + super(message); + } + + public CoderException(Throwable e) { + super(e.getMessage(), e); + } + + public CoderException(String message, Throwable throwable) { + super(message, throwable); + } + + public CoderException(String message, Throwable throwable, boolean enableSuppression, boolean writableStackTrace) { + super(message, throwable, enableSuppression, writableStackTrace); + } + +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/exception/ConnectionException.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/exception/ConnectionException.java new file mode 100644 index 00000000..e43a0465 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/exception/ConnectionException.java @@ -0,0 +1,49 @@ +/* + * 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.config.rpc.exception; + +/** + * If an exception occurs during the connection between the server and the client, an exception of type + * {@link ConnectionException} is thrown, which is not different from {@link RuntimeException}, but is more explicit + * about the type of exception + */ +public class ConnectionException extends RuntimeException { + + private static final long serialVersionUID = 8247610319171014183L; + + public ConnectionException() { + super(); + } + + public ConnectionException(String message) { + super(message); + } + + public ConnectionException(Throwable e) { + super(e.getMessage(), e); + } + + public ConnectionException(String message, Throwable throwable) { + super(message, throwable); + } + + public ConnectionException(String message, Throwable throwable, boolean enableSuppression, boolean writableStackTrace) { + super(message, throwable, enableSuppression, writableStackTrace); + } + +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/exception/TimeOutException.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/exception/TimeOutException.java new file mode 100644 index 00000000..50b902d9 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/exception/TimeOutException.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.config.rpc.exception; + +/** + * If there is a timeout between the server and the client, you will get a {@link TimeOutException}, + * which is not different from {@link RuntimeException}, but it will be more explicit about the type of exception, right + */ +public class TimeOutException extends RuntimeException { + + private static final long serialVersionUID = 8247610319171014183L; + + public TimeOutException() { + super(); + } + + public TimeOutException(String message) { + super(message); + } + + public TimeOutException(Throwable e) { + super(e.getMessage(), e); + } + + public TimeOutException(String message, Throwable throwable) { + super(message, throwable); + } + + public TimeOutException(String message, Throwable throwable, boolean enableSuppression, boolean writableStackTrace) { + super(message, throwable, enableSuppression, writableStackTrace); + } + +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/handler/ConnectHandler.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/handler/ConnectHandler.java new file mode 100644 index 00000000..4170e5b5 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/handler/ConnectHandler.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.config.rpc.handler; + +import cn.hippo4j.config.rpc.request.Request; +import cn.hippo4j.config.rpc.response.Response; + +/** + * The handler in each connection, where the specific behavior of the connection + * must be specified, such as serialization and parsing, requesting and receiving + * requests, and so on + */ +public interface ConnectHandler { + + /** + * Processing after receiving the request + * + * @param request request + */ + default Response handler(Request request) { + return null; + } + + /** + * Processing after receiving Response + * + * @param response response + */ + default void handler(Response response) { + // + } + +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/handler/Connection.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/handler/Connection.java new file mode 100644 index 00000000..b3b69d57 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/handler/Connection.java @@ -0,0 +1,27 @@ +/* + * 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.config.rpc.handler; + +import java.io.Closeable; + +/** + * Represents a network request connection and provides IO layer support + */ +public interface Connection extends Closeable { + +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/handler/NettyClientPoolHandler.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/handler/NettyClientPoolHandler.java new file mode 100644 index 00000000..bb124e81 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/handler/NettyClientPoolHandler.java @@ -0,0 +1,55 @@ +/* + * 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.config.rpc.handler; + +import cn.hippo4j.config.rpc.coder.NettyDecoder; +import cn.hippo4j.config.rpc.coder.NettyEncoder; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.pool.ChannelPoolHandler; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.serialization.ClassResolvers; +import lombok.extern.slf4j.Slf4j; + +/** + * Processing by the client connection pool handler to clean the buffer and define new connection properties + */ +@Slf4j +public class NettyClientPoolHandler implements ChannelPoolHandler { + + @Override + public void channelReleased(Channel ch) { + ch.writeAndFlush(Unpooled.EMPTY_BUFFER); + log.info("The connection buffer has been emptied of data"); + } + + @Override + public void channelAcquired(Channel ch) { + // NO SOMETHING + } + + @Override + public void channelCreated(Channel ch) { + NioSocketChannel channel = (NioSocketChannel) ch; + channel.config() + .setTcpNoDelay(false); + ch.pipeline().addLast(new NettyDecoder(ClassResolvers.cacheDisabled(null))); + ch.pipeline().addLast(new NettyEncoder()); + ch.pipeline().addLast(new NettyClientTakeHandler()); + } +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/handler/NettyClientTakeHandler.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/handler/NettyClientTakeHandler.java new file mode 100644 index 00000000..9e459a4b --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/handler/NettyClientTakeHandler.java @@ -0,0 +1,61 @@ +/* + * 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.config.rpc.handler; + +import cn.hippo4j.config.rpc.exception.ConnectionException; +import cn.hippo4j.config.rpc.support.ResultHolder; +import cn.hippo4j.config.rpc.response.Response; +import cn.hippo4j.common.web.exception.IllegalException; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; + +/** + * Interconnect with the netty mediation layer + */ +public class NettyClientTakeHandler extends ChannelInboundHandlerAdapter implements ConnectHandler { + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + try { + Response response = (Response) msg; + handler(response); + ctx.flush(); + } catch (Exception e) { + ctx.close(); + throw new IllegalException(e); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + super.exceptionCaught(ctx, cause); + Channel channel = ctx.channel(); + if (channel.isActive()) { + ctx.close(); + } else { + throw new ConnectionException(cause); + } + } + + @Override + public void handler(Response response) { + ResultHolder.put(response.getKey(), response); + ResultHolder.wake(response.getKey()); + } +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/handler/NettyServerTakeHandler.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/handler/NettyServerTakeHandler.java new file mode 100644 index 00000000..cac4ad6e --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/handler/NettyServerTakeHandler.java @@ -0,0 +1,115 @@ +/* + * 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.config.rpc.handler; + +import cn.hippo4j.config.rpc.exception.ConnectionException; +import cn.hippo4j.config.rpc.process.ActivePostProcess; +import cn.hippo4j.config.rpc.response.DefaultResponse; +import cn.hippo4j.config.rpc.support.ClassRegistry; +import cn.hippo4j.config.rpc.support.Instance; +import cn.hippo4j.config.rpc.request.Request; +import cn.hippo4j.config.rpc.response.Response; +import cn.hippo4j.common.toolkit.Assert; +import cn.hippo4j.common.toolkit.ReflectUtil; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; + +import java.lang.reflect.Method; +import java.util.LinkedList; +import java.util.List; + +/** + * netty adaptation layer + */ +public class NettyServerTakeHandler extends ChannelInboundHandlerAdapter implements ConnectHandler { + + List processes; + Instance instance; + + public NettyServerTakeHandler(List processes, Instance instance) { + this.processes = processes; + this.instance = instance; + } + + public NettyServerTakeHandler(Instance instance) { + this(new LinkedList<>(), instance); + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + if (!(msg instanceof Request)) { + return; + } + Request request = (Request) msg; + Response response = handler(request); + ctx.writeAndFlush(response); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + Channel channel = ctx.channel(); + if (channel.isActive()) { + ctx.close(); + } else { + throw new ConnectionException(cause); + } + } + + @Override + public Response handler(Request request) { + if (!preHandlers(request)) { + return null; + } + try { + Class cls = ClassRegistry.get(request.getClassName()); + Method method = ReflectUtil.getMethodByName(cls, request.getMethodName(), request.getParameterTypes()); + Assert.notNull(method); + Object invoke = ReflectUtil.invoke(instance.getInstance(cls), method, request.getParameters()); + Response response = new DefaultResponse(request.getKey(), invoke.getClass(), invoke); + postHandlers(request, response); + return response; + } catch (Exception e) { + Response response = new DefaultResponse(request.getKey(), e, e.getMessage()); + afterCompletions(request, response, e); + return response; + } + } + + private boolean preHandlers(Request request) { + for (ActivePostProcess process : processes) { + if (!process.preHandler(request)) { + return false; + } + } + return true; + } + + private void postHandlers(Request request, Response response) { + for (ActivePostProcess process : processes) { + process.postHandler(request, response); + } + } + + private void afterCompletions(Request request, Response response, Exception e) { + for (ActivePostProcess process : processes) { + process.afterCompletion(request, response, e); + } + } + +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/process/ActivePostProcess.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/process/ActivePostProcess.java new file mode 100644 index 00000000..98df5998 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/process/ActivePostProcess.java @@ -0,0 +1,60 @@ +/* + * 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.config.rpc.process; + +import cn.hippo4j.config.rpc.request.Request; +import cn.hippo4j.config.rpc.response.Response; + +/** + * Callback while the connection is in progress + */ +public interface ActivePostProcess { + + /** + * Client: After establishing a connection and before passing parameters
    + * Server: Receives parameters and performs pre-call operations
    + * + * @param request request + * @return Whether to continue the execution. If it is a client, the returned value does not affect subsequent execution + */ + default boolean preHandler(Request request) { + return true; + } + + /** + * Client: Action after receiving a response
    + * Server: performs the operation after the call
    + * + * @param request request + * @param response response + */ + default void postHandler(Request request, Response response) { + // NO SOMETHING + } + + /** + * Called when an exception or resource is cleaned + * + * @param request request + * @param response response + * @param e Exception + */ + default void afterCompletion(Request request, Response response, Exception e) { + // NO SOMETHING + } +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/request/DefaultRequest.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/request/DefaultRequest.java new file mode 100644 index 00000000..6b82a102 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/request/DefaultRequest.java @@ -0,0 +1,121 @@ +/* + * 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.config.rpc.request; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Objects; + +/** + * default request
    + * Use the fully qualified name key of the interface and override equals and hashCode + */ +public final class DefaultRequest implements Request { + + String key; + String className; + String methodName; + Class[] parameterTypes; + transient Object[] parameters; + + public DefaultRequest(String key, String className, String methodName, Class[] parameterTypes, Object[] parameters) { + this.key = key; + this.className = className; + this.methodName = methodName; + this.parameterTypes = parameterTypes; + this.parameters = parameters; + } + + @Override + public String getKey() { + return key; + } + + @Override + public String getClassName() { + return className; + } + + @Override + public String getMethodName() { + return methodName; + } + + @Override + public Class[] getParameterTypes() { + return parameterTypes; + } + + @Override + public Object[] getParameters() { + return parameters; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + DefaultRequest that = (DefaultRequest) o; + return Objects.equals(key, that.key) + && Objects.equals(className, that.className) + && Objects.equals(methodName, that.methodName); + } + + @Override + public int hashCode() { + return Objects.hash(key, className, methodName); + } + + /** + * Redefine the behavior of serialization, that is, re-acquire the initially serialized + * data from the stream and re-serialize it. Simple serialization will result in the + * loss of the field identified by transient. + */ + private void writeObject(ObjectOutputStream s) throws IOException { + s.defaultWriteObject(); + if (parameters == null) { + return; + } + // 序列化属性 parameters + for (Object parameter : parameters) { + s.writeObject(parameter); + } + } + + /** + * Redefine the deserialization behavior, and sequentially deserialize the data specified during + * serialization, because there is data that is not deserialized during initial deserialization, + * such as fields defined by transient + */ + private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { + s.defaultReadObject(); + if (parameterTypes == null) { + return; + } + // 反序列化属性 parameters + int length = parameterTypes.length; + Object[] a = new Object[length]; + for (int i = 0; i < length; i++) { + a[i] = s.readObject(); + } + this.parameters = a; + } +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/request/Request.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/request/Request.java new file mode 100644 index 00000000..a045fbbf --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/request/Request.java @@ -0,0 +1,52 @@ +/* + * 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.config.rpc.request; + +import java.io.Serializable; + +/** + * request + */ +public interface Request extends Serializable { + + /** + * The unique identity of the current request + */ + String getKey(); + + /** + * The Class name of the current request + */ + String getClassName(); + + /** + * The Method name of the current request + */ + String getMethodName(); + + /** + * The parameter type of the current request + */ + Class[] getParameterTypes(); + + /** + * The parameters of the current request + */ + Object[] getParameters(); + +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/response/DefaultResponse.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/response/DefaultResponse.java new file mode 100644 index 00000000..408d299f --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/response/DefaultResponse.java @@ -0,0 +1,122 @@ +/* + * 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.config.rpc.response; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Objects; + +/** + * default request
    + * Use the fully qualified name key of the interface and override equals and hashCode + */ +public class DefaultResponse implements Response { + + String key; + Class cls; + transient Object obj; + Throwable throwable; + String errMsg; + + public DefaultResponse(String key, Class cls, Object obj, Throwable throwable, String errMsg) { + this.key = key; + this.cls = cls; + this.obj = obj; + this.throwable = throwable; + this.errMsg = errMsg; + } + + public DefaultResponse(String key, Throwable throwable, String errMsg) { + this(key, null, null, throwable, errMsg); + } + + public DefaultResponse(String key, Class cls, Object obj) { + this(key, cls, obj, null, null); + } + + @Override + public String getKey() { + return key; + } + + @Override + public Class getCls() { + return cls; + } + + @Override + public Object getObj() { + return obj; + } + + @Override + public Throwable getThrowable() { + return throwable; + } + + @Override + public String getErrMsg() { + return errMsg; + } + + @Override + public boolean isErr() { + return throwable != null || errMsg != null; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + DefaultResponse that = (DefaultResponse) o; + return Objects.equals(key, that.key) && Objects.equals(cls, that.cls); + } + + @Override + public int hashCode() { + return Objects.hash(key, cls); + } + + /** + * Redefine the behavior of serialization, that is, re-acquire the initially serialized + * data from the stream and re-serialize it. Simple serialization will result in the + * loss of the field identified by transient. + */ + private void writeObject(ObjectOutputStream s) throws IOException { + s.defaultWriteObject(); + if (obj == null) { + return; + } + // 序列化属性 obj + s.writeObject(this.obj); + } + + /** + * Redefine the deserialization behavior, and sequentially deserialize the data specified during + * serialization, because there is data that is not deserialized during initial deserialization, + * such as fields defined by transient + */ + private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { + s.defaultReadObject(); + // 反序列化属性 obj + this.obj = s.readObject(); + } +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/response/Response.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/response/Response.java new file mode 100644 index 00000000..3c06fbaa --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/response/Response.java @@ -0,0 +1,57 @@ +/* + * 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.config.rpc.response; + +import java.io.Serializable; + +/** + * Response + */ +public interface Response extends Serializable { + + /** + * The unique identity of the current Response + */ + String getKey(); + + /** + * The class of the current Response, The target of deserialization + */ + Class getCls(); + + /** + * The results of this request can be obtained, The source of deserialization + */ + Object getObj(); + + /** + * The Throwable of the current Response + */ + Throwable getThrowable(); + + /** + * the error message + */ + String getErrMsg(); + + /** + * Whether the current request has an error + */ + boolean isErr(); + +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/server/NettyServerConnection.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/server/NettyServerConnection.java new file mode 100644 index 00000000..cb240442 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/server/NettyServerConnection.java @@ -0,0 +1,109 @@ +/* + * 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.config.rpc.server; + +import cn.hippo4j.config.rpc.coder.NettyDecoder; +import cn.hippo4j.config.rpc.coder.NettyEncoder; +import cn.hippo4j.config.rpc.handler.NettyServerTakeHandler; +import cn.hippo4j.config.rpc.process.ActivePostProcess; +import cn.hippo4j.config.rpc.support.Instance; +import cn.hippo4j.common.toolkit.Assert; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.*; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.serialization.ClassResolvers; +import lombok.extern.slf4j.Slf4j; + +import java.util.LinkedList; +import java.util.List; + +/** + * adapter to the netty server + */ +@Slf4j +public class NettyServerConnection implements ServerConnection { + + Integer port; + EventLoopGroup leader; + EventLoopGroup worker; + Class socketChannelCls = NioServerSocketChannel.class; + List processes; + Instance instance; + ChannelFuture future; + + public NettyServerConnection(EventLoopGroup leader, EventLoopGroup worker, List processes, Instance instance) { + Assert.notNull(processes); + Assert.notNull(instance); + Assert.notNull(leader); + Assert.notNull(worker); + this.leader = leader; + this.worker = worker; + this.processes = processes; + this.instance = instance; + } + + public NettyServerConnection(EventLoopGroup leader, EventLoopGroup worker, Instance instance) { + this(leader, worker, new LinkedList<>(), instance); + } + + public NettyServerConnection(List processes, Instance instance) { + this(new NioEventLoopGroup(), new NioEventLoopGroup(), processes, instance); + } + + public NettyServerConnection(Instance instance) { + this(new NioEventLoopGroup(), new NioEventLoopGroup(), new LinkedList<>(), instance); + } + + @Override + public void bind(int port) { + ServerBootstrap server = new ServerBootstrap(); + server.group(leader, worker) + .channel(socketChannelCls) + .childOption(ChannelOption.TCP_NODELAY, true) + .childHandler(new ChannelInitializer() { + + @Override + protected void initChannel(SocketChannel ch) throws Exception { + ch.pipeline().addLast(new NettyDecoder(ClassResolvers.cacheDisabled(null))); + ch.pipeline().addLast(new NettyEncoder()); + ch.pipeline().addLast(new NettyServerTakeHandler(processes, instance)); + } + }); + try { + this.future = server.bind(port); + log.info("The server is started and can receive requests. The listening port is {}", port); + this.port = port; + this.future.channel().closeFuture().sync(); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + } + + @Override + public void close() { + if (port == null) { + return; + } + leader.shutdownGracefully(); + worker.shutdownGracefully(); + this.future.channel().close(); + log.info("The server is shut down and no more requests are received. The release port is {}", port); + } +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/server/RPCServer.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/server/RPCServer.java new file mode 100644 index 00000000..b961aaf6 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/server/RPCServer.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.config.rpc.server; + +import java.io.IOException; + +/** + * Server Implementation + */ +public class RPCServer implements Server { + + int port; + ServerConnection serverConnection; + + public RPCServer(int port, ServerConnection serverConnection) { + this.port = port; + this.serverConnection = serverConnection; + } + + @Override + public void bind() { + serverConnection.bind(port); + } + + /** + * Shut down the server and release the port + */ + @Override + public void close() throws IOException { + serverConnection.close(); + } +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/server/Server.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/server/Server.java new file mode 100644 index 00000000..69056286 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/server/Server.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.config.rpc.server; + +import java.io.Closeable; + +/** + * the service for RPC, Explain the role of the service in the request + */ +public interface Server extends Closeable { + + /** + * Start the server. Attempt to listen on the port and receive the request.
    + * If the port being processed is already bound, an exception is thrown + */ + void bind(); + +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/server/ServerConnection.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/server/ServerConnection.java new file mode 100644 index 00000000..1e1d8a4b --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/server/ServerConnection.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.hippo4j.config.rpc.server; + +import cn.hippo4j.config.rpc.handler.Connection; + +/** + * This applies to server-side connections + */ +public interface ServerConnection extends Connection { + + /** + * Bind ports and process them + */ + void bind(int port); + +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/ClassRegistry.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/ClassRegistry.java new file mode 100644 index 00000000..4b938550 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/ClassRegistry.java @@ -0,0 +1,74 @@ +/* + * 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.config.rpc.support; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * the registration center for Client and Server + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ClassRegistry { + + private static final Map> serverRegister = new ConcurrentHashMap<>(); + + /** + * get a Obj in Registry center
    + * + * @param s key + * @return t element + */ + public static Class get(String s) { + return serverRegister.get(s); + } + + /** + * add the element to Registry Table
    + * if the key already exists, failure, and return before the value of the key.
    + * if success return the element + * + * @param s key + * @param cls element + * @return final mapped value + */ + public static Class set(String s, Class cls) { + return serverRegister.putIfAbsent(s, cls); + } + + /** + * add the element to Registry Table
    + * if the key already exists, failure, replace it + * + * @param s key + * @param cls element + */ + public static Class put(String s, Class cls) { + return serverRegister.put(s, cls); + } + + /** + * clear + */ + public static void clear() { + serverRegister.clear(); + } +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/DefaultInstance.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/DefaultInstance.java new file mode 100644 index 00000000..1c5ec559 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/DefaultInstance.java @@ -0,0 +1,43 @@ +/* + * 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.config.rpc.support; + +import cn.hippo4j.common.toolkit.ReflectUtil; +import cn.hippo4j.common.web.exception.IllegalException; + +/** + * Simply creating an instance of a class by its name and its specific type, + * and then throwing an exception if it is an interface, is not elegant + */ +public class DefaultInstance implements Instance { + + @Override + public Object getInstance(Class cls) { + return ReflectUtil.createInstance(cls); + } + + @Override + public Object getInstance(String name) { + try { + Class cls = Class.forName(name); + return getInstance(cls); + } catch (ClassNotFoundException e) { + throw new IllegalException(e); + } + } +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/Instance.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/Instance.java new file mode 100644 index 00000000..e1b7f33a --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/Instance.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.hippo4j.config.rpc.support; + +/** + * Instance interface to get an instance + */ +public interface Instance { + + /** + * get a instance + * + * @param cls Class object + * @return Information about instances created or found + */ + Object getInstance(Class cls); + + /** + * Gets an instance of a class with a recognizable identity, + * which can be the fully qualified name of class. It can also be a unique name in a container + * + * @param name Identifying name + * @return Information about instances created or found + */ + Object getInstance(String name); + +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/NettyConnectPool.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/NettyConnectPool.java new file mode 100644 index 00000000..4268ba4c --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/NettyConnectPool.java @@ -0,0 +1,100 @@ +/* + * 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.config.rpc.support; + +import cn.hippo4j.config.rpc.exception.ConnectionException; +import cn.hippo4j.config.rpc.handler.NettyClientPoolHandler; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.pool.ChannelHealthChecker; +import io.netty.channel.pool.ChannelPool; +import io.netty.channel.pool.ChannelPoolHandler; +import io.netty.channel.pool.FixedChannelPool; +import io.netty.util.concurrent.Future; +import lombok.extern.slf4j.Slf4j; + +import java.net.InetSocketAddress; +import java.util.concurrent.TimeUnit; + +/** + * This parameter applies only to the connection pool of netty + */ +@Slf4j +public class NettyConnectPool { + + ChannelHealthChecker healthCheck = ChannelHealthChecker.ACTIVE; + FixedChannelPool.AcquireTimeoutAction acquireTimeoutAction = FixedChannelPool.AcquireTimeoutAction.NEW; + int maxPendingAcquires = Integer.MAX_VALUE; + ChannelPoolHandler handler = new NettyClientPoolHandler(); + ChannelPool pool; + String host; + int port; + + public NettyConnectPool(String host, int port, int maxConnect, + long timeout, EventLoopGroup worker, + Class socketChannelCls) { + InetSocketAddress socketAddress = InetSocketAddress.createUnresolved(host, port); + Bootstrap bootstrap = new Bootstrap() + .group(worker) + .channel(socketChannelCls) + .remoteAddress(socketAddress); + this.host = host; + this.port = port; + this.pool = new FixedChannelPool(bootstrap, handler, healthCheck, acquireTimeoutAction, + timeout, maxConnect, maxPendingAcquires, true, true); + log.info("The connection pool is established with the connection target {}:{}", host, port); + NettyConnectPoolHolder.createPool(host, port, this); + } + + public Channel acquire(long timeoutMillis) { + try { + Future fch = pool.acquire(); + return fch.get(timeoutMillis, TimeUnit.MILLISECONDS); + } catch (Exception e) { + throw new ConnectionException("Failed to get the connection", e); + } + } + + public Future acquire() { + try { + return pool.acquire(); + } catch (Exception e) { + throw new ConnectionException("Failed to get the connection", e); + } + } + + public void release(Channel channel) { + try { + if (channel != null) { + pool.release(channel); + } + } catch (Exception e) { + throw new ConnectionException("Failed to release the connection", e); + } + } + + public void close() { + try { + pool.close(); + NettyConnectPoolHolder.remove(host, port); + } catch (Exception e) { + throw new ConnectionException("Failed to close the connection pool", e); + } + } +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/NettyConnectPoolHolder.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/NettyConnectPoolHolder.java new file mode 100644 index 00000000..d7cb7b86 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/NettyConnectPoolHolder.java @@ -0,0 +1,111 @@ +/* + * 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.config.rpc.support; + +import io.netty.channel.Channel; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.util.concurrent.EventExecutorGroup; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * To avoid creating multiple connection pools for the same host:port, save all connection pools of the client + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class NettyConnectPoolHolder { + + static int maxConnect = 64; + + static Map connectPoolMap = new ConcurrentHashMap<>(); + + private static NettyConnectPool initPool(String host, int port, + long timeout, EventLoopGroup worker) { + return new NettyConnectPool( + host, port, maxConnect, + timeout, worker, + NioSocketChannel.class); + } + + private static String getKey(String host, int port) { + return host + ":" + port; + } + + /** + * The connection pool connectPoolMapping may already exist before the connection pool + * connectPoolMapping is established. In this case, the connection pool is directly overwritten + * + * @param host the host + * @param port the port + * @param pool This parameter applies only to the connection pool of netty + */ + public static void createPool(String host, int port, NettyConnectPool pool) { + connectPoolMap.put(getKey(host, port), pool); + } + + /** + * Gets a connection pool, or null if there is no corresponding connectPoolMapping + * + * @param host the host + * @param port the port + * @return Map to the connection pool + */ + public static NettyConnectPool getPool(String host, int port) { + return connectPoolMap.get(getKey(host, port)); + } + + /** + * Gets a connection pool, and if there is no connectPoolMapping, creates one with the values provided and joins the connectPoolMapping + * + * @param host the host + * @param port the port + * @param timeout timeout + * @param worker Special {@link EventExecutorGroup} which allows registering {@link Channel}s + * that get processed for later selection during the event loop. + * @return Map to the connection pool + */ + public static synchronized NettyConnectPool getPool(String host, int port, + long timeout, EventLoopGroup worker) { + /* + * this cannot use the computeIfAbsent method directly here because put is already used in init. + * Details refer to https://bugs.openjdk.java.net/browse/JDK-8062841 + */ + NettyConnectPool pool = getPool(host, port); + return pool == null ? initPool(host, port, timeout, worker) : pool; + } + + /** + * Disconnect a connection connectPoolMapping. This must take effect at the same time as the connection pool is closed + * + * @param host host + * @param port port + */ + public static void remove(String host, int port) { + connectPoolMap.remove(getKey(host, port)); + } + + /** + * clear + */ + public static void clear() { + connectPoolMap.clear(); + } +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/NettyProxyCenter.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/NettyProxyCenter.java new file mode 100644 index 00000000..6ba1dcf6 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/NettyProxyCenter.java @@ -0,0 +1,88 @@ +/* + * 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.config.rpc.support; + +import cn.hippo4j.config.rpc.request.DefaultRequest; +import cn.hippo4j.config.rpc.client.NettyClientConnection; +import cn.hippo4j.config.rpc.request.Request; +import cn.hippo4j.config.rpc.response.Response; +import cn.hippo4j.common.toolkit.IdUtil; +import cn.hippo4j.common.web.exception.IllegalException; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.util.HashMap; +import java.util.Map; + +/** + * Add a proxy for the request, {@link Proxy} and {@link InvocationHandler} + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class NettyProxyCenter { + + // cache + static Map, Object> map = new HashMap<>(); + + /** + * 通过一个接口得到一个适用于PRC的代理对象 + * + * @param cls 接口类型 + * @param host 请求地址 + * @param port 端口 + * @param 对象类型 + * @return 代理对象 + */ + public static T getProxy(Class cls, String host, int port) { + NettyClientConnection connection = new NettyClientConnection(host, port); + return getProxy(connection, cls, host, port); + } + + @SuppressWarnings("unchecked") + public static T getProxy(NettyClientConnection connection, Class cls, String host, int port) { + boolean b = cls.isInterface(); + if (!b) { + throw new IllegalException(cls.getName() + "is not a Interface"); + } + Object o = map.get(cls); + if (o != null) { + return (T) o; + } + T obj = (T) Proxy.newProxyInstance( + cls.getClassLoader(), + new Class[]{cls}, + (proxy, method, args) -> { + String clsName = cls.getName(); + String methodName = method.getName(); + String key = host + port + clsName + methodName + IdUtil.simpleUUID(); + Class[] parameterTypes = method.getParameterTypes(); + Request request = new DefaultRequest(key, clsName, methodName, parameterTypes, args); + Response response = connection.connect(request); + if (response == null) { + return null; + } + if (response.isErr()) { + throw new IllegalException(response.getErrMsg(), response.getThrowable()); + } + return response.getObj(); + }); + map.put(cls, obj); + return obj; + } +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/ResultHolder.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/ResultHolder.java new file mode 100644 index 00000000..2220fe8e --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/ResultHolder.java @@ -0,0 +1,89 @@ +/* + * 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.config.rpc.support; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.LockSupport; + +/** + * The staging results
    + * The unique remote call can be determined by the key of request and + * response, and the result of the call is stored in the secondary cache, + * which is convenient for the client to use at any time. + */ +@Slf4j +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ResultHolder { + + private static final Map map = new ConcurrentHashMap<>(); + private static final Map threadMap = new HashMap<>(); + + /** + * Writes when the client receives a response + * + * @param key Request and response keys + * @param o The result + */ + public static void put(String key, Object o) { + log.debug("Write the result, wake up the thread"); + map.put(key, o); + } + + /** + * Stores a thread that can be woken up and is in a waiting state + * + * @param key Request and response keys + * @param t The Thread + */ + public static void put(String key, Thread t) { + log.debug("Write thread, waiting to wake up"); + threadMap.put(key, t); + } + + /** + * Stores a thread that can be woken up and is in a waiting state + * + * @param key Request and response keys + */ + public static synchronized void wake(String key) { + log.debug("The future has been fetched, wake up the thread"); + Thread thread = threadMap.remove(key); + LockSupport.unpark(thread); + } + + /** + * Called when the client gets the response
    + * After the result is obtained, the corresponding key is cleared from the cache
    + * So it's only true when you first get the result + * + * @param key Request and response keys + * @return Response body + */ + @SuppressWarnings("unchecked") + public static T get(String key) { + log.debug("Get the future"); + return (T) map.remove(key); + } + +} diff --git a/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/SpringContextInstance.java b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/SpringContextInstance.java new file mode 100644 index 00000000..9bd6ec56 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/main/java/cn/hippo4j/config/rpc/support/SpringContextInstance.java @@ -0,0 +1,37 @@ +/* + * 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.config.rpc.support; + +import cn.hippo4j.common.config.ApplicationContextHolder; + +/** + * Adapter Spring, The requested object is managed by spring + */ +public class SpringContextInstance implements Instance { + + @Override + public Object getInstance(Class cls) { + return ApplicationContextHolder.getBean(cls); + } + + @Override + public Object getInstance(String name) { + return ApplicationContextHolder.getInstance().getBean(name); + } + +} diff --git a/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/client/CallManager.java b/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/client/CallManager.java new file mode 100644 index 00000000..d3fae3ae --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/client/CallManager.java @@ -0,0 +1,26 @@ +/* + * 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.config.rpc.client; + +public class CallManager { + + public int call() { + return 1; + } + +} diff --git a/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/client/RPCClientTest.java b/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/client/RPCClientTest.java new file mode 100644 index 00000000..54c2633e --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/client/RPCClientTest.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.config.rpc.client; + +import cn.hippo4j.config.rpc.request.DefaultRequest; +import cn.hippo4j.config.rpc.request.Request; +import cn.hippo4j.config.rpc.response.Response; +import cn.hippo4j.config.rpc.server.NettyServerConnection; +import cn.hippo4j.config.rpc.server.RPCServer; +import cn.hippo4j.config.rpc.server.ServerConnection; +import cn.hippo4j.config.rpc.support.DefaultInstance; +import cn.hippo4j.config.rpc.support.Instance; +import cn.hippo4j.config.rpc.support.ClassRegistry; +import org.junit.Assert; +import org.junit.Test; + +import java.io.IOException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +public class RPCClientTest { + + String host = "localhost"; + int port = 8888; + + @Test + public void connection() throws IOException { + + Class cls = CallManager.class; + String className = cls.getName(); + ClassRegistry.put(className, cls); + // The mode connection was denied when the server was started on the specified port + Instance instance = new DefaultInstance(); + ServerConnection connection = new NettyServerConnection(instance); + RPCServer rpcServer = new RPCServer(port, connection); + CompletableFuture.runAsync(rpcServer::bind); + try { + TimeUnit.SECONDS.sleep(3); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + NettyClientConnection clientConnection = new NettyClientConnection(host, port); + RPCClient rpcClient = new RPCClient(clientConnection); + Request request = new DefaultRequest("127.0.0.18888", className, "call", null, null); + for (int i = 0; i < 100; i++) { + Response response = rpcClient.connection(request); + Assert.assertEquals(response.getObj(), 1); + } + rpcClient.close(); + rpcServer.close(); + } + +} \ No newline at end of file diff --git a/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/server/RPCServerTest.java b/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/server/RPCServerTest.java new file mode 100644 index 00000000..726ca910 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/server/RPCServerTest.java @@ -0,0 +1,64 @@ +/* + * 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.config.rpc.server; + +import cn.hippo4j.config.rpc.support.DefaultInstance; +import cn.hippo4j.config.rpc.support.Instance; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import org.junit.Test; + +import java.io.IOException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +public class RPCServerTest { + + public static int port = 8888; + + @Test + public void bind() throws IOException { + Instance instance = new DefaultInstance(); + ServerConnection connection = new NettyServerConnection(instance); + RPCServer rpcServer = new RPCServer(port, connection); + CompletableFuture.runAsync(rpcServer::bind); + try { + TimeUnit.SECONDS.sleep(3); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + rpcServer.close(); + } + + @Test + public void bindTest() throws IOException { + Instance instance = new DefaultInstance(); + EventLoopGroup leader = new NioEventLoopGroup(); + EventLoopGroup worker = new NioEventLoopGroup(); + ServerConnection connection = new NettyServerConnection(leader, worker, instance); + RPCServer rpcServer = new RPCServer(port, connection); + CompletableFuture.runAsync(rpcServer::bind); + try { + TimeUnit.SECONDS.sleep(3); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + rpcServer.close(); + } + +} \ No newline at end of file diff --git a/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/ClassRegistryTest.java b/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/ClassRegistryTest.java new file mode 100644 index 00000000..403a7114 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/ClassRegistryTest.java @@ -0,0 +1,67 @@ +/* + * 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.config.rpc.support; + +import org.junit.Assert; +import org.junit.Test; + +public class ClassRegistryTest { + + @Test + public void get() { + String getStr = "GetModel"; + Class cls = ClassRegistry.get(getStr); + Assert.assertNull(cls); + ClassRegistry.put(getStr, GetModel.class); + Class aClass = ClassRegistry.get(getStr); + Assert.assertNotNull(aClass); + ClassRegistry.clear(); + } + + @Test + public void set() { + String getStr = "GetModel"; + ClassRegistry.set(getStr, GetModel.class); + Class aClass = ClassRegistry.get(getStr); + Assert.assertEquals(aClass, GetModel.class); + ClassRegistry.set(getStr, SetModel.class); + Class aClass1 = ClassRegistry.get(getStr); + Assert.assertEquals(aClass1, GetModel.class); + ClassRegistry.clear(); + } + + @Test + public void put() { + String getStr = "GetModel"; + ClassRegistry.put(getStr, GetModel.class); + Class aClass = ClassRegistry.get(getStr); + Assert.assertEquals(aClass, GetModel.class); + ClassRegistry.put(getStr, SetModel.class); + Class aClass1 = ClassRegistry.get(getStr); + Assert.assertEquals(aClass1, SetModel.class); + ClassRegistry.clear(); + } + + public static class GetModel { + + } + + public static class SetModel { + + } +} \ No newline at end of file diff --git a/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/DefaultInstanceTest.java b/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/DefaultInstanceTest.java new file mode 100644 index 00000000..6d070446 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/DefaultInstanceTest.java @@ -0,0 +1,55 @@ +/* + * 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.config.rpc.support; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.junit.Assert; +import org.junit.Test; + +public class DefaultInstanceTest { + + Instance instance = new DefaultInstance(); + + @Test + public void getInstance() { + Class cls = InstanceModel.class; + Object instanceInstance = instance.getInstance(cls); + Assert.assertNotNull(instanceInstance); + Assert.assertEquals(cls, instanceInstance.getClass()); + } + + @Test + public void testGetInstance() { + String className = "cn.hippo4j.config.rpc.support.DefaultInstanceTest$InstanceModel"; + Object instanceInstance = instance.getInstance(className); + Assert.assertNotNull(instanceInstance); + Assert.assertEquals(className, instanceInstance.getClass().getName()); + } + + @Setter + @Getter + @AllArgsConstructor + @NoArgsConstructor + public static class InstanceModel { + + String name; + } +} \ No newline at end of file diff --git a/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/NettyConnectPoolHolderTest.java b/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/NettyConnectPoolHolderTest.java new file mode 100644 index 00000000..f510ecd5 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/NettyConnectPoolHolderTest.java @@ -0,0 +1,65 @@ +/* + * 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.config.rpc.support; + +import io.netty.channel.Channel; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioSocketChannel; +import org.junit.Assert; +import org.junit.Test; + +public class NettyConnectPoolHolderTest { + + String host = "127.0.0.1"; + int port = 8888; + int maxCount = 8; + int timeout = 5000; + EventLoopGroup group = new NioEventLoopGroup(); + Class cls = NioSocketChannel.class; + + @Test + public void createPool() { + NettyConnectPool pool = new NettyConnectPool(host, port, maxCount, timeout, group, cls); + NettyConnectPool connectPool = NettyConnectPoolHolder.getPool(host, port); + Assert.assertEquals(pool, connectPool); + NettyConnectPoolHolder.clear(); + NettyConnectPool connectPool1 = NettyConnectPoolHolder.getPool(host, port); + Assert.assertNull(connectPool1); + } + + @Test + public void testGetPool() { + NettyConnectPool connectPool = NettyConnectPoolHolder.getPool(host, port, timeout, group); + NettyConnectPool connectPool1 = NettyConnectPoolHolder.getPool(host, port); + Assert.assertEquals(connectPool1, connectPool); + NettyConnectPoolHolder.clear(); + NettyConnectPool connectPool2 = NettyConnectPoolHolder.getPool(host, port); + Assert.assertNull(connectPool2); + } + + @Test + public void remove() { + NettyConnectPool connectPool = NettyConnectPoolHolder.getPool(host, port, timeout, group); + NettyConnectPool connectPool1 = NettyConnectPoolHolder.getPool(host, port); + Assert.assertEquals(connectPool1, connectPool); + NettyConnectPoolHolder.remove(host, port); + NettyConnectPool connectPool2 = NettyConnectPoolHolder.getPool(host, port); + Assert.assertNull(connectPool2); + } +} \ No newline at end of file diff --git a/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/NettyConnectPoolTest.java b/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/NettyConnectPoolTest.java new file mode 100644 index 00000000..622e575d --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/NettyConnectPoolTest.java @@ -0,0 +1,106 @@ +/* + * 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.config.rpc.support; + +import cn.hippo4j.config.rpc.server.NettyServerConnection; +import cn.hippo4j.config.rpc.server.RPCServer; +import cn.hippo4j.config.rpc.server.ServerConnection; +import io.netty.channel.Channel; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.util.concurrent.Future; +import org.junit.Assert; +import org.junit.Test; + +import java.io.IOException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +public class NettyConnectPoolTest { + + String host = "127.0.0.1"; + int port = 8888; + int maxCount = 64; + int timeout = 5000; + EventLoopGroup group = new NioEventLoopGroup(); + Class cls = NioSocketChannel.class; + + @Test + public void acquire() throws IOException { + // The mode connection was denied when the server was started on the specified port + Instance instance = new DefaultInstance(); + ServerConnection connection = new NettyServerConnection(instance); + RPCServer rpcServer = new RPCServer(port, connection); + CompletableFuture.runAsync(rpcServer::bind); + // Given the delay in starting the server, wait here + try { + TimeUnit.SECONDS.sleep(3); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + NettyConnectPool pool = new NettyConnectPool(host, port, maxCount, timeout, group, cls); + Channel acquire = pool.acquire(timeout); + Assert.assertNotNull(acquire); + pool.release(acquire); + rpcServer.close(); + } + + @Test + public void testAcquire() throws IOException { + // The mode connection was denied when the server was started on the specified port + Instance instance = new DefaultInstance(); + ServerConnection connection = new NettyServerConnection(instance); + RPCServer rpcServer = new RPCServer(port, connection); + CompletableFuture.runAsync(rpcServer::bind); + // Given the delay in starting the server, wait here + try { + TimeUnit.SECONDS.sleep(3); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + NettyConnectPool pool = new NettyConnectPool(host, port, maxCount, timeout, group, cls); + Future acquire = pool.acquire(); + Assert.assertNotNull(acquire); + rpcServer.close(); + } + + @Test + public void close() throws IOException { + // The mode connection was denied when the server was started on the specified port + Instance instance = new DefaultInstance(); + ServerConnection connection = new NettyServerConnection(instance); + RPCServer rpcServer = new RPCServer(port, connection); + CompletableFuture.runAsync(rpcServer::bind); + // Given the delay in starting the server, wait here + try { + TimeUnit.SECONDS.sleep(3); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + NettyConnectPool pool = new NettyConnectPool(host, port, maxCount, timeout, group, cls); + Channel acquire = pool.acquire(timeout); + Assert.assertNotNull(acquire); + pool.release(acquire); + pool.close(); + rpcServer.close(); + } +} \ No newline at end of file diff --git a/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/NettyProxyCenterTest.java b/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/NettyProxyCenterTest.java new file mode 100644 index 00000000..9ae4ac50 --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/NettyProxyCenterTest.java @@ -0,0 +1,44 @@ +/* + * 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.config.rpc.support; + +import cn.hippo4j.common.web.exception.IllegalException; +import org.junit.Assert; +import org.junit.Test; + +public class NettyProxyCenterTest { + + @Test + public void getProxy() { + ProxyInterface localhost = NettyProxyCenter.getProxy(ProxyInterface.class, "localhost", 8888); + Assert.assertNotNull(localhost); + } + + @Test(expected = IllegalException.class) + public void getProxyTest() { + ProxyClass localhost = NettyProxyCenter.getProxy(ProxyClass.class, "localhost", 8888); + Assert.assertNotNull(localhost); + } + interface ProxyInterface { + + } + + static class ProxyClass { + + } +} \ No newline at end of file diff --git a/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/ResultHolderTest.java b/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/ResultHolderTest.java new file mode 100644 index 00000000..3defaf3b --- /dev/null +++ b/hippo4j-server/hippo4j-config/src/test/java/cn/hippo4j/config/rpc/support/ResultHolderTest.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.hippo4j.config.rpc.support; + +import cn.hippo4j.common.toolkit.IdUtil; +import org.junit.Assert; +import org.junit.Test; + +public class ResultHolderTest { + + @Test + public void test() { + String s1 = IdUtil.simpleUUID(); + String o1 = s1 + "1"; + String s2 = IdUtil.simpleUUID(); + String o2 = s2 + "2"; + + ResultHolder.put(s1, o1); + ResultHolder.put(s2, o2); + + Object r1 = ResultHolder.get(s1); + Object r2 = ResultHolder.get(s2); + + Assert.assertEquals(r1, o1); + Assert.assertEquals(r2, o2); + } +} \ No newline at end of file From 18b2a9bc36405995935d20e04ce9c1bb776eedb8 Mon Sep 17 00:00:00 2001 From: maxisvest <1447829379@qq.com> Date: Thu, 3 Nov 2022 11:11:59 +0800 Subject: [PATCH 13/18] fix bug (#899) --- .../cn/hippo4j/core/executor/ThreadPoolNotifyAlarmHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/ThreadPoolNotifyAlarmHandler.java b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/ThreadPoolNotifyAlarmHandler.java index 7b832473..0fe10d5d 100644 --- a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/ThreadPoolNotifyAlarmHandler.java +++ b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/ThreadPoolNotifyAlarmHandler.java @@ -124,7 +124,7 @@ public class ThreadPoolNotifyAlarmHandler implements Runnable, CommandLineRunner */ public void checkPoolActivityAlarm(String threadPoolId, ThreadPoolExecutor threadPoolExecutor) { ThreadPoolNotifyAlarm alarmConfig = GlobalNotifyAlarmManage.get(threadPoolId); - if (Objects.isNull(alarmConfig) || !alarmConfig.getAlarm() || alarmConfig.getCapacityAlarm() <= 0) { + if (Objects.isNull(alarmConfig) || !alarmConfig.getAlarm() || alarmConfig.getActiveAlarm() <= 0) { return; } int activeCount = threadPoolExecutor.getActiveCount(); From 86edb3ed2e6bf8c19500de4bc7b386109ced103c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=88=90=E5=85=B4?= <49221670+Createsequence@users.noreply.github.com> Date: Thu, 3 Nov 2022 14:13:59 +0800 Subject: [PATCH 14/18] test: add test for plugins (#896) * test: add test for plugins * test: Adjust the time precision of test cases --- .../core/plugin/ExecuteAwarePlugin.java | 1 + .../core/plugin/PluginRuntimeTest.java | 40 +++++++ .../core/plugin/ThreadPoolPluginTest.java | 92 ++++++++++++++++ .../plugin/impl/TaskDecoratorPluginTest.java | 103 ++++++++++++++++++ .../impl/TaskRejectCountRecordPluginTest.java | 83 ++++++++++++++ .../impl/TaskRejectNotifyAlarmPluginTest.java | 87 +++++++++++++++ .../plugin/impl/TaskTimeRecordPluginTest.java | 68 ++++++++++++ .../TaskTimeoutNotifyAlarmPluginTest.java | 86 +++++++++++++++ .../ThreadPoolExecutorShutdownPluginTest.java | 91 ++++++++++++++++ 9 files changed, 651 insertions(+) create mode 100644 hippo4j-core/src/test/java/cn/hippo4j/core/plugin/PluginRuntimeTest.java create mode 100644 hippo4j-core/src/test/java/cn/hippo4j/core/plugin/ThreadPoolPluginTest.java create mode 100644 hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/TaskDecoratorPluginTest.java create mode 100644 hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/TaskRejectCountRecordPluginTest.java create mode 100644 hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/TaskRejectNotifyAlarmPluginTest.java create mode 100644 hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/TaskTimeRecordPluginTest.java create mode 100644 hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/TaskTimeoutNotifyAlarmPluginTest.java create mode 100644 hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/ThreadPoolExecutorShutdownPluginTest.java diff --git a/hippo4j-core/src/main/java/cn/hippo4j/core/plugin/ExecuteAwarePlugin.java b/hippo4j-core/src/main/java/cn/hippo4j/core/plugin/ExecuteAwarePlugin.java index d6c8638a..19148432 100644 --- a/hippo4j-core/src/main/java/cn/hippo4j/core/plugin/ExecuteAwarePlugin.java +++ b/hippo4j-core/src/main/java/cn/hippo4j/core/plugin/ExecuteAwarePlugin.java @@ -32,6 +32,7 @@ public interface ExecuteAwarePlugin extends ThreadPoolPlugin { * @see ExtensibleThreadPoolExecutor#beforeExecute */ default void beforeExecute(Thread thread, Runnable runnable) { + // do noting } /** diff --git a/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/PluginRuntimeTest.java b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/PluginRuntimeTest.java new file mode 100644 index 00000000..80281460 --- /dev/null +++ b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/PluginRuntimeTest.java @@ -0,0 +1,40 @@ +/* + * 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.core.plugin; + +import org.junit.Assert; +import org.junit.Test; + +/** + * test for {@link PluginRuntime} + */ +public class PluginRuntimeTest { + + @Test + public void test() { + PluginRuntime runtime = new PluginRuntime("test"); + Assert.assertEquals("test", runtime.getPluginId()); + Assert.assertTrue(runtime.getInfoList().isEmpty()); + + runtime.addInfo("item", "item"); + PluginRuntime.Info info = runtime.getInfoList().get(0); + Assert.assertEquals("item", info.getName()); + Assert.assertEquals("item", info.getValue()); + } + +} diff --git a/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/ThreadPoolPluginTest.java b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/ThreadPoolPluginTest.java new file mode 100644 index 00000000..423400bf --- /dev/null +++ b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/ThreadPoolPluginTest.java @@ -0,0 +1,92 @@ +/* + * 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.core.plugin; + +import cn.hippo4j.common.toolkit.ThreadUtil; +import cn.hippo4j.core.executor.ExtensibleThreadPoolExecutor; +import cn.hippo4j.core.plugin.manager.DefaultThreadPoolPluginManager; +import lombok.Getter; +import org.junit.Assert; +import org.junit.Test; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * test for default method of {@link ThreadPoolPlugin} and it's subclass + */ +public class ThreadPoolPluginTest { + + @Test + public void testDefaultMethod() { + ExtensibleThreadPoolExecutor executor = new ExtensibleThreadPoolExecutor( + "test", new DefaultThreadPoolPluginManager(), + 1, 1, 1000L, TimeUnit.MILLISECONDS, + new ArrayBlockingQueue<>(1), Thread::new, new ThreadPoolExecutor.DiscardPolicy()); + + executor.register(new TestTaskAwarePlugin()); + executor.register(new TestExecuteAwarePlugin()); + executor.register(new TestRejectedAwarePlugin()); + executor.register(new TestShutdownAwarePlugin()); + + AtomicInteger count = new AtomicInteger(0); + executor.submit(() -> { + ThreadUtil.sleep(100L); + return count.incrementAndGet(); + }); + executor.submit(() -> { + ThreadUtil.sleep(100L); + count.incrementAndGet(); + }); + executor.submit(count::incrementAndGet, 2); + + // waiting for shutdown + executor.shutdown(); + while (!executor.isTerminated()) { + } + + Assert.assertEquals(2, count.get()); + } + + @Getter + private final static class TestTaskAwarePlugin implements TaskAwarePlugin { + + private final String id = this.getClass().getSimpleName(); + } + + @Getter + private final static class TestExecuteAwarePlugin implements ExecuteAwarePlugin { + + private final String id = this.getClass().getSimpleName(); + } + + @Getter + private final static class TestRejectedAwarePlugin implements RejectedAwarePlugin { + + private final String id = this.getClass().getSimpleName(); + } + + @Getter + private final static class TestShutdownAwarePlugin implements ShutdownAwarePlugin { + + private final String id = this.getClass().getSimpleName(); + } + +} diff --git a/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/TaskDecoratorPluginTest.java b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/TaskDecoratorPluginTest.java new file mode 100644 index 00000000..09f84f8f --- /dev/null +++ b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/TaskDecoratorPluginTest.java @@ -0,0 +1,103 @@ +/* + * 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.core.plugin.impl; + +import cn.hippo4j.common.toolkit.ThreadUtil; +import cn.hippo4j.core.executor.ExtensibleThreadPoolExecutor; +import cn.hippo4j.core.plugin.PluginRuntime; +import cn.hippo4j.core.plugin.ThreadPoolPlugin; +import cn.hippo4j.core.plugin.manager.DefaultThreadPoolPluginManager; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.core.task.TaskDecorator; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * test for {@link TaskDecoratorPlugin} + */ +public class TaskDecoratorPluginTest { + + private final AtomicInteger taskExecuteCount = new AtomicInteger(0); + + @Test + public void testGetId() { + Assert.assertEquals(TaskDecoratorPlugin.PLUGIN_NAME, new TaskDecoratorPlugin().getId()); + } + + @Test + public void testGetRuntime() { + ThreadPoolPlugin plugin = new TaskDecoratorPlugin(); + PluginRuntime runtime = new TaskDecoratorPlugin().getPluginRuntime(); + Assert.assertNotNull(runtime); + Assert.assertEquals(plugin.getId(), runtime.getPluginId()); + } + + @Test + public void testAddDecorator() { + TaskDecoratorPlugin plugin = new TaskDecoratorPlugin(); + plugin.addDecorator(runnable -> runnable); + plugin.addDecorator(runnable -> runnable); + Assert.assertEquals(2, plugin.getDecorators().size()); + } + + @Test + public void testRemoveDecorator() { + TaskDecoratorPlugin plugin = new TaskDecoratorPlugin(); + TaskDecorator decorator = runnable -> runnable; + plugin.addDecorator(decorator); + plugin.removeDecorator(decorator); + Assert.assertTrue(plugin.getDecorators().isEmpty()); + } + + @Test + public void testClear() { + TaskDecoratorPlugin plugin = new TaskDecoratorPlugin(); + TaskDecorator decorator = runnable -> runnable; + plugin.addDecorator(decorator); + plugin.addDecorator(decorator); + plugin.clearDecorators(); + Assert.assertTrue(plugin.getDecorators().isEmpty()); + } + + @Test + public void testBeforeTaskExecute() { + ExtensibleThreadPoolExecutor executor = new ExtensibleThreadPoolExecutor( + "test", new DefaultThreadPoolPluginManager(), + 5, 5, 1000L, TimeUnit.MILLISECONDS, + new ArrayBlockingQueue<>(1), Thread::new, new ThreadPoolExecutor.DiscardPolicy()); + TaskDecoratorPlugin plugin = new TaskDecoratorPlugin(); + plugin.addDecorator(runnable -> () -> { + taskExecuteCount.incrementAndGet(); + runnable.run(); + }); + plugin.addDecorator(runnable -> () -> { + taskExecuteCount.incrementAndGet(); + runnable.run(); + }); + executor.register(plugin); + executor.execute(() -> { + }); + ThreadUtil.sleep(500L); + Assert.assertEquals(2, taskExecuteCount.get()); + } + +} diff --git a/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/TaskRejectCountRecordPluginTest.java b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/TaskRejectCountRecordPluginTest.java new file mode 100644 index 00000000..a4ae9125 --- /dev/null +++ b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/TaskRejectCountRecordPluginTest.java @@ -0,0 +1,83 @@ +/* + * 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.core.plugin.impl; + +import cn.hippo4j.common.toolkit.ThreadUtil; +import cn.hippo4j.core.executor.ExtensibleThreadPoolExecutor; +import cn.hippo4j.core.plugin.manager.DefaultThreadPoolPluginManager; +import org.junit.Assert; +import org.junit.Test; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +/** + * test for {@link TaskRejectCountRecordPlugin} + */ +public class TaskRejectCountRecordPluginTest { + + @Test + public void testGetId() { + Assert.assertEquals(TaskRejectCountRecordPlugin.PLUGIN_NAME, new TaskRejectCountRecordPlugin().getId()); + } + + @Test + public void testGetRuntime() { + Assert.assertNotNull(new TaskRejectCountRecordPlugin().getPluginRuntime()); + } + + @Test + public void testGetRejectCountNum() { + TaskRejectCountRecordPlugin plugin = new TaskRejectCountRecordPlugin(); + Assert.assertEquals((Long) 0L, plugin.getRejectCountNum()); + } + + @Test + public void testGetRejectCount() { + TaskRejectCountRecordPlugin plugin = new TaskRejectCountRecordPlugin(); + Assert.assertEquals(0L, plugin.getRejectCount().get()); + } + + @Test + public void testSetRejectCount() { + TaskRejectCountRecordPlugin plugin = new TaskRejectCountRecordPlugin(); + AtomicLong atomicLong = new AtomicLong(0); + plugin.setRejectCount(atomicLong); + Assert.assertSame(atomicLong, plugin.getRejectCount()); + } + + @Test + public void testBeforeRejectedExecution() { + ExtensibleThreadPoolExecutor executor = new ExtensibleThreadPoolExecutor( + "test", new DefaultThreadPoolPluginManager(), + 1, 1, 1000L, TimeUnit.MILLISECONDS, + new ArrayBlockingQueue<>(1), Thread::new, new ThreadPoolExecutor.DiscardPolicy()); + + TaskRejectCountRecordPlugin plugin = new TaskRejectCountRecordPlugin(); + executor.register(plugin); + executor.submit(() -> ThreadUtil.sleep(500L)); + executor.submit(() -> ThreadUtil.sleep(500L)); + executor.submit(() -> ThreadUtil.sleep(500L)); + + ThreadUtil.sleep(500L); + Assert.assertEquals((Long) 1L, plugin.getRejectCountNum()); + } + +} diff --git a/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/TaskRejectNotifyAlarmPluginTest.java b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/TaskRejectNotifyAlarmPluginTest.java new file mode 100644 index 00000000..1842f3ef --- /dev/null +++ b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/TaskRejectNotifyAlarmPluginTest.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.core.plugin.impl; + +import cn.hippo4j.common.toolkit.ThreadUtil; +import cn.hippo4j.core.executor.ExtensibleThreadPoolExecutor; +import cn.hippo4j.core.plugin.manager.DefaultThreadPoolPluginManager; +import lombok.RequiredArgsConstructor; +import org.junit.Assert; +import org.junit.Test; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * test for {@link TaskRejectNotifyAlarmPlugin} + */ +public class TaskRejectNotifyAlarmPluginTest { + + @Test + public void testGetId() { + Assert.assertEquals(TaskRejectNotifyAlarmPlugin.PLUGIN_NAME, new TaskRejectNotifyAlarmPlugin().getId()); + } + + @Test + public void testGetRuntime() { + Assert.assertNotNull(new TaskRejectNotifyAlarmPlugin().getPluginRuntime()); + } + + @Test + public void testBeforeRejectedExecution() { + ExtensibleThreadPoolExecutor executor = new ExtensibleThreadPoolExecutor( + "test", new DefaultThreadPoolPluginManager(), + 1, 1, 1000L, TimeUnit.MILLISECONDS, + new ArrayBlockingQueue<>(1), Thread::new, new ThreadPoolExecutor.DiscardPolicy()); + + AtomicInteger rejectCount = new AtomicInteger(0); + executor.register(new TestPlugin(rejectCount, executor)); + executor.submit(() -> ThreadUtil.sleep(200L)); + executor.submit(() -> ThreadUtil.sleep(200L)); + executor.submit(() -> ThreadUtil.sleep(200L)); + + // waiting for shutdown + executor.shutdown(); + while (!executor.isTerminated()) { + } + Assert.assertEquals(1, rejectCount.get()); + } + + @RequiredArgsConstructor + private static class TestPlugin extends TaskRejectNotifyAlarmPlugin { + + private final AtomicInteger count; + private final ThreadPoolExecutor targetExecutor; + + /** + * Callback before task is rejected. + * + * @param runnable task + * @param executor executor + */ + @Override + public void beforeRejectedExecution(Runnable runnable, ThreadPoolExecutor executor) { + count.incrementAndGet(); + Assert.assertEquals(targetExecutor, executor); + super.beforeRejectedExecution(runnable, executor); + } + } + +} diff --git a/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/TaskTimeRecordPluginTest.java b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/TaskTimeRecordPluginTest.java new file mode 100644 index 00000000..d7866a28 --- /dev/null +++ b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/TaskTimeRecordPluginTest.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.core.plugin.impl; + +import cn.hippo4j.common.toolkit.ThreadUtil; +import cn.hippo4j.core.executor.ExtensibleThreadPoolExecutor; +import cn.hippo4j.core.plugin.manager.DefaultThreadPoolPluginManager; +import org.junit.Assert; +import org.junit.Test; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * test for {@link TaskTimeRecordPlugin} + */ +public class TaskTimeRecordPluginTest { + + @Test + public void testGetId() { + Assert.assertEquals(TaskTimeRecordPlugin.PLUGIN_NAME, new TaskTimeRecordPlugin().getId()); + } + + @Test + public void testGetRuntime() { + Assert.assertNotNull(new TaskTimeRecordPlugin().getPluginRuntime()); + } + + @Test + public void testSummarize() { + ExtensibleThreadPoolExecutor executor = new ExtensibleThreadPoolExecutor( + "test", new DefaultThreadPoolPluginManager(), + 3, 3, 1000L, TimeUnit.MILLISECONDS, + new ArrayBlockingQueue<>(1), Thread::new, new ThreadPoolExecutor.DiscardPolicy()); + + TaskTimeRecordPlugin plugin = new TaskTimeRecordPlugin(); + executor.register(plugin); + executor.submit(() -> ThreadUtil.sleep(1000L)); + executor.submit(() -> ThreadUtil.sleep(3000L)); + executor.submit(() -> ThreadUtil.sleep(2000L)); + + // waiting for shutdown + executor.shutdown(); + while (!executor.isTerminated()) { + } + TaskTimeRecordPlugin.Summary summary = plugin.summarize(); + Assert.assertEquals(1, summary.getMinTaskTimeMillis() / 1000L); + Assert.assertEquals(3, summary.getMaxTaskTimeMillis() / 1000L); + Assert.assertEquals(2, summary.getAvgTaskTimeMillis() / 1000L); + Assert.assertEquals(6, summary.getTotalTaskTimeMillis() / 1000L); + } +} diff --git a/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/TaskTimeoutNotifyAlarmPluginTest.java b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/TaskTimeoutNotifyAlarmPluginTest.java new file mode 100644 index 00000000..732d4915 --- /dev/null +++ b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/TaskTimeoutNotifyAlarmPluginTest.java @@ -0,0 +1,86 @@ +/* + * 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.core.plugin.impl; + +import cn.hippo4j.common.toolkit.ThreadUtil; +import cn.hippo4j.core.executor.ExtensibleThreadPoolExecutor; +import cn.hippo4j.core.plugin.manager.DefaultThreadPoolPluginManager; +import org.junit.Assert; +import org.junit.Test; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * test for {@link TaskTimeoutNotifyAlarmPlugin} + */ +public class TaskTimeoutNotifyAlarmPluginTest { + + private final ExtensibleThreadPoolExecutor executor = new ExtensibleThreadPoolExecutor( + "test", new DefaultThreadPoolPluginManager(), + 5, 5, 1000L, TimeUnit.MILLISECONDS, + new ArrayBlockingQueue<>(1), Thread::new, new ThreadPoolExecutor.AbortPolicy()); + + private final TaskTimeoutNotifyAlarmPlugin plugin = new TaskTimeoutNotifyAlarmPlugin( + executor.getThreadPoolId(), 100L, executor); + + @Test + public void testGetId() { + Assert.assertEquals(TaskTimeoutNotifyAlarmPlugin.PLUGIN_NAME, plugin.getId()); + } + + @Test + public void testGetRuntime() { + Assert.assertNotNull(plugin.getPluginRuntime()); + } + + @Test + public void testGetExecuteTimeOut() { + Assert.assertEquals(100L, plugin.getExecuteTimeOut().longValue()); + } + + @Test + public void testSetExecuteTimeOut() { + plugin.setExecuteTimeOut(200L); + Assert.assertEquals(200L, plugin.getExecuteTimeOut().longValue()); + } + + @Test + public void testProcessTaskTime() { + executor.register(plugin); + + AtomicInteger count = new AtomicInteger(0); + executor.submit(() -> { + count.incrementAndGet(); + ThreadUtil.sleep(100L); + }); + executor.submit(() -> { + count.incrementAndGet(); + ThreadUtil.sleep(300L); + }); + + // waiting for shutdown + executor.shutdown(); + while (!executor.isTerminated()) { + } + Assert.assertEquals(2, count.get()); + } + +} diff --git a/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/ThreadPoolExecutorShutdownPluginTest.java b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/ThreadPoolExecutorShutdownPluginTest.java new file mode 100644 index 00000000..056dc1bd --- /dev/null +++ b/hippo4j-core/src/test/java/cn/hippo4j/core/plugin/impl/ThreadPoolExecutorShutdownPluginTest.java @@ -0,0 +1,91 @@ +/* + * 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.core.plugin.impl; + +import cn.hippo4j.common.toolkit.ThreadUtil; +import cn.hippo4j.core.executor.ExtensibleThreadPoolExecutor; +import cn.hippo4j.core.plugin.ThreadPoolPlugin; +import cn.hippo4j.core.plugin.manager.DefaultThreadPoolPluginManager; +import org.junit.Assert; +import org.junit.Test; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * test for {@link ThreadPoolExecutorShutdownPlugin} + */ +public class ThreadPoolExecutorShutdownPluginTest { + + @Test + public void testGetId() { + Assert.assertEquals(ThreadPoolExecutorShutdownPlugin.PLUGIN_NAME, new ThreadPoolExecutorShutdownPlugin(1000L).getId()); + } + + @Test + public void testGetRuntime() { + Assert.assertNotNull(new ThreadPoolExecutorShutdownPlugin(1000L).getPluginRuntime()); + } + + @Test + public void testGetAwaitTerminationMillis() { + ThreadPoolExecutorShutdownPlugin plugin = new ThreadPoolExecutorShutdownPlugin(1000L); + Assert.assertEquals(1000L, plugin.getAwaitTerminationMillis()); + } + + @Test + public void testSetAwaitTerminationMillis() { + ThreadPoolExecutorShutdownPlugin plugin = new ThreadPoolExecutorShutdownPlugin(1000L); + plugin.setAwaitTerminationMillis(5000L); + Assert.assertEquals(5000L, plugin.getAwaitTerminationMillis()); + } + + public ExtensibleThreadPoolExecutor getExecutor(ThreadPoolPlugin plugin) { + ExtensibleThreadPoolExecutor executor = new ExtensibleThreadPoolExecutor( + "test", new DefaultThreadPoolPluginManager(), + 2, 2, 1000L, TimeUnit.MILLISECONDS, + new ArrayBlockingQueue<>(1), Thread::new, new ThreadPoolExecutor.DiscardPolicy()); + executor.register(plugin); + return executor; + } + + private static Callable getCallable(AtomicInteger completedCount) { + return () -> { + ThreadUtil.sleep(1000L); + return completedCount.incrementAndGet(); + }; + } + + @Test + public void testShutdown() { + ExtensibleThreadPoolExecutor executor = getExecutor( + new ThreadPoolExecutorShutdownPlugin(2000L)); + + AtomicInteger completedCount = new AtomicInteger(0); + executor.submit(getCallable(completedCount)); + executor.submit(getCallable(completedCount)); + executor.submit(getCallable(completedCount)); + + executor.shutdownNow(); + Assert.assertEquals(2, completedCount.get()); + } + +} \ No newline at end of file From b883dab4f371fb4843af2f215985b434b93f99d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=9D=B0?= <345127857@qq.com> Date: Thu, 3 Nov 2022 16:03:38 +0800 Subject: [PATCH 15/18] fix #901 (#902) --- .../java/cn/hippo4j/adapter/dubbo/DubboThreadPoolAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hippo4j-adapter/hippo4j-adapter-dubbo/src/main/java/cn/hippo4j/adapter/dubbo/DubboThreadPoolAdapter.java b/hippo4j-adapter/hippo4j-adapter-dubbo/src/main/java/cn/hippo4j/adapter/dubbo/DubboThreadPoolAdapter.java index f11d8ee1..1f063db0 100644 --- a/hippo4j-adapter/hippo4j-adapter-dubbo/src/main/java/cn/hippo4j/adapter/dubbo/DubboThreadPoolAdapter.java +++ b/hippo4j-adapter/hippo4j-adapter-dubbo/src/main/java/cn/hippo4j/adapter/dubbo/DubboThreadPoolAdapter.java @@ -69,7 +69,7 @@ public class DubboThreadPoolAdapter implements ThreadPoolAdapter, ApplicationLis @Override public List getThreadPoolStates() { List threadPoolAdapterStates = new ArrayList<>(); - DUBBO_PROTOCOL_EXECUTOR.forEach((kel, val) -> threadPoolAdapterStates.add(getThreadPoolState(String.valueOf(val)))); + DUBBO_PROTOCOL_EXECUTOR.forEach((key, val) -> threadPoolAdapterStates.add(getThreadPoolState(String.valueOf(key)))); return threadPoolAdapterStates; } From 733567e4985957d4a6b68b6fdb0ea5ba91478a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E7=A7=B0=20Ma=20Chen?= Date: Thu, 3 Nov 2022 21:17:35 +0800 Subject: [PATCH 16/18] Supplemental code comments (#904) --- .../core/executor/DynamicThreadPool.java | 1 - .../executor/DynamicThreadPoolExecutor.java | 54 ++++++------- .../ExtensibleThreadPoolExecutor.java | 79 +++++++++---------- .../ThreadPoolNotifyAlarmHandler.java | 33 ++++---- .../DynamicThreadPoolBannerHandler.java | 8 ++ .../core/plugin/ExecuteAwarePlugin.java | 7 +- .../cn/hippo4j/core/plugin/PluginRuntime.java | 17 +++- .../core/plugin/RejectedAwarePlugin.java | 2 - .../core/plugin/ShutdownAwarePlugin.java | 6 +- .../hippo4j/core/plugin/TaskAwarePlugin.java | 3 +- .../hippo4j/core/plugin/ThreadPoolPlugin.java | 3 - .../plugin/impl/AbstractTaskTimerPlugin.java | 5 +- .../core/plugin/impl/TaskDecoratorPlugin.java | 11 +-- .../impl/TaskRejectCountRecordPlugin.java | 5 +- .../plugin/impl/TaskTimeRecordPlugin.java | 22 +++--- .../impl/TaskTimeoutNotifyAlarmPlugin.java | 8 +- .../ThreadPoolExecutorShutdownPlugin.java | 5 +- .../DefaultThreadPoolPluginManager.java | 21 ++--- .../DefaultThreadPoolPluginRegistrar.java | 8 +- .../manager/EmptyThreadPoolPluginManager.java | 10 +-- .../manager/ThreadPoolPluginManager.java | 13 ++- .../manager/ThreadPoolPluginRegistrar.java | 1 - .../manager/ThreadPoolPluginSupport.java | 5 +- .../proxy/RejectedProxyInvocationHandler.java | 9 +++ .../hippo4j/core/proxy/RejectedProxyUtil.java | 6 +- .../toolkit/ExecutorTraceContextUtil.java | 6 +- .../cn/hippo4j/core/toolkit/IdentifyUtil.java | 14 +++- .../cn/hippo4j/core/toolkit/SystemClock.java | 33 ++++++++ .../hippo4j/core/toolkit/inet/InetUtils.java | 5 +- 29 files changed, 212 insertions(+), 188 deletions(-) diff --git a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/DynamicThreadPool.java b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/DynamicThreadPool.java index eb6632ff..8a3c758b 100644 --- a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/DynamicThreadPool.java +++ b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/DynamicThreadPool.java @@ -26,7 +26,6 @@ import java.lang.annotation.Target; /** * An annotation that enhances the functionality of the jdk acoustic thread pool, * with the following list of enhancements. - * *