From 2f600c8c27131d6b9b41feba84728092d13033ca Mon Sep 17 00:00:00 2001 From: magicliang Date: Thu, 28 Aug 2025 15:39:11 +0800 Subject: [PATCH 01/19] =?UTF-8?q?feat:=20=E4=BF=AE=E5=A4=8D=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E6=B1=A0=E6=B3=84=E9=9C=B2=E9=97=AE=E9=A2=98=EF=BC=8C?= =?UTF-8?q?=E5=B9=B6=E5=A2=9E=E5=8A=A0=E7=9B=B8=E5=BA=94=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../container/ConsumerProxyTest.java | 2 + .../trpc/core/common/ConfigManager.java | 53 +++- .../trpc/core/common/ShutdownListener.java | 26 ++ .../support/MBeanRegistryHelper.java | 16 ++ .../trpc/core/rpc/def/DefTimeoutManager.java | 1 + .../support/thread/ForkJoinWorkerPool.java | 5 + .../support/thread/ThreadWorkerPool.java | 4 + .../trpc/core/common/ConfigManagerTest.java | 259 +++++++++++++++++- .../thread/ForkJoinWorkerPoolTest.java | 38 ++- .../support/thread/ThreadWorkerPoolTest.java | 34 +++ .../proto/standard/common/TRpcServerTest.java | 2 + .../concurrenttest/TcpConcurrentTest.java | 2 + .../concurrenttest/UdpConcurrentTest.java | 2 + .../support/DefResponseFutureManager.java | 29 +- 14 files changed, 461 insertions(+), 12 deletions(-) create mode 100644 trpc-core/src/main/java/com/tencent/trpc/core/common/ShutdownListener.java diff --git a/trpc-container/trpc-container-default/src/test/java/com/tencent/trpc/container/container/ConsumerProxyTest.java b/trpc-container/trpc-container-default/src/test/java/com/tencent/trpc/container/container/ConsumerProxyTest.java index 384f12479..344d105a6 100644 --- a/trpc-container/trpc-container-default/src/test/java/com/tencent/trpc/container/container/ConsumerProxyTest.java +++ b/trpc-container/trpc-container-default/src/test/java/com/tencent/trpc/container/container/ConsumerProxyTest.java @@ -22,6 +22,7 @@ import com.tencent.trpc.core.exception.TRpcException; import com.tencent.trpc.core.rpc.RpcClientContext; import com.tencent.trpc.core.rpc.TRpcProxy; +import com.tencent.trpc.proto.support.DefResponseFutureManager; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -44,6 +45,7 @@ public void after() { container.stop(); } ConfigManager.stopTest(); + DefResponseFutureManager.reset(); } @Test diff --git a/trpc-core/src/main/java/com/tencent/trpc/core/common/ConfigManager.java b/trpc-core/src/main/java/com/tencent/trpc/core/common/ConfigManager.java index bf241850b..29d517f6e 100644 --- a/trpc-core/src/main/java/com/tencent/trpc/core/common/ConfigManager.java +++ b/trpc-core/src/main/java/com/tencent/trpc/core/common/ConfigManager.java @@ -44,6 +44,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.WeakHashMap; import java.util.concurrent.TimeUnit; public class ConfigManager { @@ -55,6 +56,12 @@ public class ConfigManager { * Container startup listener */ private final Set tRPCRunListeners = Sets.newConcurrentHashSet(); + + /** + * Shutdown listeners for decoupled cleanup, using WeakHashMap to prevent classloader leaks + */ + private final Map shutdownListeners = new WeakHashMap<>(); + /** * Business initialization */ @@ -107,6 +114,7 @@ public static void stopTest() { RpcClusterClientManager.reset(); RpcServerManager.reset(); WorkerPoolManager.reset(); + instance.setDefault = false; } @@ -251,6 +259,41 @@ public void addTRPCRunListener(TRPCRunListener trpcRunListener) { tRPCRunListeners.add(trpcRunListener); } + /** + * Get all shutdown listeners loaded via SPI. + * + * @return set of shutdown listeners + */ + public void registerShutdownListener(ShutdownListener listener) { + if (listener != null) { + shutdownListeners.put(listener, Boolean.TRUE); + } + } + + /** + * Unregister a shutdown listener. + * + * @param listener the shutdown listener to unregister + */ + public void unregisterShutdownListener(ShutdownListener listener) { + if (listener != null) { + shutdownListeners.remove(listener); + } + } + + /** + * Notify all registered shutdown listeners. + */ + private void notifyShutdownListeners() { + shutdownListeners.keySet().forEach(listener -> { + try { + listener.onShutdown(); + } catch (Exception e) { + logger.error("Error executing shutdown listener: {}", listener.getClass().getName(), e); + } + }); + } + @Override public String toString() { return "ApplicationConfig {globalConfig=" + globalConfig + ", serverConfig=" + serverConfig @@ -301,10 +344,10 @@ private void warmupSelector(BackendConfig backendConfig) { try { String selectorId = nm.getSelectorId(); SelectorManager.getManager().get(selectorId).warmup(backendConfig.toNamingServiceId()); - logger.warn("Warm up selector success。(selectorId={},naming={}) ", selectorId, + logger.warn("Warm up selector success.(selectorId={},naming={}) ", selectorId, backendConfig.getNamingOptions().getServiceNaming()); } catch (Exception ex) { - logger.warn("Warm up selector exception。(selectorId={}, naming={}) ", + logger.warn("Warm up selector exception.(selectorId={}, naming={}) ", nm.getSelectorId(), nm.getServiceNaming(), ex); } }); @@ -385,6 +428,10 @@ protected void stopInternal() throws Exception { ExtensionLoader.destroyAllPlugin(); // 8) close client cluster RpcClusterClientManager.close(); + + // Notify all shutdown listeners before actual shutdown + notifyShutdownListeners(); + logger.info(">>>tRPC Server stopped"); } @@ -394,4 +441,4 @@ public String toString() { } } -} +} \ No newline at end of file diff --git a/trpc-core/src/main/java/com/tencent/trpc/core/common/ShutdownListener.java b/trpc-core/src/main/java/com/tencent/trpc/core/common/ShutdownListener.java new file mode 100644 index 000000000..38422f5e0 --- /dev/null +++ b/trpc-core/src/main/java/com/tencent/trpc/core/common/ShutdownListener.java @@ -0,0 +1,26 @@ +/* + * Tencent is pleased to support the open source community by making tRPC available. + * + * Copyright (C) 2023 Tencent. + * All rights reserved. + * + * If you have downloaded a copy of the tRPC source code from Tencent, + * please note that tRPC source code is licensed under the Apache 2.0 License, + * A copy of the Apache 2.0 License can be found in the LICENSE file. + */ + +package com.tencent.trpc.core.common; + +/** + * Shutdown listener for components that need to perform cleanup when the container stops. + * This provides a decoupled way for components to register shutdown hooks without + * creating circular dependencies. + */ +public interface ShutdownListener { + + /** + * Called when the container is shutting down. + */ + void onShutdown(); + +} \ No newline at end of file diff --git a/trpc-core/src/main/java/com/tencent/trpc/core/management/support/MBeanRegistryHelper.java b/trpc-core/src/main/java/com/tencent/trpc/core/management/support/MBeanRegistryHelper.java index 146021d6a..d42cf9d5e 100644 --- a/trpc-core/src/main/java/com/tencent/trpc/core/management/support/MBeanRegistryHelper.java +++ b/trpc-core/src/main/java/com/tencent/trpc/core/management/support/MBeanRegistryHelper.java @@ -36,4 +36,20 @@ public static void registerMBean(Object object, ObjectName objectName) { } } + /** + * Unregister mbean + * + * @param objectName mbean object name {@link ObjectName} + */ + public static void unregisterMBean(ObjectName objectName) { + try { + MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); + if (mBeanServer.isRegistered(objectName)) { + mBeanServer.unregisterMBean(objectName); + } + } catch (Exception e) { + logger.warn("unregister mbean exception: ", e); + } + } + } \ No newline at end of file diff --git a/trpc-core/src/main/java/com/tencent/trpc/core/rpc/def/DefTimeoutManager.java b/trpc-core/src/main/java/com/tencent/trpc/core/rpc/def/DefTimeoutManager.java index 6fc15a417..05bb7df16 100644 --- a/trpc-core/src/main/java/com/tencent/trpc/core/rpc/def/DefTimeoutManager.java +++ b/trpc-core/src/main/java/com/tencent/trpc/core/rpc/def/DefTimeoutManager.java @@ -34,6 +34,7 @@ public DefTimeoutManager(int tickms) { public Future watch(final Runnable task, long timeout) { FutureAdapter adapter = new FutureAdapter(task); adapter.wrap = timer.newTimeout(adapter, timeout, TimeUnit.MILLISECONDS); + return adapter; } diff --git a/trpc-core/src/main/java/com/tencent/trpc/core/worker/support/thread/ForkJoinWorkerPool.java b/trpc-core/src/main/java/com/tencent/trpc/core/worker/support/thread/ForkJoinWorkerPool.java index 8be64f429..218b4239e 100644 --- a/trpc-core/src/main/java/com/tencent/trpc/core/worker/support/thread/ForkJoinWorkerPool.java +++ b/trpc-core/src/main/java/com/tencent/trpc/core/worker/support/thread/ForkJoinWorkerPool.java @@ -126,6 +126,11 @@ public void close(long timeoutMills) { shutdownGraceful(timeoutMills); } } + + // 注销MBean注册 + if (this.forkJoinPoolMXBean != null) { + MBeanRegistryHelper.unregisterMBean(this.forkJoinPoolMXBean.getObjectName()); + } } private void shutdownGraceful(long timeoutMills) { diff --git a/trpc-core/src/main/java/com/tencent/trpc/core/worker/support/thread/ThreadWorkerPool.java b/trpc-core/src/main/java/com/tencent/trpc/core/worker/support/thread/ThreadWorkerPool.java index c7b5b919e..8e00d7a2c 100644 --- a/trpc-core/src/main/java/com/tencent/trpc/core/worker/support/thread/ThreadWorkerPool.java +++ b/trpc-core/src/main/java/com/tencent/trpc/core/worker/support/thread/ThreadWorkerPool.java @@ -149,6 +149,10 @@ public void close(long timeoutMills) { shutdownGraceful(timeoutMills); } } + // 注销MBean注册 + if (this.threadPoolMXBean != null) { + MBeanRegistryHelper.unregisterMBean(this.threadPoolMXBean.getObjectName()); + } } @Override diff --git a/trpc-core/src/test/java/com/tencent/trpc/core/common/ConfigManagerTest.java b/trpc-core/src/test/java/com/tencent/trpc/core/common/ConfigManagerTest.java index 7d69d1f1f..6e4b9036c 100644 --- a/trpc-core/src/test/java/com/tencent/trpc/core/common/ConfigManagerTest.java +++ b/trpc-core/src/test/java/com/tencent/trpc/core/common/ConfigManagerTest.java @@ -21,12 +21,16 @@ import com.tencent.trpc.core.worker.support.thread.ThreadPoolConfig; import com.tencent.trpc.core.worker.support.thread.ThreadWorkerPool; import java.util.HashMap; +import java.util.concurrent.atomic.AtomicBoolean; import org.assertj.core.util.Lists; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + public class ConfigManagerTest { private static final int TCP_PORT = 8090; @@ -73,8 +77,19 @@ public void teardown() { @Test public void testStart() { - ConfigManager.getInstance().start(); - ConfigManager.getInstance().stop(); + // 重置ConfigManager状态,避免setUp()中的listener1配置影响 + ConfigManager.stopTest(); + ConfigManager.startTest(); + + ConfigManager configManager = ConfigManager.getInstance(); + + // 设置最小配置,不使用listener1 + ServerConfig serverConfig = new ServerConfig(); + serverConfig.setRunListeners(Lists.newArrayList()); // 空的runListeners列表 + configManager.setServerConfig(serverConfig); + + configManager.start(); + configManager.stop(); } @Test @@ -167,14 +182,248 @@ public void stop() { @Test public void testGracefulRestart() throws InterruptedException { + // 重置ConfigManager状态,避免setUp()中的listener1配置影响 + ConfigManager.stopTest(); ConfigManager.startTest(); + + ServerConfig serverConfig = new ServerConfig(); serverConfig.setWaitTimeout(WAIT_TIME); serverConfig.setCloseTimeout(WAIT_TIME); + serverConfig.setRunListeners(Lists.newArrayList()); // 空的runListeners列表 serverConfig.setDefault(); - ConfigManager.getInstance().setServerConfig(serverConfig); - ConfigManager.getInstance().start(); + + final ConfigManager configManager = ConfigManager.getInstance(); + configManager.setServerConfig(serverConfig); + + configManager.start(); Thread.sleep(WAIT_TIME); - ConfigManager.getInstance().stop(); + configManager.stop(); + } + + @Test + public void testRegisterShutdownListener() { + // 重置ConfigManager状态,避免setUp()中的listener1配置影响 + ConfigManager.stopTest(); + ConfigManager.startTest(); + + ConfigManager configManager = ConfigManager.getInstance(); + + // 设置最小配置,不使用listener1 + ServerConfig serverConfig = new ServerConfig(); + serverConfig.setRunListeners(Lists.newArrayList()); // 空的runListeners列表 + configManager.setServerConfig(serverConfig); + + TestShutdownListener listener = new TestShutdownListener(); + + configManager.registerShutdownListener(listener); + + // 验证listener已注册,通过启动和停止容器来测试 + configManager.start(false); // 不注册shutdown hook + configManager.stop(); + + assertTrue("Shutdown listener should be called", listener.isShutdownCalled()); + } + + @Test + public void testUnregisterShutdownListener() { + // 重置ConfigManager状态,避免setUp()中的listener1配置影响 + ConfigManager.stopTest(); + ConfigManager.startTest(); + + ConfigManager configManager = ConfigManager.getInstance(); + + // 设置最小配置,不使用listener1 + ServerConfig serverConfig = new ServerConfig(); + serverConfig.setRunListeners(Lists.newArrayList()); // 空的runListeners列表 + configManager.setServerConfig(serverConfig); + + TestShutdownListener listener = new TestShutdownListener(); + + configManager.registerShutdownListener(listener); + configManager.unregisterShutdownListener(listener); + + // 验证listener被注销后不会被调用 + configManager.start(false); + configManager.stop(); + + assertFalse("Shutdown listener should not be called after unregister", listener.isShutdownCalled()); + } + + @Test + public void testMultipleShutdownListeners() { + // 重置ConfigManager状态,避免setUp()中的listener1配置影响 + ConfigManager.stopTest(); + ConfigManager.startTest(); + + ConfigManager configManager = ConfigManager.getInstance(); + + // 设置最小配置,不使用listener1 + ServerConfig serverConfig = new ServerConfig(); + serverConfig.setRunListeners(Lists.newArrayList()); // 空的runListeners列表 + configManager.setServerConfig(serverConfig); + + TestShutdownListener listener1 = new TestShutdownListener("listener1"); + TestShutdownListener listener2 = new TestShutdownListener("listener2"); + TestShutdownListener listener3 = new TestShutdownListener("listener3"); + + configManager.registerShutdownListener(listener1); + configManager.registerShutdownListener(listener2); + configManager.registerShutdownListener(listener3); + + configManager.start(false); + configManager.stop(); + + assertTrue("Listener1 should be called", listener1.isShutdownCalled()); + assertTrue("Listener2 should be called", listener2.isShutdownCalled()); + assertTrue("Listener3 should be called", listener3.isShutdownCalled()); + } + + @Test + public void testNullShutdownListenerHandling() { + // 重置ConfigManager状态,避免setUp()中的listener1配置影响 + ConfigManager.stopTest(); + ConfigManager.startTest(); + + ConfigManager configManager = ConfigManager.getInstance(); + + // 设置最小配置,不使用listener1 + ServerConfig serverConfig = new ServerConfig(); + serverConfig.setRunListeners(Lists.newArrayList()); // 空的runListeners列表 + configManager.setServerConfig(serverConfig); + + // 验证空listener不会导致异常 + configManager.registerShutdownListener(null); + configManager.unregisterShutdownListener(null); + + configManager.start(false); + configManager.stop(); + } + + @Test + public void testShutdownListenerExceptionHandling() { + // 重置ConfigManager状态,避免setUp()中的listener1配置影响 + ConfigManager.stopTest(); + ConfigManager.startTest(); + + ConfigManager configManager = ConfigManager.getInstance(); + + // 设置最小配置,不使用listener1 + ServerConfig serverConfig = new ServerConfig(); + serverConfig.setRunListeners(Lists.newArrayList()); // 空的runListeners列表 + configManager.setServerConfig(serverConfig); + + TestShutdownListener goodListener = new TestShutdownListener("good"); + TestShutdownListener badListener = new TestShutdownListener("bad", true); + + configManager.registerShutdownListener(goodListener); + configManager.registerShutdownListener(badListener); + + configManager.start(false); + configManager.stop(); + + // 即使一个listener抛出异常,其他listener也应该被调用 + assertTrue("Good listener should be called despite bad listener exception", goodListener.isShutdownCalled()); + assertTrue("Bad listener should be called even with exception", badListener.isShutdownCalled()); + } + + @Test + public void testConfigManagerShutdownListenerOnly() { + // 创建一个独立的测试,专门测试ShutdownListener功能,不依赖listener1 + ConfigManager.stopTest(); + + // 重新初始化以确保扩展被正确加载 + ConfigManager.startTest(); + ConfigManager configManager = ConfigManager.getInstance(); + + // 设置最小配置,不使用listener1 + ServerConfig serverConfig = new ServerConfig(); + serverConfig.setRunListeners(Lists.newArrayList()); // 空的runListeners列表 + configManager.setServerConfig(serverConfig); + + TestShutdownListener testListener = new TestShutdownListener("isolated-test"); + configManager.registerShutdownListener(testListener); + + try { + configManager.start(false); + configManager.stop(); + + assertTrue("Shutdown listener should be called", testListener.isShutdownCalled()); + } catch (Exception e) { + // 即使启动失败,shutdown listener也应该被调用 + assertTrue("Shutdown listener should be called even on startup failure", + testListener.isShutdownCalled()); + } + } + + @Test + public void testShutdownListenerWithStartupFailure() { + // 专门测试启动失败时ShutdownListener是否仍然被调用 + ConfigManager.stopTest(); + ConfigManager.startTest(); + ConfigManager configManager = ConfigManager.getInstance(); + + TestShutdownListener testListener = new TestShutdownListener("startup-failure-test"); + configManager.registerShutdownListener(testListener); + + // 故意设置一个不存在的listener来触发启动失败 + ServerConfig serverConfig = new ServerConfig(); + serverConfig.setRunListeners(Lists.newArrayList("nonexistent-listener")); + configManager.setServerConfig(serverConfig); + + try { + configManager.start(false); + // 如果没有异常,正常停止 + configManager.stop(); + } catch (Exception e) { + // 预期会有异常,但ShutdownListener应该在异常处理过程中被调用 + } + + assertTrue("Shutdown listener should be called even when startup fails", + testListener.isShutdownCalled()); + } + + /** + * 测试用的ShutdownListener实现 + */ + private static class TestShutdownListener implements ShutdownListener { + private final String name; + private final boolean throwException; + private final AtomicBoolean shutdownCalled = new AtomicBoolean(false); + + public TestShutdownListener() { + this("test-listener", false); + } + + public TestShutdownListener(String name) { + this(name, false); + } + + public TestShutdownListener(String name, boolean throwException) { + this.name = name; + this.throwException = throwException; + } + + @Override + public void onShutdown() { + shutdownCalled.set(true); + if (throwException) { + throw new RuntimeException("Simulated exception in " + name); + } + } + + public boolean isShutdownCalled() { + return shutdownCalled.get(); + } + } + + @Test + public void testShutdownHook() { + // 重置ConfigManager状态,避免setUp()中的listener1配置影响 + ConfigManager.stopTest(); + ConfigManager.startTest(); + + ConfigManager configManager = ConfigManager.getInstance(); + configManager.start(); } } \ No newline at end of file diff --git a/trpc-core/src/test/java/com/tencent/trpc/core/worker/support/thread/ForkJoinWorkerPoolTest.java b/trpc-core/src/test/java/com/tencent/trpc/core/worker/support/thread/ForkJoinWorkerPoolTest.java index c74f4f95c..b70f97d5e 100644 --- a/trpc-core/src/test/java/com/tencent/trpc/core/worker/support/thread/ForkJoinWorkerPoolTest.java +++ b/trpc-core/src/test/java/com/tencent/trpc/core/worker/support/thread/ForkJoinWorkerPoolTest.java @@ -15,10 +15,14 @@ import com.tencent.trpc.core.management.ForkJoinPoolMXBean; import com.tencent.trpc.core.management.PoolMXBean; import com.tencent.trpc.core.management.PoolMXBean.WorkerPoolType; +import com.tencent.trpc.core.management.support.MBeanRegistryHelper; +import java.lang.management.ManagementFactory; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.ForkJoinPool; +import javax.management.MBeanServer; +import javax.management.ObjectName; import org.junit.Assert; import org.junit.Test; @@ -73,4 +77,36 @@ public void testForkJoinWorkerPool() { Assert.assertTrue(forkJoinPool.isShutdown()); forkJoinWorkerPool.close(0); } -} + + /** + * Test MBean unregistration when closing ForkJoinWorkerPool + */ + @Test + public void testMBeanUnregistrationOnClose() throws Exception { + Map properties = new HashMap<>(); + properties.put(ForkJoinPoolConfig.PARALLELISM, PARALLELISM); + properties.put(ForkJoinPoolConfig.TIMEOUT_MS, TIMEOUT_MILLS); + PluginConfig poolPluginConfig = new PluginConfig(ForkJoinWorkerPool.NAME, ThreadWorkerPool.class, + properties); + ForkJoinWorkerPool forkJoinWorkerPool = new ForkJoinWorkerPool(); + forkJoinWorkerPool.setPluginConfig(poolPluginConfig); + forkJoinWorkerPool.init(); + + // Get the MXBean and its object name + PoolMXBean report = forkJoinWorkerPool.report(); + ForkJoinPoolMXBean forkJoinPoolMXBean = (ForkJoinPoolMXBean) report; + ObjectName objectName = forkJoinPoolMXBean.getObjectName(); + + // Verify MBean is registered + MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); + Assert.assertTrue("MBean should be registered after init", + mBeanServer.isRegistered(objectName)); + + // Close the worker pool + forkJoinWorkerPool.close(1000); + + // Verify MBean is unregistered after close + Assert.assertFalse("MBean should be unregistered after close", + mBeanServer.isRegistered(objectName)); + } +} \ No newline at end of file diff --git a/trpc-core/src/test/java/com/tencent/trpc/core/worker/support/thread/ThreadWorkerPoolTest.java b/trpc-core/src/test/java/com/tencent/trpc/core/worker/support/thread/ThreadWorkerPoolTest.java index db4048cab..633e92740 100644 --- a/trpc-core/src/test/java/com/tencent/trpc/core/worker/support/thread/ThreadWorkerPoolTest.java +++ b/trpc-core/src/test/java/com/tencent/trpc/core/worker/support/thread/ThreadWorkerPoolTest.java @@ -18,8 +18,12 @@ import com.tencent.trpc.core.management.PoolMXBean.WorkerPoolType; import com.tencent.trpc.core.management.ThreadPerTaskExecutorMXBeanImpl; import com.tencent.trpc.core.management.ThreadPoolMXBean; +import com.tencent.trpc.core.management.support.MBeanRegistryHelper; +import java.lang.management.ManagementFactory; import java.util.HashMap; import java.util.Map; +import javax.management.MBeanServer; +import javax.management.ObjectName; import org.junit.Assert; import org.junit.Test; @@ -113,4 +117,34 @@ public void testVirtualThreads() { Assert.assertNotNull(report.toString()); } + /** + * Test MBean unregistration when closing ThreadWorkerPool + */ + @Test + public void testMBeanUnregistrationOnClose() throws Exception { + Map properties = getProperties(); + PluginConfig poolPluginConfig = new PluginConfig("work_pool", ThreadWorkerPool.class, + properties); + ThreadWorkerPool threadWorkerPool = new ThreadWorkerPool(); + threadWorkerPool.setPluginConfig(poolPluginConfig); + threadWorkerPool.init(); + + // Get the MXBean and its object name + PoolMXBean report = threadWorkerPool.report(); + ThreadPoolMXBean threadPoolMXBean = (ThreadPoolMXBean) report; + ObjectName objectName = threadPoolMXBean.getObjectName(); + + // Verify MBean is registered + MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); + Assert.assertTrue("MBean should be registered after init", + mBeanServer.isRegistered(objectName)); + + // Close the worker pool + threadWorkerPool.close(1000); + + // Verify MBean is unregistered after close + Assert.assertFalse("MBean should be unregistered after close", + mBeanServer.isRegistered(objectName)); + } + } \ No newline at end of file diff --git a/trpc-proto/trpc-proto-standard/src/test/java/com/tencent/trpc/proto/standard/common/TRpcServerTest.java b/trpc-proto/trpc-proto-standard/src/test/java/com/tencent/trpc/proto/standard/common/TRpcServerTest.java index a9aa1ed59..c99d2dfba 100644 --- a/trpc-proto/trpc-proto-standard/src/test/java/com/tencent/trpc/proto/standard/common/TRpcServerTest.java +++ b/trpc-proto/trpc-proto-standard/src/test/java/com/tencent/trpc/proto/standard/common/TRpcServerTest.java @@ -40,6 +40,7 @@ import com.tencent.trpc.proto.standard.common.HelloRequestProtocol.HelloRequest; import com.tencent.trpc.proto.standard.common.HelloRequestProtocol.HelloResponse; import com.tencent.trpc.proto.standard.server.StandardRpcServerFactory; +import com.tencent.trpc.proto.support.DefResponseFutureManager; import java.lang.reflect.Field; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; @@ -114,6 +115,7 @@ public void after() { } catch (InterruptedException e) { e.printStackTrace(); } + DefResponseFutureManager.reset(); } @Test diff --git a/trpc-proto/trpc-proto-standard/src/test/java/com/tencent/trpc/proto/standard/concurrenttest/TcpConcurrentTest.java b/trpc-proto/trpc-proto-standard/src/test/java/com/tencent/trpc/proto/standard/concurrenttest/TcpConcurrentTest.java index 15c5f0f7e..6888593ee 100644 --- a/trpc-proto/trpc-proto-standard/src/test/java/com/tencent/trpc/proto/standard/concurrenttest/TcpConcurrentTest.java +++ b/trpc-proto/trpc-proto-standard/src/test/java/com/tencent/trpc/proto/standard/concurrenttest/TcpConcurrentTest.java @@ -22,6 +22,7 @@ import com.tencent.trpc.core.rpc.RpcClientContext; import com.tencent.trpc.core.utils.RpcContextUtils; import com.tencent.trpc.proto.standard.common.HelloRequestProtocol.HelloRequest; +import com.tencent.trpc.proto.support.DefResponseFutureManager; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -68,6 +69,7 @@ public void testUdpConcurrentTest() throws InterruptedException { List results = new ArrayList<>(); for (int i = 0; i < concurrent; i++) { BackendConfig config = new BackendConfig(); + DefResponseFutureManager.reset(); config.setNamingUrl("ip://127.0.0.1:" + TCP_PORT); config.setConnsPerAddr(1); config.setNetwork("tcp"); diff --git a/trpc-proto/trpc-proto-standard/src/test/java/com/tencent/trpc/proto/standard/concurrenttest/UdpConcurrentTest.java b/trpc-proto/trpc-proto-standard/src/test/java/com/tencent/trpc/proto/standard/concurrenttest/UdpConcurrentTest.java index 80e4dcaaa..7bd073b48 100644 --- a/trpc-proto/trpc-proto-standard/src/test/java/com/tencent/trpc/proto/standard/concurrenttest/UdpConcurrentTest.java +++ b/trpc-proto/trpc-proto-standard/src/test/java/com/tencent/trpc/proto/standard/concurrenttest/UdpConcurrentTest.java @@ -22,6 +22,7 @@ import com.tencent.trpc.core.rpc.RpcClientContext; import com.tencent.trpc.core.utils.RpcContextUtils; import com.tencent.trpc.proto.standard.common.HelloRequestProtocol; +import com.tencent.trpc.proto.support.DefResponseFutureManager; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -67,6 +68,7 @@ public void testUdpConcurrentTest() throws InterruptedException { List results = new ArrayList(); for (int i = 0; i < concurrent; i++) { BackendConfig config = new BackendConfig(); + DefResponseFutureManager.reset(); config.setNamingUrl("ip://127.0.0.1:" + UDP_PORT); config.setConnsPerAddr(1); config.setNetwork("udp"); diff --git a/trpc-proto/trpc-rpc-support/src/main/java/com/tencent/trpc/proto/support/DefResponseFutureManager.java b/trpc-proto/trpc-rpc-support/src/main/java/com/tencent/trpc/proto/support/DefResponseFutureManager.java index 8d2884e1e..4bf5111b2 100644 --- a/trpc-proto/trpc-rpc-support/src/main/java/com/tencent/trpc/proto/support/DefResponseFutureManager.java +++ b/trpc-proto/trpc-rpc-support/src/main/java/com/tencent/trpc/proto/support/DefResponseFutureManager.java @@ -23,6 +23,8 @@ import com.tencent.trpc.core.rpc.TimeoutManager; import com.tencent.trpc.core.rpc.def.DefTimeoutManager; import com.tencent.trpc.core.transport.ClientTransport; +import com.tencent.trpc.core.common.ConfigManager; +import com.tencent.trpc.core.common.ShutdownListener; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -33,18 +35,23 @@ * * @see DefResponseFuture */ -public class DefResponseFutureManager { +public class DefResponseFutureManager implements ShutdownListener { private static final Logger LOG = LoggerFactory.getLogger(DefResponseFutureManager.class); + /** * Watcher for timeouts */ - private static final TimeoutManager TIMEOUT_MANAGER = new DefTimeoutManager(10); + private static TimeoutManager TIMEOUT_MANAGER = new DefTimeoutManager(10); /** * Store */ private final ConcurrentMap futureMap = new ConcurrentHashMap<>(); +public DefResponseFutureManager() { + ConfigManager.getInstance().registerShutdownListener(this); + } + /** * Create a {@link DefResponseFuture} for a tRPC request * @@ -152,6 +159,22 @@ public void stop() { TIMEOUT_MANAGER.close(); } + /** + * Called when the container is reset. + */ + public static void reset() { + TIMEOUT_MANAGER = new DefTimeoutManager(10); + } + + /** + * Shutdown listener implementation to handle container shutdown + */ + @Override + public void onShutdown() { + LOG.info("DefResponseFutureManager received shutdown notification"); + stop(); + } + /** * Add a watcher for timeout check of the {@link DefResponseFuture} */ @@ -181,4 +204,4 @@ private void watchTimeout(final DefResponseFuture future) { }, timeoutMills); future.setTimeoutFuture(watch); } -} +} \ No newline at end of file From 0c6c2f2ded770be1667824d8bcee0d97fc88e2ed Mon Sep 17 00:00:00 2001 From: chuanliang Date: Fri, 29 Aug 2025 10:50:51 +0800 Subject: [PATCH 02/19] =?UTF-8?q?chore(test):=20=E6=96=B0=E5=A2=9E=20DefRe?= =?UTF-8?q?sponseFutureManager.reset()=20=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/tencent/trpc/proto/standard/common/TRpcServerTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/trpc-proto/trpc-proto-standard/src/test/java/com/tencent/trpc/proto/standard/common/TRpcServerTest.java b/trpc-proto/trpc-proto-standard/src/test/java/com/tencent/trpc/proto/standard/common/TRpcServerTest.java index c99d2dfba..64983c84a 100644 --- a/trpc-proto/trpc-proto-standard/src/test/java/com/tencent/trpc/proto/standard/common/TRpcServerTest.java +++ b/trpc-proto/trpc-proto-standard/src/test/java/com/tencent/trpc/proto/standard/common/TRpcServerTest.java @@ -465,6 +465,7 @@ public void testCommonMethod() { public void testDefaultMethod() { HelloRequest.Builder builder = HelloRequest.newBuilder(); builder.setMessage(ByteString.copyFromUtf8("hello")); + DefResponseFutureManager.reset(); HelloServiceApi serviceApi = helloClientConfig.getProxy(); HelloResponse response = serviceApi.doDefaultMethod(new RpcClientContext(), builder.build()); Assert.assertEquals(response.getMessage().toStringUtf8(), "this is default method"); From 81c41ff2ce858ef90ec5281ed837fb2b3cc2fce1 Mon Sep 17 00:00:00 2001 From: chuanliang Date: Wed, 17 Sep 2025 11:42:02 +0800 Subject: [PATCH 03/19] =?UTF-8?q?docs(test):=20=E5=B0=86=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=E5=8F=8A=E5=AD=97=E7=AC=A6=E4=B8=B2=E8=BD=AC?= =?UTF-8?q?=E4=B8=BA=E8=8B=B1=E6=96=87=EF=BC=8C=E6=96=B0=E5=A2=9E=E4=B8=A4?= =?UTF-8?q?=E4=B8=AA=E6=B5=8B=E8=AF=95=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trpc/core/common/ConfigManagerTest.java | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/trpc-core/src/test/java/com/tencent/trpc/core/common/ConfigManagerTest.java b/trpc-core/src/test/java/com/tencent/trpc/core/common/ConfigManagerTest.java index 6e4b9036c..1957df962 100644 --- a/trpc-core/src/test/java/com/tencent/trpc/core/common/ConfigManagerTest.java +++ b/trpc-core/src/test/java/com/tencent/trpc/core/common/ConfigManagerTest.java @@ -201,41 +201,41 @@ public void testGracefulRestart() throws InterruptedException { configManager.stop(); } - @Test +@Test public void testRegisterShutdownListener() { - // 重置ConfigManager状态,避免setUp()中的listener1配置影响 + // Reset ConfigManager state to avoid influence from listener1 configuration in setUp() ConfigManager.stopTest(); ConfigManager.startTest(); ConfigManager configManager = ConfigManager.getInstance(); - // 设置最小配置,不使用listener1 + // Set minimal configuration, do not use listener1 ServerConfig serverConfig = new ServerConfig(); - serverConfig.setRunListeners(Lists.newArrayList()); // 空的runListeners列表 + serverConfig.setRunListeners(Lists.newArrayList()); // Empty runListeners list configManager.setServerConfig(serverConfig); TestShutdownListener listener = new TestShutdownListener(); configManager.registerShutdownListener(listener); - // 验证listener已注册,通过启动和停止容器来测试 - configManager.start(false); // 不注册shutdown hook + // Verify listener is registered by starting and stopping the container + configManager.start(false); // Do not register shutdown hook configManager.stop(); assertTrue("Shutdown listener should be called", listener.isShutdownCalled()); } - @Test +@Test public void testUnregisterShutdownListener() { - // 重置ConfigManager状态,避免setUp()中的listener1配置影响 + // Reset ConfigManager state to avoid influence from listener1 configuration in setUp() ConfigManager.stopTest(); ConfigManager.startTest(); ConfigManager configManager = ConfigManager.getInstance(); - // 设置最小配置,不使用listener1 + // Set minimal configuration, do not use listener1 ServerConfig serverConfig = new ServerConfig(); - serverConfig.setRunListeners(Lists.newArrayList()); // 空的runListeners列表 + serverConfig.setRunListeners(Lists.newArrayList()); // Empty runListeners list configManager.setServerConfig(serverConfig); TestShutdownListener listener = new TestShutdownListener(); @@ -243,24 +243,24 @@ public void testUnregisterShutdownListener() { configManager.registerShutdownListener(listener); configManager.unregisterShutdownListener(listener); - // 验证listener被注销后不会被调用 + // Verify listener is not called after being unregistered configManager.start(false); configManager.stop(); assertFalse("Shutdown listener should not be called after unregister", listener.isShutdownCalled()); } - @Test +@Test public void testMultipleShutdownListeners() { - // 重置ConfigManager状态,避免setUp()中的listener1配置影响 + // Reset ConfigManager state to avoid influence from listener1 configuration in setUp() ConfigManager.stopTest(); ConfigManager.startTest(); ConfigManager configManager = ConfigManager.getInstance(); - // 设置最小配置,不使用listener1 + // Set minimal configuration, do not use listener1 ServerConfig serverConfig = new ServerConfig(); - serverConfig.setRunListeners(Lists.newArrayList()); // 空的runListeners列表 + serverConfig.setRunListeners(Lists.newArrayList()); // Empty runListeners list configManager.setServerConfig(serverConfig); TestShutdownListener listener1 = new TestShutdownListener("listener1"); @@ -279,20 +279,20 @@ public void testMultipleShutdownListeners() { assertTrue("Listener3 should be called", listener3.isShutdownCalled()); } - @Test +@Test public void testNullShutdownListenerHandling() { - // 重置ConfigManager状态,避免setUp()中的listener1配置影响 + // Reset ConfigManager state to avoid influence from listener1 configuration in setUp() ConfigManager.stopTest(); ConfigManager.startTest(); ConfigManager configManager = ConfigManager.getInstance(); - // 设置最小配置,不使用listener1 + // Set minimal configuration, do not use listener1 ServerConfig serverConfig = new ServerConfig(); - serverConfig.setRunListeners(Lists.newArrayList()); // 空的runListeners列表 + serverConfig.setRunListeners(Lists.newArrayList()); // Empty runListeners list configManager.setServerConfig(serverConfig); - // 验证空listener不会导致异常 + // Verify null listener does not cause exceptions configManager.registerShutdownListener(null); configManager.unregisterShutdownListener(null); @@ -300,17 +300,17 @@ public void testNullShutdownListenerHandling() { configManager.stop(); } - @Test +@Test public void testShutdownListenerExceptionHandling() { - // 重置ConfigManager状态,避免setUp()中的listener1配置影响 + // Reset ConfigManager state to avoid influence from listener1 configuration in setUp() ConfigManager.stopTest(); ConfigManager.startTest(); ConfigManager configManager = ConfigManager.getInstance(); - // 设置最小配置,不使用listener1 + // Set minimal configuration, do not use listener1 ServerConfig serverConfig = new ServerConfig(); - serverConfig.setRunListeners(Lists.newArrayList()); // 空的runListeners列表 + serverConfig.setRunListeners(Lists.newArrayList()); // Empty runListeners list configManager.setServerConfig(serverConfig); TestShutdownListener goodListener = new TestShutdownListener("good"); @@ -322,23 +322,23 @@ public void testShutdownListenerExceptionHandling() { configManager.start(false); configManager.stop(); - // 即使一个listener抛出异常,其他listener也应该被调用 + // Verify other listeners are called even if one listener throws an exception assertTrue("Good listener should be called despite bad listener exception", goodListener.isShutdownCalled()); assertTrue("Bad listener should be called even with exception", badListener.isShutdownCalled()); } - @Test +@Test public void testConfigManagerShutdownListenerOnly() { - // 创建一个独立的测试,专门测试ShutdownListener功能,不依赖listener1 + // Create an isolated test specifically for ShutdownListener functionality, not dependent on listener1 ConfigManager.stopTest(); - // 重新初始化以确保扩展被正确加载 + // Reinitialize to ensure extensions are properly loaded ConfigManager.startTest(); ConfigManager configManager = ConfigManager.getInstance(); - // 设置最小配置,不使用listener1 + // Set minimal configuration, do not use listener1 ServerConfig serverConfig = new ServerConfig(); - serverConfig.setRunListeners(Lists.newArrayList()); // 空的runListeners列表 + serverConfig.setRunListeners(Lists.newArrayList()); // Empty runListeners list configManager.setServerConfig(serverConfig); TestShutdownListener testListener = new TestShutdownListener("isolated-test"); @@ -350,15 +350,15 @@ public void testConfigManagerShutdownListenerOnly() { assertTrue("Shutdown listener should be called", testListener.isShutdownCalled()); } catch (Exception e) { - // 即使启动失败,shutdown listener也应该被调用 + // Shutdown listener should be called even if startup fails assertTrue("Shutdown listener should be called even on startup failure", testListener.isShutdownCalled()); } } - @Test +@Test public void testShutdownListenerWithStartupFailure() { - // 专门测试启动失败时ShutdownListener是否仍然被调用 + // Specifically test if ShutdownListener is still called when startup fails ConfigManager.stopTest(); ConfigManager.startTest(); ConfigManager configManager = ConfigManager.getInstance(); @@ -366,25 +366,25 @@ public void testShutdownListenerWithStartupFailure() { TestShutdownListener testListener = new TestShutdownListener("startup-failure-test"); configManager.registerShutdownListener(testListener); - // 故意设置一个不存在的listener来触发启动失败 + // Deliberately set a nonexistent listener to trigger startup failure ServerConfig serverConfig = new ServerConfig(); serverConfig.setRunListeners(Lists.newArrayList("nonexistent-listener")); configManager.setServerConfig(serverConfig); try { configManager.start(false); - // 如果没有异常,正常停止 + // If no exception, stop normally configManager.stop(); } catch (Exception e) { - // 预期会有异常,但ShutdownListener应该在异常处理过程中被调用 + // Exception is expected, but ShutdownListener should be called during exception handling } - assertTrue("Shutdown listener should be called even when startup fails", +assertTrue("Shutdown listener should be called even when startup fails", testListener.isShutdownCalled()); } /** - * 测试用的ShutdownListener实现 + * Test ShutdownListener implementation */ private static class TestShutdownListener implements ShutdownListener { private final String name; From 44a753e0bda6699779b2bed0c12866c2f894069b Mon Sep 17 00:00:00 2001 From: chuanliang Date: Wed, 17 Sep 2025 12:01:57 +0800 Subject: [PATCH 04/19] =?UTF-8?q?docs:=20=E5=B0=86=E9=83=A8=E5=88=86?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=B3=A8=E9=87=8A=E4=BB=8E=E4=B8=AD=E6=96=87?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E8=8B=B1=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../support/thread/ForkJoinWorkerPool.java | 2 +- .../support/thread/ThreadWorkerPool.java | 2 +- .../trpc/core/common/ConfigManagerTest.java | 18 +++++++++--------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/trpc-core/src/main/java/com/tencent/trpc/core/worker/support/thread/ForkJoinWorkerPool.java b/trpc-core/src/main/java/com/tencent/trpc/core/worker/support/thread/ForkJoinWorkerPool.java index 218b4239e..6686b6f02 100644 --- a/trpc-core/src/main/java/com/tencent/trpc/core/worker/support/thread/ForkJoinWorkerPool.java +++ b/trpc-core/src/main/java/com/tencent/trpc/core/worker/support/thread/ForkJoinWorkerPool.java @@ -127,7 +127,7 @@ public void close(long timeoutMills) { } } - // 注销MBean注册 + // unregister MBean if (this.forkJoinPoolMXBean != null) { MBeanRegistryHelper.unregisterMBean(this.forkJoinPoolMXBean.getObjectName()); } diff --git a/trpc-core/src/main/java/com/tencent/trpc/core/worker/support/thread/ThreadWorkerPool.java b/trpc-core/src/main/java/com/tencent/trpc/core/worker/support/thread/ThreadWorkerPool.java index 8e00d7a2c..a5450e20c 100644 --- a/trpc-core/src/main/java/com/tencent/trpc/core/worker/support/thread/ThreadWorkerPool.java +++ b/trpc-core/src/main/java/com/tencent/trpc/core/worker/support/thread/ThreadWorkerPool.java @@ -149,7 +149,7 @@ public void close(long timeoutMills) { shutdownGraceful(timeoutMills); } } - // 注销MBean注册 + // unregister Mbean if (this.threadPoolMXBean != null) { MBeanRegistryHelper.unregisterMBean(this.threadPoolMXBean.getObjectName()); } diff --git a/trpc-core/src/test/java/com/tencent/trpc/core/common/ConfigManagerTest.java b/trpc-core/src/test/java/com/tencent/trpc/core/common/ConfigManagerTest.java index 1957df962..32f272393 100644 --- a/trpc-core/src/test/java/com/tencent/trpc/core/common/ConfigManagerTest.java +++ b/trpc-core/src/test/java/com/tencent/trpc/core/common/ConfigManagerTest.java @@ -75,17 +75,17 @@ public void teardown() { ConfigManager.stopTest(); } - @Test +@Test public void testStart() { - // 重置ConfigManager状态,避免setUp()中的listener1配置影响 + // Reset ConfigManager state to avoid influence from listener1 configuration in setUp() ConfigManager.stopTest(); ConfigManager.startTest(); ConfigManager configManager = ConfigManager.getInstance(); - // 设置最小配置,不使用listener1 + // Set minimal configuration, do not use listener1 ServerConfig serverConfig = new ServerConfig(); - serverConfig.setRunListeners(Lists.newArrayList()); // 空的runListeners列表 + serverConfig.setRunListeners(Lists.newArrayList()); // Empty runListeners list configManager.setServerConfig(serverConfig); configManager.start(); @@ -180,9 +180,9 @@ public void stop() { }); } - @Test +@Test public void testGracefulRestart() throws InterruptedException { - // 重置ConfigManager状态,避免setUp()中的listener1配置影响 + // Reset ConfigManager state to avoid influence from listener1 configuration in setUp() ConfigManager.stopTest(); ConfigManager.startTest(); @@ -190,7 +190,7 @@ public void testGracefulRestart() throws InterruptedException { ServerConfig serverConfig = new ServerConfig(); serverConfig.setWaitTimeout(WAIT_TIME); serverConfig.setCloseTimeout(WAIT_TIME); - serverConfig.setRunListeners(Lists.newArrayList()); // 空的runListeners列表 + serverConfig.setRunListeners(Lists.newArrayList()); // Empty runListeners list serverConfig.setDefault(); final ConfigManager configManager = ConfigManager.getInstance(); @@ -417,9 +417,9 @@ public boolean isShutdownCalled() { } } - @Test +@Test public void testShutdownHook() { - // 重置ConfigManager状态,避免setUp()中的listener1配置影响 + // Reset ConfigManager state to avoid influence from listener1 configuration in setUp() ConfigManager.stopTest(); ConfigManager.startTest(); From 31388494e654cf9dfe1ab6af92398018af0eaac5 Mon Sep 17 00:00:00 2001 From: chuanliang Date: Wed, 17 Sep 2025 12:10:28 +0800 Subject: [PATCH 05/19] =?UTF-8?q?docs:=20=E5=B0=86=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=E2=80=9C=E6=9C=8D=E5=8A=A1=E6=8E=A5=E5=8F=A3=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E2=80=9D=E6=94=B9=E4=B8=BA=E8=8B=B1=E6=96=87=E2=80=9CService?= =?UTF-8?q?=20interface=20configuration=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trpc/proto/standard/concurrenttest/UdpConcurrentTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trpc-proto/trpc-proto-standard/src/test/java/com/tencent/trpc/proto/standard/concurrenttest/UdpConcurrentTest.java b/trpc-proto/trpc-proto-standard/src/test/java/com/tencent/trpc/proto/standard/concurrenttest/UdpConcurrentTest.java index 7bd073b48..71e13298d 100644 --- a/trpc-proto/trpc-proto-standard/src/test/java/com/tencent/trpc/proto/standard/concurrenttest/UdpConcurrentTest.java +++ b/trpc-proto/trpc-proto-standard/src/test/java/com/tencent/trpc/proto/standard/concurrenttest/UdpConcurrentTest.java @@ -116,7 +116,7 @@ public void run() { } private void startServer() { - // 1)服务接口配置 + // 1) Service interface configuration ProviderConfig providerConfig = new ProviderConfig<>(); providerConfig.setRef(new ConcurrentTestServiceImpl()); serviceConfig = getServiceConfig(); From 6fec83106335a2d20aa3778bcf3e7909527cec2a Mon Sep 17 00:00:00 2001 From: chuanliang Date: Wed, 17 Sep 2025 13:18:49 +0800 Subject: [PATCH 06/19] =?UTF-8?q?docs:=20=E5=B0=86=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=E2=80=9C=E6=9C=8D=E5=8A=A1=E6=8E=A5=E5=8F=A3=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E2=80=9D=E6=94=B9=E4=B8=BA=E8=8B=B1=E6=96=87=E2=80=9CService?= =?UTF-8?q?=20interface=20configuration=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../support/DefResponseFutureManager.java | 32 ++++++++++++++----- .../proto/support/DefResponseFutureTest.java | 8 +++++ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/trpc-proto/trpc-rpc-support/src/main/java/com/tencent/trpc/proto/support/DefResponseFutureManager.java b/trpc-proto/trpc-rpc-support/src/main/java/com/tencent/trpc/proto/support/DefResponseFutureManager.java index 4bf5111b2..df1af9260 100644 --- a/trpc-proto/trpc-rpc-support/src/main/java/com/tencent/trpc/proto/support/DefResponseFutureManager.java +++ b/trpc-proto/trpc-rpc-support/src/main/java/com/tencent/trpc/proto/support/DefResponseFutureManager.java @@ -35,7 +35,7 @@ * * @see DefResponseFuture */ -public class DefResponseFutureManager implements ShutdownListener { +public class DefResponseFutureManager { private static final Logger LOG = LoggerFactory.getLogger(DefResponseFutureManager.class); @@ -47,9 +47,14 @@ public class DefResponseFutureManager implements ShutdownListener { * Store */ private final ConcurrentMap futureMap = new ConcurrentHashMap<>(); + + /** + * Internal shutdown listener that handles the shutdown of this manager + */ + private final ShutdownListener shutdownListener = new InternalShutdownListener(); -public DefResponseFutureManager() { - ConfigManager.getInstance().registerShutdownListener(this); + public DefResponseFutureManager() { + ConfigManager.getInstance().registerShutdownListener(shutdownListener); } /** @@ -167,12 +172,23 @@ public static void reset() { } /** - * Shutdown listener implementation to handle container shutdown + * Get the internal shutdown listener for testing purposes + * + * @return the internal shutdown listener + */ + public ShutdownListener getShutdownListener() { + return shutdownListener; + } + + /** + * Internal shutdown listener implementation */ - @Override - public void onShutdown() { - LOG.info("DefResponseFutureManager received shutdown notification"); - stop(); + private class InternalShutdownListener implements ShutdownListener { + @Override + public void onShutdown() { + LOG.info("DefResponseFutureManager received shutdown notification"); + DefResponseFutureManager.this.stop(); + } } /** diff --git a/trpc-proto/trpc-rpc-support/src/test/java/com/tencent/trpc/proto/support/DefResponseFutureTest.java b/trpc-proto/trpc-rpc-support/src/test/java/com/tencent/trpc/proto/support/DefResponseFutureTest.java index b3fe2b180..14ff7affb 100644 --- a/trpc-proto/trpc-rpc-support/src/test/java/com/tencent/trpc/proto/support/DefResponseFutureTest.java +++ b/trpc-proto/trpc-rpc-support/src/test/java/com/tencent/trpc/proto/support/DefResponseFutureTest.java @@ -16,6 +16,7 @@ import com.tencent.trpc.core.common.config.ConsumerConfig; import com.tencent.trpc.core.common.config.ProtocolConfig; +import com.tencent.trpc.core.common.ShutdownListener; import com.tencent.trpc.core.exception.ErrorCode; import com.tencent.trpc.core.exception.TRpcException; import com.tencent.trpc.core.rpc.ConsumerInvoker; @@ -100,6 +101,13 @@ public void test() throws Exception { } manager.stop(); + + // Test internal shutdown listener + ShutdownListener shutdownListener = manager.getShutdownListener(); + assertTrue(shutdownListener != null); + + // Test shutdown listener functionality + shutdownListener.onShutdown(); } private class TestClientCodec extends ClientCodec { From c778e25c18ed24a9e1c4410e0705901d27006a3a Mon Sep 17 00:00:00 2001 From: chuanliang Date: Wed, 17 Sep 2025 14:22:43 +0800 Subject: [PATCH 07/19] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=85=B3?= =?UTF-8?q?=E9=97=AD=E7=9B=91=E5=90=AC=E5=99=A8=E5=8F=8A=E8=B6=85=E6=97=B6?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=99=A8=E6=8E=A7=E5=88=B6=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../http/client/AbstractConsumerInvoker.java | 46 ++++++++++++++++++- .../trpc/proto/http/HttpRpcClientTest.java | 35 ++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/trpc-proto/trpc-proto-http/src/main/java/com/tencent/trpc/proto/http/client/AbstractConsumerInvoker.java b/trpc-proto/trpc-proto-http/src/main/java/com/tencent/trpc/proto/http/client/AbstractConsumerInvoker.java index c3db4ba53..37173870f 100644 --- a/trpc-proto/trpc-proto-http/src/main/java/com/tencent/trpc/proto/http/client/AbstractConsumerInvoker.java +++ b/trpc-proto/trpc-proto-http/src/main/java/com/tencent/trpc/proto/http/client/AbstractConsumerInvoker.java @@ -35,6 +35,8 @@ import com.tencent.trpc.core.rpc.TimeoutManager; import com.tencent.trpc.core.rpc.def.DefTimeoutManager; import com.tencent.trpc.core.utils.JsonUtils; +import com.tencent.trpc.core.common.ConfigManager; +import com.tencent.trpc.core.common.ShutdownListener; import com.tencent.trpc.core.utils.ProtoJsonConverter; import com.tencent.trpc.core.utils.RpcUtils; import com.tencent.trpc.core.utils.StringUtils; @@ -64,7 +66,12 @@ public abstract class AbstractConsumerInvoker implements ConsumerInvoker { /** * The request timeout manager, used to handle the result of client requests in timeout scenarios. */ - private static final TimeoutManager TIMEOUT_MANAGER = new DefTimeoutManager(10); + private static TimeoutManager TIMEOUT_MANAGER = new DefTimeoutManager(10); + + /** + * Internal shutdown listener that handles the shutdown of the timeout manager + */ + private final ShutdownListener shutdownListener = new InternalShutdownListener(); /** * Http client for the request @@ -96,6 +103,9 @@ public AbstractConsumerInvoker(AbstractRpcClient client, if (extMap.containsKey(KEYSTORE_PATH) && extMap.containsKey(KEYSTORE_PASS)) { scheme = HTTPS_SCHEME; } + + // register shutdown listener + ConfigManager.getInstance().registerShutdownListener(shutdownListener); } /** @@ -285,4 +295,38 @@ public ProtocolConfig getProtocolConfig() { public Class getInterface() { return config.getServiceInterface(); } + + /** + * Stop the timeout manager + */ + public static void stop() { + TIMEOUT_MANAGER.close(); + } + + /** + * Called when the container is reset. + */ + public static void reset() { + TIMEOUT_MANAGER = new DefTimeoutManager(10); + } + + /** + * Get the internal shutdown listener for testing purposes + * + * @return the internal shutdown listener + */ + public ShutdownListener getShutdownListener() { + return shutdownListener; + } + + /** + * Internal shutdown listener implementation + */ + private class InternalShutdownListener implements ShutdownListener { + @Override + public void onShutdown() { + logger.info("AbstractConsumerInvoker received shutdown notification"); + AbstractConsumerInvoker.stop(); + } + } } diff --git a/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/HttpRpcClientTest.java b/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/HttpRpcClientTest.java index d7e699d39..2a7ddb0f0 100644 --- a/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/HttpRpcClientTest.java +++ b/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/HttpRpcClientTest.java @@ -15,6 +15,7 @@ import static com.tencent.trpc.transport.http.common.Constants.HTTP_SCHEME; import com.tencent.trpc.core.common.ConfigManager; +import com.tencent.trpc.core.common.ShutdownListener; import com.tencent.trpc.core.common.config.BackendConfig; import com.tencent.trpc.core.common.config.ConsumerConfig; import com.tencent.trpc.core.common.config.GlobalConfig; @@ -49,6 +50,7 @@ import tests.service.impl1.GreeterJavaBeanServiceImpl; import tests.service.impl1.GreeterJsonServiceImpl1; import tests.service.impl1.GreeterServiceImpl1; +import com.tencent.trpc.proto.http.client.AbstractConsumerInvoker; public class HttpRpcClientTest { @@ -488,4 +490,37 @@ public void testHttpRpcAttachmentWithJavaBean() { backendConfig.stop(); } } + + @Test + public void testAbstractConsumerInvokerShutdownListener() { + BackendConfig backendConfig = new BackendConfig(); + backendConfig.setName("serviceId"); + backendConfig.setRequestTimeout(REQUEST_TIMEOUT); + backendConfig.setMaxConns(MAX_CONNECTIONS); + backendConfig.setNamingUrl("ip://127.0.0.1:18088"); + backendConfig.setKeepAlive(false); + backendConfig.setConnsPerAddr(4); + backendConfig.setProtocol("http"); + + ConsumerConfig consumerConfig = new ConsumerConfig<>(); + consumerConfig.setServiceInterface(GreeterService.class); + consumerConfig.setBackendConfig(backendConfig); + + try { + GreeterService proxy = consumerConfig.getProxy(); + + // Test that the shutdown listener is properly registered + // We can't directly access the AbstractConsumerInvoker from the proxy, + // but we can test the static methods + AbstractConsumerInvoker.reset(); + + // Test shutdown functionality + AbstractConsumerInvoker.stop(); + + logger.info("AbstractConsumerInvoker shutdown listener test completed successfully"); + + } finally { + backendConfig.stop(); + } + } } From 251abed012f73e68daf639b616b82db3fade4563 Mon Sep 17 00:00:00 2001 From: chuanliang Date: Wed, 17 Sep 2025 14:55:17 +0800 Subject: [PATCH 08/19] =?UTF-8?q?style:=20=E7=A7=BB=E9=99=A4=20DefTimeoutM?= =?UTF-8?q?anager=20=E7=B1=BB=20watch=20=E6=96=B9=E6=B3=95=E5=A4=9A?= =?UTF-8?q?=E4=BD=99=E5=87=8F=E5=8F=B7=E6=B3=A8=E9=87=8A=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/tencent/trpc/core/rpc/def/DefTimeoutManager.java | 1 - 1 file changed, 1 deletion(-) diff --git a/trpc-core/src/main/java/com/tencent/trpc/core/rpc/def/DefTimeoutManager.java b/trpc-core/src/main/java/com/tencent/trpc/core/rpc/def/DefTimeoutManager.java index 05bb7df16..6fc15a417 100644 --- a/trpc-core/src/main/java/com/tencent/trpc/core/rpc/def/DefTimeoutManager.java +++ b/trpc-core/src/main/java/com/tencent/trpc/core/rpc/def/DefTimeoutManager.java @@ -34,7 +34,6 @@ public DefTimeoutManager(int tickms) { public Future watch(final Runnable task, long timeout) { FutureAdapter adapter = new FutureAdapter(task); adapter.wrap = timer.newTimeout(adapter, timeout, TimeUnit.MILLISECONDS); - return adapter; } From 1e63482b180da65fd010cf3c82812a1c1e397222 Mon Sep 17 00:00:00 2001 From: chuanliang Date: Wed, 17 Sep 2025 14:56:56 +0800 Subject: [PATCH 09/19] =?UTF-8?q?style:=20=E7=BB=9F=E4=B8=80=20ConfigManag?= =?UTF-8?q?erTest=20=E6=B5=8B=E8=AF=95=E6=96=B9=E6=B3=95=E7=BC=A9=E8=BF=9B?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trpc/core/common/ConfigManagerTest.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/trpc-core/src/test/java/com/tencent/trpc/core/common/ConfigManagerTest.java b/trpc-core/src/test/java/com/tencent/trpc/core/common/ConfigManagerTest.java index 32f272393..ff0c3d8e7 100644 --- a/trpc-core/src/test/java/com/tencent/trpc/core/common/ConfigManagerTest.java +++ b/trpc-core/src/test/java/com/tencent/trpc/core/common/ConfigManagerTest.java @@ -75,7 +75,7 @@ public void teardown() { ConfigManager.stopTest(); } -@Test + @Test public void testStart() { // Reset ConfigManager state to avoid influence from listener1 configuration in setUp() ConfigManager.stopTest(); @@ -180,7 +180,7 @@ public void stop() { }); } -@Test + @Test public void testGracefulRestart() throws InterruptedException { // Reset ConfigManager state to avoid influence from listener1 configuration in setUp() ConfigManager.stopTest(); @@ -201,7 +201,7 @@ public void testGracefulRestart() throws InterruptedException { configManager.stop(); } -@Test + @Test public void testRegisterShutdownListener() { // Reset ConfigManager state to avoid influence from listener1 configuration in setUp() ConfigManager.stopTest(); @@ -225,7 +225,7 @@ public void testRegisterShutdownListener() { assertTrue("Shutdown listener should be called", listener.isShutdownCalled()); } -@Test + @Test public void testUnregisterShutdownListener() { // Reset ConfigManager state to avoid influence from listener1 configuration in setUp() ConfigManager.stopTest(); @@ -250,7 +250,7 @@ public void testUnregisterShutdownListener() { assertFalse("Shutdown listener should not be called after unregister", listener.isShutdownCalled()); } -@Test + @Test public void testMultipleShutdownListeners() { // Reset ConfigManager state to avoid influence from listener1 configuration in setUp() ConfigManager.stopTest(); @@ -279,7 +279,7 @@ public void testMultipleShutdownListeners() { assertTrue("Listener3 should be called", listener3.isShutdownCalled()); } -@Test + @Test public void testNullShutdownListenerHandling() { // Reset ConfigManager state to avoid influence from listener1 configuration in setUp() ConfigManager.stopTest(); @@ -300,7 +300,7 @@ public void testNullShutdownListenerHandling() { configManager.stop(); } -@Test + @Test public void testShutdownListenerExceptionHandling() { // Reset ConfigManager state to avoid influence from listener1 configuration in setUp() ConfigManager.stopTest(); @@ -327,7 +327,7 @@ public void testShutdownListenerExceptionHandling() { assertTrue("Bad listener should be called even with exception", badListener.isShutdownCalled()); } -@Test + @Test public void testConfigManagerShutdownListenerOnly() { // Create an isolated test specifically for ShutdownListener functionality, not dependent on listener1 ConfigManager.stopTest(); @@ -356,7 +356,7 @@ public void testConfigManagerShutdownListenerOnly() { } } -@Test + @Test public void testShutdownListenerWithStartupFailure() { // Specifically test if ShutdownListener is still called when startup fails ConfigManager.stopTest(); @@ -379,7 +379,7 @@ public void testShutdownListenerWithStartupFailure() { // Exception is expected, but ShutdownListener should be called during exception handling } -assertTrue("Shutdown listener should be called even when startup fails", + assertTrue("Shutdown listener should be called even when startup fails", testListener.isShutdownCalled()); } @@ -417,7 +417,7 @@ public boolean isShutdownCalled() { } } -@Test + @Test public void testShutdownHook() { // Reset ConfigManager state to avoid influence from listener1 configuration in setUp() ConfigManager.stopTest(); From 40e716271eb318e7837ce006e986032c852f3b20 Mon Sep 17 00:00:00 2001 From: chuanliang Date: Wed, 17 Sep 2025 14:57:33 +0800 Subject: [PATCH 10/19] =?UTF-8?q?style:=20=E7=A7=BB=E9=99=A4=20ConfigManag?= =?UTF-8?q?er.reset()=20=E7=A9=BA=E8=A1=8C=E5=B9=B6=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=80=BC=20false?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/tencent/trpc/core/common/ConfigManager.java | 1 - 1 file changed, 1 deletion(-) diff --git a/trpc-core/src/main/java/com/tencent/trpc/core/common/ConfigManager.java b/trpc-core/src/main/java/com/tencent/trpc/core/common/ConfigManager.java index 29d517f6e..4ccac91ee 100644 --- a/trpc-core/src/main/java/com/tencent/trpc/core/common/ConfigManager.java +++ b/trpc-core/src/main/java/com/tencent/trpc/core/common/ConfigManager.java @@ -114,7 +114,6 @@ public static void stopTest() { RpcClusterClientManager.reset(); RpcServerManager.reset(); WorkerPoolManager.reset(); - instance.setDefault = false; } From ba6b3446bd6d58fa2a29c9cdaa536c5b23f010ec Mon Sep 17 00:00:00 2001 From: chuanliang Date: Wed, 17 Sep 2025 16:44:25 +0800 Subject: [PATCH 11/19] =?UTF-8?q?test:=20=E4=B8=BA=20Http2cRpcClientTest?= =?UTF-8?q?=20=E6=96=B0=E5=A2=9E=E5=89=8D=E7=BD=AE=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E4=B8=8E=E7=B1=BB=E5=AF=BC=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/tencent/trpc/proto/http/Http2cRpcClientTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/Http2cRpcClientTest.java b/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/Http2cRpcClientTest.java index 2eea59e18..e84340ff2 100644 --- a/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/Http2cRpcClientTest.java +++ b/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/Http2cRpcClientTest.java @@ -24,10 +24,12 @@ import com.tencent.trpc.core.logger.LoggerFactory; import com.tencent.trpc.core.rpc.RpcClientContext; import com.tencent.trpc.core.utils.NetUtils; +import com.tencent.trpc.proto.http.client.AbstractConsumerInvoker; import java.util.HashMap; import java.util.Map; import org.junit.AfterClass; import org.junit.Assert; +import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import tests.service.GreeterJsonService; @@ -47,6 +49,11 @@ public class Http2cRpcClientTest { private static ServerConfig serverConfig; + @Before + public void beforeTest() { + AbstractConsumerInvoker.reset(); + } + @BeforeClass public static void startHttpServer() { ConfigManager.stopTest(); From 700d8d9851c2612960604cef44b6383ccef3b8c2 Mon Sep 17 00:00:00 2001 From: chuanliang Date: Wed, 17 Sep 2025 16:49:36 +0800 Subject: [PATCH 12/19] =?UTF-8?q?test:=20=E6=96=B0=E5=A2=9E=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=B1=BB=E5=88=9D=E5=A7=8B=E5=8C=96=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E9=87=8D=E7=BD=AE=20AbstractConsumerInvoker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/tencent/trpc/proto/http/Http2RpcClientTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/Http2RpcClientTest.java b/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/Http2RpcClientTest.java index 6f53fa6f4..0d262d7a9 100644 --- a/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/Http2RpcClientTest.java +++ b/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/Http2RpcClientTest.java @@ -26,12 +26,14 @@ import com.tencent.trpc.core.logger.LoggerFactory; import com.tencent.trpc.core.rpc.RpcClientContext; import com.tencent.trpc.core.utils.NetUtils; +import com.tencent.trpc.proto.http.client.AbstractConsumerInvoker; import java.io.File; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.junit.AfterClass; import org.junit.Assert; +import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import tests.service.GreeterJsonService; @@ -53,6 +55,11 @@ public class Http2RpcClientTest { private static Map extMap = new HashMap<>(); + @Before + public void beforeTest() { + AbstractConsumerInvoker.reset(); + } + @BeforeClass public static void startHttpServer() { From 32775c8c02b4d81872984ceab61859bf3bd2c888 Mon Sep 17 00:00:00 2001 From: chuanliang Date: Wed, 17 Sep 2025 17:01:59 +0800 Subject: [PATCH 13/19] =?UTF-8?q?fix:=20=E4=BF=AE=E6=AD=A3=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E6=A0=87=E7=82=B9=E5=8F=8A=E5=BC=82=E5=B8=B8=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E9=A1=BA=E5=BA=8F=E5=B9=B6=E8=A1=A5=E5=85=85=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E5=AF=B9=E8=B1=A1=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/tencent/trpc/core/common/ConfigManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trpc-core/src/main/java/com/tencent/trpc/core/common/ConfigManager.java b/trpc-core/src/main/java/com/tencent/trpc/core/common/ConfigManager.java index 4ccac91ee..7e53da3d4 100644 --- a/trpc-core/src/main/java/com/tencent/trpc/core/common/ConfigManager.java +++ b/trpc-core/src/main/java/com/tencent/trpc/core/common/ConfigManager.java @@ -343,10 +343,10 @@ private void warmupSelector(BackendConfig backendConfig) { try { String selectorId = nm.getSelectorId(); SelectorManager.getManager().get(selectorId).warmup(backendConfig.toNamingServiceId()); - logger.warn("Warm up selector success.(selectorId={},naming={}) ", selectorId, + logger.warn("Warm up selector success。(selectorId={},naming={}) ", selectorId, backendConfig.getNamingOptions().getServiceNaming()); } catch (Exception ex) { - logger.warn("Warm up selector exception.(selectorId={}, naming={}) ", + logger.warn("Warm up selector exception。(selectorId={}, naming={}) ", nm.getSelectorId(), nm.getServiceNaming(), ex); } }); From 2c625557698f4a1cb67c33b687703f9f59c5566e Mon Sep 17 00:00:00 2001 From: chuanliang Date: Wed, 17 Sep 2025 17:04:34 +0800 Subject: [PATCH 14/19] =?UTF-8?q?test:=20=E6=96=B0=E5=A2=9E=20AbstractCons?= =?UTF-8?q?umerInvoker=20=E7=9A=84=20ShutdownListener=20=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/AbstractConsumerInvokerTest.java | 174 ++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/client/AbstractConsumerInvokerTest.java diff --git a/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/client/AbstractConsumerInvokerTest.java b/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/client/AbstractConsumerInvokerTest.java new file mode 100644 index 000000000..ecab6f77f --- /dev/null +++ b/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/client/AbstractConsumerInvokerTest.java @@ -0,0 +1,174 @@ +/* + * Tencent is pleased to support the open source community by making tRPC available. + * + * Copyright (C) 2023 Tencent. + * All rights reserved. + * + * If you have downloaded a copy of the tRPC source code from Tencent, + * please note that tRPC source code is licensed under the Apache 2.0 License, + * A copy of the Apache 2.0 License can be found in the LICENSE file. + */ + +package com.tencent.trpc.proto.http.client; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.tencent.trpc.core.common.ShutdownListener; +import com.tencent.trpc.core.common.config.BackendConfig; +import com.tencent.trpc.core.common.config.ConsumerConfig; +import com.tencent.trpc.core.common.config.ProtocolConfig; +import com.tencent.trpc.core.logger.Logger; +import com.tencent.trpc.core.logger.LoggerFactory; +import com.tencent.trpc.core.rpc.AbstractRpcClient; +import com.tencent.trpc.core.rpc.Request; +import com.tencent.trpc.core.rpc.Response; +import com.tencent.trpc.core.worker.spi.WorkerPool; +import java.util.HashMap; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * 测试 AbstractConsumerInvoker 的 ShutdownListener 功能 + */ +public class AbstractConsumerInvokerTest { + + private static final Logger logger = LoggerFactory.getLogger(AbstractConsumerInvokerTest.class); + + private AbstractRpcClient mockClient; + private ConsumerConfig mockConfig; + private ProtocolConfig mockProtocolConfig; + private BackendConfig mockBackendConfig; + private WorkerPool mockWorkerPool; + + private TestConsumerInvoker testInvoker; + + @Before + public void setUp() { + // 创建 mock 对象 + mockClient = mock(AbstractRpcClient.class); + mockConfig = mock(ConsumerConfig.class); + mockProtocolConfig = mock(ProtocolConfig.class); + mockBackendConfig = mock(BackendConfig.class); + mockWorkerPool = mock(WorkerPool.class); + + // 设置 mock 对象的行为 + when(mockConfig.getBackendConfig()).thenReturn(mockBackendConfig); + when(mockBackendConfig.getWorkerPoolObj()).thenReturn(mockWorkerPool); + when(mockProtocolConfig.getIp()).thenReturn("127.0.0.1"); + when(mockProtocolConfig.getPort()).thenReturn(8080); + when(mockProtocolConfig.getExtMap()).thenReturn(new HashMap()); + + // 创建测试实例 + testInvoker = new TestConsumerInvoker(mockClient, mockConfig, mockProtocolConfig); + } + + @After + public void tearDown() { + // 重置静态状态 + AbstractConsumerInvoker.reset(); + } + + /** + * 测试 ShutdownListener 不为空 + */ + @Test + public void testShutdownListenerNotNull() { + ShutdownListener shutdownListener = testInvoker.getShutdownListener(); + assertNotNull("ShutdownListener should not be null", shutdownListener); + } + + /** + * 测试 onShutdown 方法的日志输出和执行 + */ + @Test + public void testOnShutdownExecution() { + // 获取 ShutdownListener + ShutdownListener shutdownListener = testInvoker.getShutdownListener(); + assertNotNull("ShutdownListener should not be null", shutdownListener); + + // 测试 onShutdown 方法不会抛出异常 + try { + shutdownListener.onShutdown(); + // 如果没有异常,测试通过 + assertTrue("onShutdown method should execute without exceptions", true); + } catch (Exception e) { + throw new AssertionError("onShutdown method should not throw exceptions", e); + } + } + + /** + * 测试多次调用 onShutdown 方法的安全性 + */ + @Test + public void testMultipleOnShutdownCalls() { + ShutdownListener shutdownListener = testInvoker.getShutdownListener(); + assertNotNull("ShutdownListener should not be null", shutdownListener); + + // 多次调用 onShutdown 方法,确保不会出现异常 + try { + shutdownListener.onShutdown(); + shutdownListener.onShutdown(); + shutdownListener.onShutdown(); + assertTrue("Multiple onShutdown calls should be safe", true); + } catch (Exception e) { + throw new AssertionError("Multiple onShutdown calls should not throw exceptions", e); + } + } + + /** + * 测试 ShutdownListener 的类型 + */ + @Test + public void testShutdownListenerType() { + ShutdownListener shutdownListener = testInvoker.getShutdownListener(); + assertNotNull("ShutdownListener should not be null", shutdownListener); + + // 验证 ShutdownListener 是内部类的实例 + String className = shutdownListener.getClass().getSimpleName(); + assertTrue("ShutdownListener should be InternalShutdownListener", + className.contains("InternalShutdownListener")); + } + + /** + * 测试静态方法 stop() 和 reset() 的调用 + */ + @Test + public void testStaticMethods() { + try { + // 测试静态方法调用不会抛出异常 + AbstractConsumerInvoker.stop(); + AbstractConsumerInvoker.reset(); + assertTrue("Static methods should execute without exceptions", true); + } catch (Exception e) { + throw new AssertionError("Static methods should not throw exceptions", e); + } + } + + /** + * 测试用的 ConsumerInvoker 实现类 + */ + private static class TestConsumerInvoker extends AbstractConsumerInvoker { + + public TestConsumerInvoker(AbstractRpcClient client, ConsumerConfig config, + ProtocolConfig protocolConfig) { + super(client, config, protocolConfig); + } + + @Override + public Response send(Request request) throws Exception { + // 简单的测试实现 + return null; + } + } + + /** + * 测试用的服务接口 + */ + private interface TestService { + String testMethod(String input); + } +} \ No newline at end of file From e19a490ad4ca7826205cf5bc30c4c48811ea1cc1 Mon Sep 17 00:00:00 2001 From: chuanliang Date: Wed, 17 Sep 2025 17:11:38 +0800 Subject: [PATCH 15/19] =?UTF-8?q?feat(test):=20=E6=96=B0=E5=A2=9EHTTP/HTTP?= =?UTF-8?q?S=E5=8D=8F=E8=AE=AE=E9=85=8D=E7=BD=AE=E5=8F=8AURI=E6=9E=84?= =?UTF-8?q?=E5=BB=BA=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/AbstractConsumerInvokerTest.java | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/client/AbstractConsumerInvokerTest.java b/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/client/AbstractConsumerInvokerTest.java index ecab6f77f..6aed98b45 100644 --- a/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/client/AbstractConsumerInvokerTest.java +++ b/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/client/AbstractConsumerInvokerTest.java @@ -1,4 +1,6 @@ /* + + * Tencent is pleased to support the open source community by making tRPC available. * * Copyright (C) 2023 Tencent. @@ -13,8 +15,13 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static com.tencent.trpc.transport.http.common.Constants.KEYSTORE_PATH; +import static com.tencent.trpc.transport.http.common.Constants.KEYSTORE_PASS; +import static com.tencent.trpc.transport.http.common.Constants.HTTPS_SCHEME; +import static com.tencent.trpc.transport.http.common.Constants.HTTP_SCHEME; import com.tencent.trpc.core.common.ShutdownListener; import com.tencent.trpc.core.common.config.BackendConfig; @@ -26,7 +33,10 @@ import com.tencent.trpc.core.rpc.Request; import com.tencent.trpc.core.rpc.Response; import com.tencent.trpc.core.worker.spi.WorkerPool; +import java.lang.reflect.Field; +import java.net.URI; import java.util.HashMap; +import java.util.Map; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -148,6 +158,121 @@ public void testStaticMethods() { } } + /** + * 测试 HTTP 协议的默认配置(不包含 keystore 配置) + */ + @Test + public void testHttpSchemeWithoutKeystore() throws Exception { + // 创建不包含 keystore 配置的 extMap + Map extMap = new HashMap<>(); + when(mockProtocolConfig.getExtMap()).thenReturn(extMap); + + // 创建新的测试实例 + TestConsumerInvoker httpInvoker = new TestConsumerInvoker(mockClient, mockConfig, mockProtocolConfig); + + // 通过反射获取 scheme 字段来验证 + Field schemeField = AbstractConsumerInvoker.class.getDeclaredField("scheme"); + schemeField.setAccessible(true); + String scheme = (String) schemeField.get(httpInvoker); + + assertEquals("Should use HTTP scheme when keystore is not configured", HTTP_SCHEME, scheme); + } + + /** + * 测试 HTTPS 协议的配置(包含 keystore 配置) + */ + @Test + public void testHttpsSchemeWithKeystore() throws Exception { + // 创建包含 keystore 配置的 extMap + Map extMap = new HashMap<>(); + extMap.put(KEYSTORE_PATH, "/path/to/keystore.jks"); + extMap.put(KEYSTORE_PASS, "password123"); + when(mockProtocolConfig.getExtMap()).thenReturn(extMap); + + // 创建新的测试实例 + TestConsumerInvoker httpsInvoker = new TestConsumerInvoker(mockClient, mockConfig, mockProtocolConfig); + + // 通过反射获取 scheme 字段来验证 + Field schemeField = AbstractConsumerInvoker.class.getDeclaredField("scheme"); + schemeField.setAccessible(true); + String scheme = (String) schemeField.get(httpsInvoker); + + assertEquals("Should use HTTPS scheme when keystore is configured", HTTPS_SCHEME, scheme); + } + + /** + * 测试只有 KEYSTORE_PATH 但没有 KEYSTORE_PASS 的情况(应该使用 HTTP) + */ + @Test + public void testHttpSchemeWithOnlyKeystorePath() throws Exception { + // 创建只包含 KEYSTORE_PATH 的 extMap + Map extMap = new HashMap<>(); + extMap.put(KEYSTORE_PATH, "/path/to/keystore.jks"); + when(mockProtocolConfig.getExtMap()).thenReturn(extMap); + + // 创建新的测试实例 + TestConsumerInvoker httpInvoker = new TestConsumerInvoker(mockClient, mockConfig, mockProtocolConfig); + + // 通过反射获取 scheme 字段来验证 + Field schemeField = AbstractConsumerInvoker.class.getDeclaredField("scheme"); + schemeField.setAccessible(true); + String scheme = (String) schemeField.get(httpInvoker); + + assertEquals("Should use HTTP scheme when only KEYSTORE_PATH is configured", HTTP_SCHEME, scheme); + } + + /** + * 测试只有 KEYSTORE_PASS 但没有 KEYSTORE_PATH 的情况(应该使用 HTTP) + */ + @Test + public void testHttpSchemeWithOnlyKeystorePass() throws Exception { + // 创建只包含 KEYSTORE_PASS 的 extMap + Map extMap = new HashMap<>(); + extMap.put(KEYSTORE_PASS, "password123"); + when(mockProtocolConfig.getExtMap()).thenReturn(extMap); + + // 创建新的测试实例 + TestConsumerInvoker httpInvoker = new TestConsumerInvoker(mockClient, mockConfig, mockProtocolConfig); + + // 通过反射获取 scheme 字段来验证 + Field schemeField = AbstractConsumerInvoker.class.getDeclaredField("scheme"); + schemeField.setAccessible(true); + String scheme = (String) schemeField.get(httpInvoker); + + assertEquals("Should use HTTP scheme when only KEYSTORE_PASS is configured", HTTP_SCHEME, scheme); + } + + /** + * 测试 URI 构建在不同协议下的正确性 + */ + @Test + public void testUriConstructionWithDifferentSchemes() throws Exception { + // 设置 mock 对象的基本配置 + when(mockConfig.getBackendConfig().getBasePath()).thenReturn("/api"); + + // 创建 mock request 和 invocation + Request mockRequest = mock(Request.class); + com.tencent.trpc.core.rpc.RpcInvocation mockInvocation = mock(com.tencent.trpc.core.rpc.RpcInvocation.class); + when(mockRequest.getInvocation()).thenReturn(mockInvocation); + when(mockInvocation.getFunc()).thenReturn("/test"); + + // 测试 HTTP 协议的 URI + Map httpExtMap = new HashMap<>(); + when(mockProtocolConfig.getExtMap()).thenReturn(httpExtMap); + TestConsumerInvoker httpInvoker = new TestConsumerInvoker(mockClient, mockConfig, mockProtocolConfig); + URI httpUri = httpInvoker.getUri(mockRequest); + assertEquals("HTTP URI scheme should be http", HTTP_SCHEME, httpUri.getScheme()); + + // 测试 HTTPS 协议的 URI + Map httpsExtMap = new HashMap<>(); + httpsExtMap.put(KEYSTORE_PATH, "/path/to/keystore.jks"); + httpsExtMap.put(KEYSTORE_PASS, "password123"); + when(mockProtocolConfig.getExtMap()).thenReturn(httpsExtMap); + TestConsumerInvoker httpsInvoker = new TestConsumerInvoker(mockClient, mockConfig, mockProtocolConfig); + URI httpsUri = httpsInvoker.getUri(mockRequest); + assertEquals("HTTPS URI scheme should be https", HTTPS_SCHEME, httpsUri.getScheme()); + } + /** * 测试用的 ConsumerInvoker 实现类 */ From 4b81079ef6405af7baad4ed7a58ec72f7140e22d Mon Sep 17 00:00:00 2001 From: chuanliang Date: Wed, 17 Sep 2025 17:17:26 +0800 Subject: [PATCH 16/19] =?UTF-8?q?feat(test):=20=E6=96=B0=E5=A2=9E=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E9=AA=8C=E8=AF=81=20MXBean=20=E4=B8=BA=20null=20?= =?UTF-8?q?=E6=97=B6=E5=85=B3=E9=97=AD=E6=96=B9=E6=B3=95=E6=AD=A3=E5=B8=B8?= =?UTF-8?q?=E6=89=A7=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thread/ForkJoinWorkerPoolTest.java | 21 +++++++++++++++++++ .../support/thread/ThreadWorkerPoolTest.java | 19 +++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/trpc-core/src/test/java/com/tencent/trpc/core/worker/support/thread/ForkJoinWorkerPoolTest.java b/trpc-core/src/test/java/com/tencent/trpc/core/worker/support/thread/ForkJoinWorkerPoolTest.java index b70f97d5e..3d632723a 100644 --- a/trpc-core/src/test/java/com/tencent/trpc/core/worker/support/thread/ForkJoinWorkerPoolTest.java +++ b/trpc-core/src/test/java/com/tencent/trpc/core/worker/support/thread/ForkJoinWorkerPoolTest.java @@ -109,4 +109,25 @@ public void testMBeanUnregistrationOnClose() throws Exception { Assert.assertFalse("MBean should be unregistered after close", mBeanServer.isRegistered(objectName)); } + + /** + * Test close method when forkJoinPoolMXBean is null (covers the null check branch) + */ + @Test + public void testCloseWithNullMXBean() throws Exception { + Map properties = new HashMap<>(); + properties.put(ForkJoinPoolConfig.PARALLELISM, PARALLELISM); + properties.put(ForkJoinPoolConfig.TIMEOUT_MS, TIMEOUT_MILLS); + PluginConfig poolPluginConfig = new PluginConfig(ForkJoinWorkerPool.NAME, ThreadWorkerPool.class, + properties); + ForkJoinWorkerPool forkJoinWorkerPool = new ForkJoinWorkerPool(); + forkJoinWorkerPool.setPluginConfig(poolPluginConfig); + + // Don't call init() so forkJoinPoolMXBean remains null + // This should not throw any exception when closing + forkJoinWorkerPool.close(1000); + + // Verify no exception is thrown and method completes successfully + Assert.assertTrue("Close method should complete successfully even with null MXBean", true); + } } \ No newline at end of file diff --git a/trpc-core/src/test/java/com/tencent/trpc/core/worker/support/thread/ThreadWorkerPoolTest.java b/trpc-core/src/test/java/com/tencent/trpc/core/worker/support/thread/ThreadWorkerPoolTest.java index 633e92740..094fec0fb 100644 --- a/trpc-core/src/test/java/com/tencent/trpc/core/worker/support/thread/ThreadWorkerPoolTest.java +++ b/trpc-core/src/test/java/com/tencent/trpc/core/worker/support/thread/ThreadWorkerPoolTest.java @@ -147,4 +147,23 @@ public void testMBeanUnregistrationOnClose() throws Exception { mBeanServer.isRegistered(objectName)); } + /** + * Test close method when threadPoolMXBean is null (covers the null check branch) + */ + @Test + public void testCloseWithNullMXBean() throws Exception { + Map properties = getProperties(); + PluginConfig poolPluginConfig = new PluginConfig("work_pool", ThreadWorkerPool.class, + properties); + ThreadWorkerPool threadWorkerPool = new ThreadWorkerPool(); + threadWorkerPool.setPluginConfig(poolPluginConfig); + + // Don't call init() so threadPoolMXBean remains null + // This should not throw any exception when closing + threadWorkerPool.close(1000); + + // Verify no exception is thrown and method completes successfully + Assert.assertTrue("Close method should complete successfully even with null MXBean", true); + } + } \ No newline at end of file From 9bb151fae7fe5e6cabf3c3556c0a0a6bb8ce80ba Mon Sep 17 00:00:00 2001 From: chuanliang Date: Wed, 17 Sep 2025 17:28:50 +0800 Subject: [PATCH 17/19] =?UTF-8?q?test:=20=E6=96=B0=E5=A2=9E=20MBean=20?= =?UTF-8?q?=E6=B3=A8=E5=86=8C=E6=B3=A8=E9=94=80=E5=BC=82=E5=B8=B8=E5=A4=84?= =?UTF-8?q?=E7=90=86=E5=8F=8A=20TestMBean=20=E6=B5=8B=E8=AF=95=E7=94=A8?= =?UTF-8?q?=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../support/MBeanRegistryHelperTest.java | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 trpc-core/src/test/java/com/tencent/trpc/core/management/support/MBeanRegistryHelperTest.java diff --git a/trpc-core/src/test/java/com/tencent/trpc/core/management/support/MBeanRegistryHelperTest.java b/trpc-core/src/test/java/com/tencent/trpc/core/management/support/MBeanRegistryHelperTest.java new file mode 100644 index 000000000..c99c580fc --- /dev/null +++ b/trpc-core/src/test/java/com/tencent/trpc/core/management/support/MBeanRegistryHelperTest.java @@ -0,0 +1,127 @@ +/* + * Tencent is pleased to support the open source community by making tRPC available. + * + * Copyright (C) 2023 Tencent. + * All rights reserved. + * + * If you have downloaded a copy of the tRPC source code from Tencent, + * please note that tRPC source code is licensed under the Apache 2.0 License, + * A copy of the Apache 2.0 License can be found in the LICENSE file. + */ + +package com.tencent.trpc.core.management.support; + +import java.lang.management.ManagementFactory; +import javax.management.MBeanServer; +import javax.management.ObjectName; +import org.junit.Assert; +import org.junit.Test; + +/** + * Test class for MBeanRegistryHelper to cover exception handling branches + */ +public class MBeanRegistryHelperTest { + + /** + * Test registerMBean method with normal operation - should not throw exception + */ + @Test + public void testRegisterMBeanNormal() throws Exception { + // Create test objects + Object testObject = new TestMBeanImpl(); + ObjectName objectName = new ObjectName("test:type=TestMBean"); + + // This should not throw exception + MBeanRegistryHelper.registerMBean(testObject, objectName); + + // Clean up - unregister the MBean + MBeanRegistryHelper.unregisterMBean(objectName); + } + + /** + * Test registerMBean method with invalid ObjectName - should trigger exception handling + */ + @Test + public void testRegisterMBeanWithInvalidObjectName() throws Exception { + // Create test objects + Object testObject = new Object(); // Not a valid MBean + ObjectName objectName = new ObjectName("test:type=TestMBean"); + + // This should trigger exception handling (NotCompliantMBeanException) but not throw + MBeanRegistryHelper.registerMBean(testObject, objectName); + } + + /** + * Test registerMBean method with duplicate registration - should trigger exception handling + */ + @Test + public void testRegisterMBeanDuplicate() throws Exception { + // Create test objects + Object testObject = new TestMBeanImpl(); + ObjectName objectName = new ObjectName("test:type=DuplicateTestMBean"); + + try { + // Register first time - should succeed + MBeanRegistryHelper.registerMBean(testObject, objectName); + + // Register second time - should trigger exception handling (InstanceAlreadyExistsException) but not throw + MBeanRegistryHelper.registerMBean(testObject, objectName); + } finally { + // Clean up + MBeanRegistryHelper.unregisterMBean(objectName); + } + } + + /** + * Test unregisterMBean method with valid ObjectName - should work normally + */ + @Test + public void testUnregisterMBeanNormal() throws Exception { + // Create ObjectName for a simple test + ObjectName objectName = new ObjectName("test:type=UnregisterTestMBean"); + + // This should not throw exception even if MBean doesn't exist + MBeanRegistryHelper.unregisterMBean(objectName); + } + + /** + * Test unregisterMBean method with non-existent ObjectName - should not throw exception + */ + @Test + public void testUnregisterMBeanNonExistent() throws Exception { + // Create ObjectName for non-existent MBean + ObjectName objectName = new ObjectName("test:type=NonExistentMBean"); + + // This should not throw exception even though MBean doesn't exist + MBeanRegistryHelper.unregisterMBean(objectName); + } + + /** + * Test unregisterMBean method with invalid ObjectName pattern - should trigger exception handling + */ + @Test + public void testUnregisterMBeanWithInvalidPattern() throws Exception { + // Create ObjectName with pattern (which cannot be used for unregistering) + ObjectName objectName = new ObjectName("test:type=*"); + + // This should trigger exception handling but not throw + MBeanRegistryHelper.unregisterMBean(objectName); + } + + /** + * Test MBean interface for testing purposes + */ + public interface TestMBean { + String getName(); + } + + /** + * Test MBean implementation for testing purposes + */ + public static class TestMBeanImpl implements TestMBean { + @Override + public String getName() { + return "TestMBean"; + } + } +} \ No newline at end of file From 54ae07efa26c21e4bbe93a56d2041770654483fb Mon Sep 17 00:00:00 2001 From: chuanliang Date: Wed, 17 Sep 2025 18:48:05 +0800 Subject: [PATCH 18/19] =?UTF-8?q?docs:=20=E5=B0=86=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=B1=BB=E6=B3=A8=E9=87=8A=E4=BB=8E=E4=B8=AD=E6=96=87=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E8=8B=B1=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trpc/proto/http/client/AbstractConsumerInvokerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/client/AbstractConsumerInvokerTest.java b/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/client/AbstractConsumerInvokerTest.java index 6aed98b45..8b7fee455 100644 --- a/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/client/AbstractConsumerInvokerTest.java +++ b/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/client/AbstractConsumerInvokerTest.java @@ -42,7 +42,7 @@ import org.junit.Test; /** - * 测试 AbstractConsumerInvoker 的 ShutdownListener 功能 + * Test the ShutdownListener functionality of AbstractConsumerInvoker */ public class AbstractConsumerInvokerTest { From 130229aff32174cb2661c66c55d9a842f90794a8 Mon Sep 17 00:00:00 2001 From: chuanliang Date: Wed, 17 Sep 2025 18:55:44 +0800 Subject: [PATCH 19/19] =?UTF-8?q?test:=20=E6=96=B0=E5=A2=9E=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E9=AA=8C=E8=AF=81=20unregisterMBean=20=E5=AF=B9=20nul?= =?UTF-8?q?l=20=E5=8F=82=E6=95=B0=E7=9A=84=E5=BC=82=E5=B8=B8=E5=A4=84?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../support/MBeanRegistryHelperTest.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/trpc-core/src/test/java/com/tencent/trpc/core/management/support/MBeanRegistryHelperTest.java b/trpc-core/src/test/java/com/tencent/trpc/core/management/support/MBeanRegistryHelperTest.java index c99c580fc..99aa2a20e 100644 --- a/trpc-core/src/test/java/com/tencent/trpc/core/management/support/MBeanRegistryHelperTest.java +++ b/trpc-core/src/test/java/com/tencent/trpc/core/management/support/MBeanRegistryHelperTest.java @@ -108,6 +108,29 @@ public void testUnregisterMBeanWithInvalidPattern() throws Exception { MBeanRegistryHelper.unregisterMBean(objectName); } + /** + * Test unregisterMBean method exception handling by creating a scenario that triggers exception + * This test covers the exception handling branch in unregisterMBean method + */ + @Test + public void testUnregisterMBeanException() throws Exception { + // Test with null ObjectName which should cause RuntimeOperationsException + // in the isRegistered(null) call, which will trigger the exception handling branch + + // This should not throw any exception - the exception should be caught and logged + try { + MBeanRegistryHelper.unregisterMBean(null); + // If we reach here, it means the exception was properly caught and handled + } catch (Exception e) { + // If any exception escapes, the test should fail + Assert.fail("Exception should have been caught and logged, but was thrown: " + e.getMessage()); + } + + // The test passes if no exception is thrown (exception is caught and logged) + // The logger.warn("unregister mbean exception: ", e) line should be executed + // This covers the exception handling branch that was previously untested + } + /** * Test MBean interface for testing purposes */