From f97df8750188eb2148e5ff9cb82bc3da41726e22 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Tue, 20 Jan 2026 15:52:00 +0800 Subject: [PATCH 01/38] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E9=9B=86=E6=88=90CI/CD?= =?UTF-8?q?=E8=BF=9B=E8=A1=8C=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/maven.yml | 74 +++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 .github/workflows/maven.yml diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 0000000..0e66740 --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,74 @@ +name: Java CI with Maven, Nacos, and Python + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + integration-test: + runs-on: ubuntu-latest + + services: + nacos: + image: nacos/nacos-server:v2.2.3 + env: + MODE: standalone + AUTH_ENABLE: false + ports: + - 8848:8848 + # Wait for Nacos to be fully ready + options: >- + --name nacos-server + --health-cmd "curl -f http://localhost:8848/nacos/v1/console/health/readiness" + --health-interval 10s + --health-timeout 5s + --health-retries 10 + + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + cache: 'pip' + + - name: Install Python Dependencies + run: | + python -m pip install --upgrade pip + pip install grpcio grpcio-tools protobuf + + - name: Build with Maven + run: mvn clean install -DskipTests + + - name: Start RPC Provider (Java Server) + run: | + # Build classpath including project jars and dependencies + CLASSPATH="rpc-provider/target/rpc-provider-1.0-SNAPSHOT.jar:rpc-core/target/rpc-core-1.0-SNAPSHOT.jar:rpc-common/target/rpc-common-1.0-SNAPSHOT.jar:rpc-api/target/rpc-api-1.0-SNAPSHOT.jar:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout -pl rpc-provider -am)" + + echo "Starting Java Server..." + # Run in background with nohup + nohup java -cp "$CLASSPATH" com.xiaoyu.rpc.provider.ProviderApp > server.log 2>&1 & + + # Wait for server port 8080 to be available + echo "Waiting for Java Server to start on port 8080..." + timeout 60s bash -c 'until echo > /dev/tcp/localhost/8080; do sleep 1; done' + + echo "Java Server is running!" + # Optional: Print initial logs + head -n 20 server.log + + - name: Run Python Client + working-directory: python_client + run: | + echo "Running Python Client..." + python client.py From 01dcf0796f961780da5b5ac4321b1138dbc31fb1 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Tue, 20 Jan 2026 16:05:05 +0800 Subject: [PATCH 02/38] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E4=BA=86=E5=AF=B9pytho?= =?UTF-8?q?n=E5=AE=A2=E6=88=B7=E7=AB=AF=E7=8E=AF=E5=A2=83=E7=9A=84requirem?= =?UTF-8?q?ent.txt=E7=9A=84=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/maven.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 0e66740..d66de1b 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -40,7 +40,7 @@ jobs: uses: actions/setup-python@v4 with: python-version: '3.x' - cache: 'pip' + - name: Install Python Dependencies run: | From 694f1b2c887a266f51ea683792392cec6c801fa4 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Tue, 20 Jan 2026 16:20:38 +0800 Subject: [PATCH 03/38] docs: improve CI/CD section in README and fix workflow cache --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index a80c050..ef147fe 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,17 @@ This framework supports interoperability with standard gRPC clients (e.g., Pytho Message: Success ``` +## 🔄 Continuous Integration & Delivery (CI/CD) + +To ensure system reliability and code quality, this project integrates a robust CI/CD pipeline using **GitHub Actions**. This pipeline automatically validates the build process and runs integration tests upon every push and pull request. + +**Key Workflows:** +- **Automated Testing**: Runs unit and integration tests to verify RPC functionality. +- **Service Verification**: Launches Nacos, the Java Provider, and Python Client in a containerized environment to test cross-language interoperability. +- **Build Status**: Provides immediate feedback on code health via GitHub Actions. + +![CI/CD Workflow Result](docs/images/image.png) + --- ## 🤝 Contributing From 4e44fd118bd5ef811c6a327a245f08515b25267a Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Tue, 20 Jan 2026 16:26:52 +0800 Subject: [PATCH 04/38] =?UTF-8?q?=E4=BF=AE=E6=94=B9README.md=E6=96=87?= =?UTF-8?q?=E4=BB=B6=EF=BC=8C=E5=A2=9E=E5=8A=A0CI/CD=E5=86=85=E5=AE=B9?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- docs/images/image.png | Bin 0 -> 590059 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 docs/images/image.png diff --git a/README.md b/README.md index ef147fe..bc9313f 100644 --- a/README.md +++ b/README.md @@ -186,7 +186,6 @@ This framework supports interoperability with standard gRPC clients (e.g., Pytho Data: Hello, World! (from Multi-Module Netty Server) Message: Success ``` - ## 🔄 Continuous Integration & Delivery (CI/CD) To ensure system reliability and code quality, this project integrates a robust CI/CD pipeline using **GitHub Actions**. This pipeline automatically validates the build process and runs integration tests upon every push and pull request. @@ -198,6 +197,7 @@ To ensure system reliability and code quality, this project integrates a robust ![CI/CD Workflow Result](docs/images/image.png) + --- ## 🤝 Contributing diff --git a/docs/images/image.png b/docs/images/image.png new file mode 100644 index 0000000000000000000000000000000000000000..5d9c706d76d1c9e3d10d37f298e6656d63b95776 GIT binary patch literal 590059 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�(pOX0P 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%0A)X*wLu`&7U+cB=o+?o{IPC>u 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π$s5cS(>pLZx^sjQCu42 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%0AfLu@uhKlw`n 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⋙z;*v)xg+F}fz>Y#Z~j*n5xl@oS8 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 Date: Fri, 23 Jan 2026 09:22:34 +0800 Subject: [PATCH 05/38] =?UTF-8?q?=E5=B0=86=E6=B5=8B=E8=AF=95=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=94=B9=E6=88=90=E4=BD=BF=E7=94=A8Juint=E6=9D=A5?= =?UTF-8?q?=E5=86=99=E5=90=8C=E6=97=B6=E4=BF=AE=E6=94=B9CI/CD=E6=B5=81?= =?UTF-8?q?=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/maven.yml | 5 + rpc-core/pom.xml | 5 + .../xiaoyu/rpc/core/config/RpcConfigTest.java | 115 +++++++++++ .../core/extension/ExtensionLoaderTest.java | 131 +++++++++++++ .../core/loadbalancer/LoadBalancerTest.java | 106 ++++++++++ .../rpc/core/protocol/ProtocolTest.java | 75 +++++++ .../rpc/core/registry/LocalRegistryTest.java | 83 ++++++++ .../core/serialization/SerializerTest.java | 185 ++++++++++++++++++ .../rpc/core/test/LoadBalancerTest.java | 33 ---- .../xiaoyu/rpc/core/test/ProtocolSpiTest.java | 37 ---- .../com/xiaoyu/rpc/core/test/SpiTest.java | 54 ----- 11 files changed, 705 insertions(+), 124 deletions(-) create mode 100644 rpc-core/src/test/java/com/xiaoyu/rpc/core/config/RpcConfigTest.java create mode 100644 rpc-core/src/test/java/com/xiaoyu/rpc/core/extension/ExtensionLoaderTest.java create mode 100644 rpc-core/src/test/java/com/xiaoyu/rpc/core/loadbalancer/LoadBalancerTest.java create mode 100644 rpc-core/src/test/java/com/xiaoyu/rpc/core/protocol/ProtocolTest.java create mode 100644 rpc-core/src/test/java/com/xiaoyu/rpc/core/registry/LocalRegistryTest.java create mode 100644 rpc-core/src/test/java/com/xiaoyu/rpc/core/serialization/SerializerTest.java delete mode 100644 rpc-core/src/test/java/com/xiaoyu/rpc/core/test/LoadBalancerTest.java delete mode 100644 rpc-core/src/test/java/com/xiaoyu/rpc/core/test/ProtocolSpiTest.java delete mode 100644 rpc-core/src/test/java/com/xiaoyu/rpc/core/test/SpiTest.java diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index d66de1b..9869712 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -50,6 +50,11 @@ jobs: - name: Build with Maven run: mvn clean install -DskipTests + - name: Run Unit Tests + run: | + echo "Running unit tests..." + mvn test -pl rpc-core -am -Dtest="**/extension/*Test,**/serialization/*Test,**/loadbalancer/*Test,**/registry/*Test,**/config/*Test,**/protocol/*Test" + - name: Start RPC Provider (Java Server) run: | # Build classpath including project jars and dependencies diff --git a/rpc-core/pom.xml b/rpc-core/pom.xml index 5b97bf5..d3bd67e 100644 --- a/rpc-core/pom.xml +++ b/rpc-core/pom.xml @@ -66,6 +66,11 @@ gson 2.10.1 + + org.junit.jupiter + junit-jupiter + test + diff --git a/rpc-core/src/test/java/com/xiaoyu/rpc/core/config/RpcConfigTest.java b/rpc-core/src/test/java/com/xiaoyu/rpc/core/config/RpcConfigTest.java new file mode 100644 index 0000000..b12a7b5 --- /dev/null +++ b/rpc-core/src/test/java/com/xiaoyu/rpc/core/config/RpcConfigTest.java @@ -0,0 +1,115 @@ +package com.xiaoyu.rpc.core.config; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; + +import java.lang.reflect.Field; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * RPC 配置单元测试 + */ +@DisplayName("RpcConfig 配置测试") +public class RpcConfigTest { + + @BeforeEach + void setUp() throws Exception { + // 重置单例以便每个测试独立 + resetSingleton(); + // 清理测试用的系统属性 + System.clearProperty("rpc.registry"); + System.clearProperty("rpc.serializer"); + System.clearProperty("rpc.server-port"); + } + + @AfterEach + void tearDown() throws Exception { + // 清理系统属性 + System.clearProperty("rpc.registry"); + System.clearProperty("rpc.serializer"); + System.clearProperty("rpc.server-port"); + // 重置单例 + resetSingleton(); + } + + private void resetSingleton() throws Exception { + Field instanceField = RpcConfig.class.getDeclaredField("instance"); + instanceField.setAccessible(true); + instanceField.set(null, null); + } + + @Test + @DisplayName("测试单例模式") + void testSingletonPattern() { + RpcConfig config1 = RpcConfig.getInstance(); + RpcConfig config2 = RpcConfig.getInstance(); + + assertSame(config1, config2, "RpcConfig should be singleton"); + } + + @Test + @DisplayName("测试默认配置值") + void testDefaultConfigValues() { + RpcConfig config = RpcConfig.getInstance(); + + assertNotNull(config.getSerializerType(), "Serializer type should not be null"); + assertNotNull(config.getServerHost(), "Server host should not be null"); + assertNotNull(config.getServerPort(), "Server port should not be null"); + assertNotNull(config.getProtocol(), "Protocol should not be null"); + } + + @Test + @DisplayName("测试系统属性覆盖 - 注册中心类型") + void testSystemPropertyOverrideRegistry() throws Exception { + System.setProperty("rpc.registry", "local"); + resetSingleton(); + + RpcConfig config = RpcConfig.getInstance(); + assertEquals("local", config.getRegistryType(), "Registry type should be overridden by system property"); + } + + @Test + @DisplayName("测试系统属性覆盖 - 序列化器") + void testSystemPropertyOverrideSerializer() throws Exception { + System.setProperty("rpc.serializer", "kryo"); + resetSingleton(); + + RpcConfig config = RpcConfig.getInstance(); + assertEquals("kryo", config.getSerializerType(), "Serializer should be overridden by system property"); + } + + @Test + @DisplayName("测试系统属性覆盖 - 服务端口") + void testSystemPropertyOverridePort() throws Exception { + System.setProperty("rpc.server-port", "9999"); + resetSingleton(); + + RpcConfig config = RpcConfig.getInstance(); + assertEquals(9999, config.getServerPort(), "Server port should be overridden by system property"); + } + + @Test + @DisplayName("测试 getSerializerCode 方法") + void testGetSerializerCode() { + System.setProperty("rpc.serializer", "kryo"); + RpcConfig config = RpcConfig.getInstance(); + + byte code = config.getSerializerCode(); + assertTrue(code > 0, "Serializer code should be positive"); + } + + @Test + @DisplayName("测试 toString 方法") + void testToString() { + RpcConfig config = RpcConfig.getInstance(); + String str = config.toString(); + + assertNotNull(str, "toString should not return null"); + assertTrue(str.contains("RpcConfig"), "toString should contain class name"); + assertTrue(str.contains("serializerType"), "toString should contain serializerType"); + assertTrue(str.contains("serverPort"), "toString should contain serverPort"); + } +} diff --git a/rpc-core/src/test/java/com/xiaoyu/rpc/core/extension/ExtensionLoaderTest.java b/rpc-core/src/test/java/com/xiaoyu/rpc/core/extension/ExtensionLoaderTest.java new file mode 100644 index 0000000..3cc0dcb --- /dev/null +++ b/rpc-core/src/test/java/com/xiaoyu/rpc/core/extension/ExtensionLoaderTest.java @@ -0,0 +1,131 @@ +package com.xiaoyu.rpc.core.extension; + +import com.xiaoyu.rpc.common.extension.ExtensionLoader; +import com.xiaoyu.rpc.common.serialization.Serializer; +import com.xiaoyu.rpc.core.loadbalancer.LoadBalancer; +import com.xiaoyu.rpc.core.client.ProxyFactory; +import com.xiaoyu.rpc.core.protocol.Protocol; +import com.xiaoyu.rpc.core.registry.ServiceRegistry; +import com.xiaoyu.rpc.core.registry.ServiceDiscovery; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * ExtensionLoader SPI 机制单元测试 + */ +@DisplayName("SPI ExtensionLoader 测试") +public class ExtensionLoaderTest { + + @Test + @DisplayName("测试加载 Serializer 扩展") + void testLoadSerializerExtensions() { + ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Serializer.class); + + // 测试所有支持的序列化器 + assertNotNull(loader.getExtension("java"), "Java serializer should be loaded"); + assertNotNull(loader.getExtension("kryo"), "Kryo serializer should be loaded"); + assertNotNull(loader.getExtension("protobuf"), "Protobuf serializer should be loaded"); + assertNotNull(loader.getExtension("json"), "JSON serializer should be loaded"); + } + + @Test + @DisplayName("测试获取所有支持扩展名") + void testGetSupportedExtensions() { + ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Serializer.class); + + var extensions = loader.getSupportedExtensions(); + assertTrue(extensions.contains("java"), "Should contain 'java' extension"); + assertTrue(extensions.contains("kryo"), "Should contain 'kryo' extension"); + assertTrue(extensions.contains("protobuf"), "Should contain 'protobuf' extension"); + assertTrue(extensions.contains("json"), "Should contain 'json' extension"); + assertEquals(4, extensions.size(), "Should have exactly 4 serializer extensions"); + } + + @Test + @DisplayName("测试扩展单例缓存") + void testExtensionCaching() { + ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Serializer.class); + + Serializer first = loader.getExtension("kryo"); + Serializer second = loader.getExtension("kryo"); + + assertSame(first, second, "Same extension should return same instance (singleton)"); + } + + @Test + @DisplayName("测试加载不存在扩展时抛出异常") + void testLoadNonExistentExtension() { + ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Serializer.class); + + assertThrows(RuntimeException.class, () -> { + loader.getExtension("non_existent"); + }, "Loading non-existent extension should throw exception"); + } + + @Test + @DisplayName("测试 LoadBalancer 扩展加载") + void testLoadBalancerExtensions() { + ExtensionLoader loader = ExtensionLoader.getExtensionLoader(LoadBalancer.class); + + assertNotNull(loader.getExtension("random"), "Random load balancer should be loaded"); + assertNotNull(loader.getExtension("roundrobin"), "RoundRobin load balancer should be loaded"); + + var extensions = loader.getSupportedExtensions(); + assertEquals(2, extensions.size(), "Should have exactly 2 load balancer extensions"); + } + + @Test + @DisplayName("测试 Protocol 扩展加载") + void testProtocolExtensions() { + ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Protocol.class); + + assertNotNull(loader.getExtension("netty"), "Netty protocol should be loaded"); + assertNotNull(loader.getExtension("http"), "HTTP protocol should be loaded"); + assertNotNull(loader.getExtension("http2"), "HTTP2 protocol should be loaded"); + assertNotNull(loader.getExtension("grpc"), "gRPC protocol should be loaded"); + + var extensions = loader.getSupportedExtensions(); + assertEquals(4, extensions.size(), "Should have exactly 4 protocol extensions"); + } + + @Test + @DisplayName("测试 ProxyFactory 扩展加载") + void testProxyFactoryExtensions() { + ExtensionLoader loader = ExtensionLoader.getExtensionLoader(ProxyFactory.class); + + assertNotNull(loader.getExtension("jdk"), "JDK proxy factory should be loaded"); + assertNotNull(loader.getExtension("bytebuddy"), "ByteBuddy proxy factory should be loaded"); + + var extensions = loader.getSupportedExtensions(); + assertEquals(2, extensions.size(), "Should have exactly 2 proxy factory extensions"); + } + + @Test + @DisplayName("测试 ServiceRegistry 扩展加载") + void testServiceRegistryExtensions() { + ExtensionLoader loader = ExtensionLoader.getExtensionLoader(ServiceRegistry.class); + + // local registry should be loadable without external dependencies + assertNotNull(loader.getExtension("local"), "Local service registry should be loaded"); + + var extensions = loader.getSupportedExtensions(); + assertTrue(extensions.contains("local"), "Should contain 'local' extension"); + assertTrue(extensions.contains("nacos"), "Should contain 'nacos' extension"); + } + + @Test + @DisplayName("测试 ServiceDiscovery 扩展加载") + void testServiceDiscoveryExtensions() { + ExtensionLoader loader = ExtensionLoader.getExtensionLoader(ServiceDiscovery.class); + + // local registry should be loadable without external dependencies + assertNotNull(loader.getExtension("local"), "Local service discovery should be loaded"); + + var extensions = loader.getSupportedExtensions(); + assertTrue(extensions.contains("local"), "Should contain 'local' extension"); + assertTrue(extensions.contains("nacos"), "Should contain 'nacos' extension"); + } +} diff --git a/rpc-core/src/test/java/com/xiaoyu/rpc/core/loadbalancer/LoadBalancerTest.java b/rpc-core/src/test/java/com/xiaoyu/rpc/core/loadbalancer/LoadBalancerTest.java new file mode 100644 index 0000000..0f241d3 --- /dev/null +++ b/rpc-core/src/test/java/com/xiaoyu/rpc/core/loadbalancer/LoadBalancerTest.java @@ -0,0 +1,106 @@ +package com.xiaoyu.rpc.core.loadbalancer; + +import com.xiaoyu.rpc.common.extension.ExtensionLoader; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.RepeatedTest; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * 负载均衡器单元测试 + */ +@DisplayName("LoadBalancer 负载均衡器测试") +public class LoadBalancerTest { + + private final List servers = Arrays.asList( + "127.0.0.1:8080", + "127.0.0.1:8081", + "127.0.0.1:8082"); + + @Test + @DisplayName("测试 Random 负载均衡器加载") + void testRandomLoadBalancerLoading() { + LoadBalancer lb = ExtensionLoader.getExtensionLoader(LoadBalancer.class).getExtension("random"); + assertNotNull(lb, "Random load balancer should be loaded"); + assertTrue(lb instanceof RandomLoadBalancer, "Should be instance of RandomLoadBalancer"); + } + + @Test + @DisplayName("测试 RoundRobin 负载均衡器加载") + void testRoundRobinLoadBalancerLoading() { + LoadBalancer lb = ExtensionLoader.getExtensionLoader(LoadBalancer.class).getExtension("roundrobin"); + assertNotNull(lb, "RoundRobin load balancer should be loaded"); + assertTrue(lb instanceof RoundRobinLoadBalancer, "Should be instance of RoundRobinLoadBalancer"); + } + + @Test + @DisplayName("测试 Random 负载均衡器选择服务器") + void testRandomLoadBalancerSelect() { + LoadBalancer lb = ExtensionLoader.getExtensionLoader(LoadBalancer.class).getExtension("random"); + + // 多次选择确保都在列表中 + for (int i = 0; i < 10; i++) { + String selected = lb.select(servers); + assertNotNull(selected, "Selected server should not be null"); + assertTrue(servers.contains(selected), "Selected server should be in the list"); + } + } + + @Test + @DisplayName("测试 RoundRobin 负载均衡器轮询") + void testRoundRobinLoadBalancerSelect() { + // 创建新的 RoundRobin 实例来确保从头开始 + LoadBalancer lb = new RoundRobinLoadBalancer(); + + // 测试轮询模式 + String first = lb.select(servers); + String second = lb.select(servers); + String third = lb.select(servers); + String fourth = lb.select(servers); // 应该回到第一个 + + assertNotNull(first, "First selection should not be null"); + assertNotNull(second, "Second selection should not be null"); + assertNotNull(third, "Third selection should not be null"); + assertNotNull(fourth, "Fourth selection should not be null"); + + // 确保四次选择覆盖了所有服务器 + Set selected = new HashSet<>(Arrays.asList(first, second, third)); + assertEquals(3, selected.size(), "RoundRobin should cycle through all 3 servers"); + + // 第四次选择应该与前三次之一相同(循环) + assertTrue(servers.contains(fourth), "Fourth selection should be in server list"); + } + + @Test + @DisplayName("测试负载均衡器处理单节点列表") + void testLoadBalancerWithSingleServer() { + LoadBalancer randomLb = ExtensionLoader.getExtensionLoader(LoadBalancer.class).getExtension("random"); + LoadBalancer rrLb = ExtensionLoader.getExtensionLoader(LoadBalancer.class).getExtension("roundrobin"); + + List singleServer = Arrays.asList("127.0.0.1:9999"); + + assertEquals("127.0.0.1:9999", randomLb.select(singleServer), "Random should return the only server"); + assertEquals("127.0.0.1:9999", rrLb.select(singleServer), "RoundRobin should return the only server"); + } + + @RepeatedTest(5) + @DisplayName("测试 Random 负载均衡器随机性") + void testRandomnessOfRandomLoadBalancer() { + LoadBalancer lb = ExtensionLoader.getExtensionLoader(LoadBalancer.class).getExtension("random"); + + Set selections = new HashSet<>(); + // 多次选择,应该最终选中所有服务器 + for (int i = 0; i < 100; i++) { + selections.add(lb.select(servers)); + } + + // 在100次随机选择后,应该覆盖所有3个服务器 + assertEquals(3, selections.size(), "Random load balancer should eventually select all servers"); + } +} diff --git a/rpc-core/src/test/java/com/xiaoyu/rpc/core/protocol/ProtocolTest.java b/rpc-core/src/test/java/com/xiaoyu/rpc/core/protocol/ProtocolTest.java new file mode 100644 index 0000000..80c1768 --- /dev/null +++ b/rpc-core/src/test/java/com/xiaoyu/rpc/core/protocol/ProtocolTest.java @@ -0,0 +1,75 @@ +package com.xiaoyu.rpc.core.protocol; + +import com.xiaoyu.rpc.core.protocol.http.HttpProtocol; +import com.xiaoyu.rpc.core.protocol.netty.NettyProtocol; +import com.xiaoyu.rpc.core.protocol.http2.Http2Protocol; +import com.xiaoyu.rpc.core.protocol.grpc.GrpcProtocol; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * 协议 SPI 单元测试 + */ +@DisplayName("Protocol 协议测试") +public class ProtocolTest { + + @Test + @DisplayName("测试 Netty 协议加载") + void testNettyProtocolLoading() { + Protocol protocol = ProtocolFactory.getProtocol("netty"); + + assertNotNull(protocol, "Netty protocol should be loaded"); + assertTrue(protocol instanceof NettyProtocol, "Should be instance of NettyProtocol"); + } + + @Test + @DisplayName("测试 HTTP 协议加载") + void testHttpProtocolLoading() { + Protocol protocol = ProtocolFactory.getProtocol("http"); + + assertNotNull(protocol, "HTTP protocol should be loaded"); + assertTrue(protocol instanceof HttpProtocol, "Should be instance of HttpProtocol"); + } + + @Test + @DisplayName("测试 HTTP2 协议加载") + void testHttp2ProtocolLoading() { + Protocol protocol = ProtocolFactory.getProtocol("http2"); + + assertNotNull(protocol, "HTTP2 protocol should be loaded"); + assertTrue(protocol instanceof Http2Protocol, "Should be instance of Http2Protocol"); + } + + @Test + @DisplayName("测试 gRPC 协议加载") + void testGrpcProtocolLoading() { + Protocol protocol = ProtocolFactory.getProtocol("grpc"); + + assertNotNull(protocol, "gRPC protocol should be loaded"); + assertTrue(protocol instanceof GrpcProtocol, "Should be instance of GrpcProtocol"); + } + + @Test + @DisplayName("测试未知协议回退到 Netty") + void testUnknownProtocolFallback() { + Protocol protocol = ProtocolFactory.getProtocol("unknown_protocol"); + + assertNotNull(protocol, "Unknown protocol should fallback"); + assertTrue(protocol instanceof NettyProtocol, "Should fallback to NettyProtocol"); + } + + @Test + @DisplayName("测试 ProtocolFactory 多次获取相同协议") + void testProtocolCaching() { + Protocol first = ProtocolFactory.getProtocol("netty"); + Protocol second = ProtocolFactory.getProtocol("netty"); + + assertNotNull(first, "First should not be null"); + assertNotNull(second, "Second should not be null"); + // 由于 SPI ExtensionLoader 使用缓存,两次获取应该是同一实例 + assertSame(first, second, "Same protocol should return same instance"); + } +} diff --git a/rpc-core/src/test/java/com/xiaoyu/rpc/core/registry/LocalRegistryTest.java b/rpc-core/src/test/java/com/xiaoyu/rpc/core/registry/LocalRegistryTest.java new file mode 100644 index 0000000..b71f3d0 --- /dev/null +++ b/rpc-core/src/test/java/com/xiaoyu/rpc/core/registry/LocalRegistryTest.java @@ -0,0 +1,83 @@ +package com.xiaoyu.rpc.core.registry; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; + +import java.net.InetSocketAddress; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * 本地注册中心单元测试 + */ +@DisplayName("LocalRegistry 本地注册中心测试") +public class LocalRegistryTest { + + private LocalRegistry registry; + + @BeforeEach + void setUp() { + registry = new LocalRegistry(); + } + + @Test + @DisplayName("测试注册和查找服务") + void testRegisterAndLookupService() { + String serviceName = "com.example.TestService"; + InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8080); + + // 注册服务 + registry.registerService(serviceName, address); + + // 查找服务 + InetSocketAddress result = registry.lookupService(serviceName); + + assertNotNull(result, "Should find registered service"); + assertEquals(address.getHostName(), result.getHostName(), "Host should match"); + assertEquals(address.getPort(), result.getPort(), "Port should match"); + } + + @Test + @DisplayName("测试查找不存在的服务") + void testLookupNonExistentService() { + InetSocketAddress result = registry.lookupService("non.existent.Service"); + assertNull(result, "Should return null for non-existent service"); + } + + @Test + @DisplayName("测试注册多个服务") + void testRegisterMultipleServices() { + String service1 = "com.example.Service1"; + String service2 = "com.example.Service2"; + InetSocketAddress address1 = new InetSocketAddress("127.0.0.1", 8081); + InetSocketAddress address2 = new InetSocketAddress("127.0.0.1", 8082); + + registry.registerService(service1, address1); + registry.registerService(service2, address2); + + InetSocketAddress result1 = registry.lookupService(service1); + InetSocketAddress result2 = registry.lookupService(service2); + + assertNotNull(result1, "Should find first service"); + assertNotNull(result2, "Should find second service"); + assertEquals(8081, result1.getPort(), "First service port should match"); + assertEquals(8082, result2.getPort(), "Second service port should match"); + } + + @Test + @DisplayName("测试覆盖注册服务") + void testOverwriteService() { + String serviceName = "com.example.OverwriteService"; + InetSocketAddress oldAddress = new InetSocketAddress("127.0.0.1", 8080); + InetSocketAddress newAddress = new InetSocketAddress("127.0.0.1", 9090); + + registry.registerService(serviceName, oldAddress); + registry.registerService(serviceName, newAddress); // 覆盖 + + InetSocketAddress result = registry.lookupService(serviceName); + + assertNotNull(result, "Should find service"); + assertEquals(9090, result.getPort(), "Port should be updated to new address"); + } +} diff --git a/rpc-core/src/test/java/com/xiaoyu/rpc/core/serialization/SerializerTest.java b/rpc-core/src/test/java/com/xiaoyu/rpc/core/serialization/SerializerTest.java new file mode 100644 index 0000000..38a3a17 --- /dev/null +++ b/rpc-core/src/test/java/com/xiaoyu/rpc/core/serialization/SerializerTest.java @@ -0,0 +1,185 @@ +package com.xiaoyu.rpc.core.serialization; + +import com.xiaoyu.rpc.common.extension.ExtensionLoader; +import com.xiaoyu.rpc.common.serialization.Serializer; +import com.xiaoyu.rpc.common.serialization.SerializerCode; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.io.Serializable; +import java.util.Objects; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * 序列化器单元测试 + * 测试 Java、Kryo、JSON 序列化器的序列化/反序列化功能 + */ +@DisplayName("Serializer 序列化器测试") +public class SerializerTest { + + /** + * 用于测试的简单可序列化对象 + */ + public static class TestMessage implements Serializable { + private static final long serialVersionUID = 1L; + + private String interfaceName; + private String methodName; + private String[] paramTypes; + + public TestMessage() { + } + + public TestMessage(String interfaceName, String methodName, String[] paramTypes) { + this.interfaceName = interfaceName; + this.methodName = methodName; + this.paramTypes = paramTypes; + } + + public String getInterfaceName() { + return interfaceName; + } + + public void setInterfaceName(String interfaceName) { + this.interfaceName = interfaceName; + } + + public String getMethodName() { + return methodName; + } + + public void setMethodName(String methodName) { + this.methodName = methodName; + } + + public String[] getParamTypes() { + return paramTypes; + } + + public void setParamTypes(String[] paramTypes) { + this.paramTypes = paramTypes; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + TestMessage that = (TestMessage) o; + return Objects.equals(interfaceName, that.interfaceName) && + Objects.equals(methodName, that.methodName); + } + } + + @ParameterizedTest + @ValueSource(strings = { "java", "kryo", "json" }) + @DisplayName("测试序列化和反序列化 TestMessage") + void testSerializeTestMessage(String serializerName) { + Serializer serializer = ExtensionLoader.getExtensionLoader(Serializer.class).getExtension(serializerName); + + // 创建测试消息 + TestMessage message = new TestMessage( + "com.example.HelloService", + "sayHello", + new String[] { "java.lang.String" }); + + // 序列化 + byte[] bytes = serializer.serialize(message); + assertNotNull(bytes, "Serialized bytes should not be null"); + assertTrue(bytes.length > 0, "Serialized bytes should have content"); + + // 反序列化 + TestMessage deserialized = serializer.deserialize(bytes, TestMessage.class); + assertNotNull(deserialized, "Deserialized message should not be null"); + assertEquals(message.getInterfaceName(), deserialized.getInterfaceName(), "Interface name should match"); + assertEquals(message.getMethodName(), deserialized.getMethodName(), "Method name should match"); + } + + @ParameterizedTest + @ValueSource(strings = { "java", "kryo", "json" }) + @DisplayName("测试序列化和反序列化简单字符串") + void testSerializeString(String serializerName) { + Serializer serializer = ExtensionLoader.getExtensionLoader(Serializer.class).getExtension(serializerName); + + String original = "Hello, World!"; + + // 序列化 + byte[] bytes = serializer.serialize(original); + assertNotNull(bytes, "Serialized bytes should not be null"); + + // 反序列化 + String deserialized = serializer.deserialize(bytes, String.class); + assertEquals(original, deserialized, "Deserialized string should match original"); + } + + @Test + @DisplayName("测试 Java 序列化器代码") + void testJavaSerializerCode() { + Serializer serializer = ExtensionLoader.getExtensionLoader(Serializer.class).getExtension("java"); + assertEquals(SerializerCode.JAVA_SERIALIZER, serializer.getCode(), "Java serializer should have code 0x01"); + } + + @Test + @DisplayName("测试 Kryo 序列化器代码") + void testKryoSerializerCode() { + Serializer serializer = ExtensionLoader.getExtensionLoader(Serializer.class).getExtension("kryo"); + assertEquals(SerializerCode.KRYO_SERIALIZER, serializer.getCode(), "Kryo serializer should have code 0x02"); + } + + @Test + @DisplayName("测试 Protobuf 序列化器代码") + void testProtobufSerializerCode() { + Serializer serializer = ExtensionLoader.getExtensionLoader(Serializer.class).getExtension("protobuf"); + assertEquals(SerializerCode.PROTOBUF_SERIALIZER, serializer.getCode(), + "Protobuf serializer should have code 0x03"); + } + + @Test + @DisplayName("测试 JSON 序列化器代码") + void testJsonSerializerCode() { + Serializer serializer = ExtensionLoader.getExtensionLoader(Serializer.class).getExtension("json"); + // JSON serializer uses code 0x04 + assertEquals((byte) 0x04, serializer.getCode(), "JSON serializer should have code 0x04"); + } + + @Test + @DisplayName("测试 SerializerCode 按名称获取序列化器") + void testGetSerializerByName() { + Serializer javaSerializer = SerializerCode.getSerializerByName("java"); + assertNotNull(javaSerializer, "Should get Java serializer by name"); + + Serializer kryoSerializer = SerializerCode.getSerializerByName("kryo"); + assertNotNull(kryoSerializer, "Should get Kryo serializer by name"); + + // 测试大小写不敏感 + Serializer protobufSerializer = SerializerCode.getSerializerByName("PROTOBUF"); + assertNotNull(protobufSerializer, "Should get Protobuf serializer by uppercase name"); + } + + @Test + @DisplayName("测试 SerializerCode 按代码获取序列化器") + void testGetSerializerByCode() { + Serializer javaSerializer = SerializerCode.getSerializerByCode(SerializerCode.JAVA_SERIALIZER); + assertNotNull(javaSerializer, "Should get serializer by code 0x01"); + + Serializer kryoSerializer = SerializerCode.getSerializerByCode(SerializerCode.KRYO_SERIALIZER); + assertNotNull(kryoSerializer, "Should get serializer by code 0x02"); + + Serializer protobufSerializer = SerializerCode.getSerializerByCode(SerializerCode.PROTOBUF_SERIALIZER); + assertNotNull(protobufSerializer, "Should get serializer by code 0x03"); + } + + @Test + @DisplayName("测试空对象序列化") + void testSerializeNull() { + Serializer jsonSerializer = ExtensionLoader.getExtensionLoader(Serializer.class).getExtension("json"); + + byte[] bytes = jsonSerializer.serialize(null); + assertNotNull(bytes, "Serialized null should return empty array"); + } +} diff --git a/rpc-core/src/test/java/com/xiaoyu/rpc/core/test/LoadBalancerTest.java b/rpc-core/src/test/java/com/xiaoyu/rpc/core/test/LoadBalancerTest.java deleted file mode 100644 index c04a4da..0000000 --- a/rpc-core/src/test/java/com/xiaoyu/rpc/core/test/LoadBalancerTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.xiaoyu.rpc.core.test; - -import com.xiaoyu.rpc.common.extension.ExtensionLoader; -import com.xiaoyu.rpc.core.loadbalancer.LoadBalancer; -import java.util.Arrays; -import java.util.List; - -public class LoadBalancerTest { - public static void main(String[] args) { - System.out.println("--- 测试 LoadBalancer SPI ---"); - - // 1. 测试 SPI 机制是否能加载 - LoadBalancer randomLb = ExtensionLoader.getExtensionLoader(LoadBalancer.class).getExtension("random"); - LoadBalancer roundRobinLb = ExtensionLoader.getExtensionLoader(LoadBalancer.class).getExtension("roundrobin"); - - System.out.println("Random LB Loaded: " + (randomLb != null)); - System.out.println("RoundRobin LB Loaded: " + (roundRobinLb != null)); - - List servers = Arrays.asList("127.0.0.1:8080", "127.0.0.1:8081", "127.0.0.1:8082"); - - // 2. 测试 RoundRobin - System.out.println("\n--- 测试 RoundRobin ---"); - for (int i = 0; i < 5; i++) { - System.out.println("Select: " + roundRobinLb.select(servers)); - } - - // 3. 测试 Random - System.out.println("\n--- 测试 Random ---"); - for (int i = 0; i < 5; i++) { - System.out.println("Select: " + randomLb.select(servers)); - } - } -} diff --git a/rpc-core/src/test/java/com/xiaoyu/rpc/core/test/ProtocolSpiTest.java b/rpc-core/src/test/java/com/xiaoyu/rpc/core/test/ProtocolSpiTest.java deleted file mode 100644 index eec203e..0000000 --- a/rpc-core/src/test/java/com/xiaoyu/rpc/core/test/ProtocolSpiTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.xiaoyu.rpc.core.test; - -import com.xiaoyu.rpc.core.protocol.http.HttpProtocol; -import com.xiaoyu.rpc.core.protocol.netty.NettyProtocol; -import com.xiaoyu.rpc.core.protocol.Protocol; -import com.xiaoyu.rpc.core.protocol.ProtocolFactory; - -public class ProtocolSpiTest { - public static void main(String[] args) { - System.out.println("Starting Protocol SPI Test..."); - - // 1. Test loading Netty protocol (dynamic SPI or fallback logic) - Protocol netty = ProtocolFactory.getProtocol("netty"); - System.out.println("Loaded 'netty': " + netty.getClass().getName()); - if (!(netty instanceof NettyProtocol)) { - throw new RuntimeException("Expected NettyProtocol but got " + netty.getClass().getName()); - } - - // 2. Test loading HTTP protocol - Protocol http = ProtocolFactory.getProtocol("http"); - System.out.println("Loaded 'http': " + http.getClass().getName()); - if (!(http instanceof HttpProtocol)) { - throw new RuntimeException("Expected HttpProtocol but got " + http.getClass().getName()); - } - - // 3. Test loading unknown protocol (should fallback to Netty with warning log) - System.out.println("Testing unknown protocol fallback..."); - Protocol unknown = ProtocolFactory.getProtocol("unknown_proto"); - System.out.println("Loaded 'unknown_proto': " + unknown.getClass().getName()); - if (!(unknown instanceof NettyProtocol)) { - throw new RuntimeException( - "Fallback failed: Expected NettyProtocol but got " + unknown.getClass().getName()); - } - - System.out.println("Protocol SPI Test Passed Successfully!"); - } -} diff --git a/rpc-core/src/test/java/com/xiaoyu/rpc/core/test/SpiTest.java b/rpc-core/src/test/java/com/xiaoyu/rpc/core/test/SpiTest.java deleted file mode 100644 index 71f8cf7..0000000 --- a/rpc-core/src/test/java/com/xiaoyu/rpc/core/test/SpiTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.xiaoyu.rpc.core.test; - -import com.xiaoyu.rpc.common.serialization.Serializer; -import com.xiaoyu.rpc.common.serialization.SerializerCode; -import com.xiaoyu.rpc.common.extension.ExtensionLoader; - -public class SpiTest { - public static void main(String[] args) { - System.out.println("Beginning SPI Test..."); - - // 1. Test ExtensionLoader directly - System.out.println("\n--- Testing ExtensionLoader ---"); - ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Serializer.class); - System.out.println("Supported extensions: " + loader.getSupportedExtensions()); - - try { - Serializer javaSerializer = loader.getExtension("java"); - System.out.println("Loaded 'java': " + javaSerializer.getClass().getName()); - - Serializer kryoSerializer = loader.getExtension("kryo"); - System.out.println("Loaded 'kryo': " + kryoSerializer.getClass().getName()); - - Serializer protoSerializer = loader.getExtension("protobuf"); - System.out.println("Loaded 'protobuf': " + protoSerializer.getClass().getName()); - - } catch (Exception e) { - e.printStackTrace(); - } - - // 2. Test SerializerCode (which uses ExtensionLoader internally) - System.out.println("\n--- Testing SerializerCode ---"); - try { - Serializer s1 = SerializerCode.getSerializerByName("java"); - System.out.println("getSerializerByName('java') -> " + s1.getClass().getName() + ", Code: " + s1.getCode()); - - Serializer s2 = SerializerCode.getSerializerByCode((byte) 0x02); // Kryo - System.out.println("getSerializerByCode(0x02) -> " + s2.getClass().getName() + ", Code: " + s2.getCode()); - - Serializer s3 = SerializerCode.getSerializerByName("protobuf"); - System.out.println( - "getSerializerByName('protobuf') -> " + s3.getClass().getName() + ", Code: " + s3.getCode()); - - // Test Case Insensitivity - Serializer s4 = SerializerCode.getSerializerByName("PROTOBUF"); - System.out.println( - "getSerializerByName('PROTOBUF') -> " + s4.getClass().getName() + ", Code: " + s4.getCode()); - - } catch (Exception e) { - e.printStackTrace(); - } - - System.out.println("\nSPI Test Finished."); - } -} From f0da3bfd97153c800de2fc0764f1e72f39eef432 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Fri, 23 Jan 2026 09:30:33 +0800 Subject: [PATCH 06/38] =?UTF-8?q?fix=E4=BA=86common=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E6=B5=8B=E8=AF=95=E9=94=99=E8=AF=AF=E7=9A=84?= =?UTF-8?q?=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/maven.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 9869712..2537f57 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -53,7 +53,7 @@ jobs: - name: Run Unit Tests run: | echo "Running unit tests..." - mvn test -pl rpc-core -am -Dtest="**/extension/*Test,**/serialization/*Test,**/loadbalancer/*Test,**/registry/*Test,**/config/*Test,**/protocol/*Test" + mvn test -pl rpc-core - name: Start RPC Provider (Java Server) run: | From 60621e96fdd6fe02a78ac3f3e567b47e9012eac4 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Fri, 23 Jan 2026 11:00:05 +0800 Subject: [PATCH 07/38] README: add test --- README.md | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index bc9313f..fa753b7 100644 --- a/README.md +++ b/README.md @@ -95,13 +95,21 @@ Execute the `ConsumerApp` in the `rpc-consumer` module to make calls to the prov java -cp rpc-consumer/target/rpc-consumer-1.0-SNAPSHOT.jar:rpc-core/target/rpc-core-1.0-SNAPSHOT.jar:rpc-common/target/rpc-common-1.0-SNAPSHOT.jar:rpc-api/target/rpc-api-1.0-SNAPSHOT.jar:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout -pl rpc-consumer -am) com.xiaoyu.rpc.consumer.ConsumerApp ``` -### 4. Running Integration Tests - -To run the full integration test suite: - -```bash -mvn test -pl rpc-consumer -Dtest=FullIntegrationTest -``` +### 4. Running Tests + + **Unit Tests**: + Run the comprehensive unit test suite covering SPI, serializers, load balancers, and more: + + ```bash + mvn test -pl rpc-core + ``` + + **Integration Tests**: + Run the full integration test suite: + + ```bash + mvn test -pl rpc-consumer -Dtest=FullIntegrationTest + ``` --- From ef42aad07a548dafc0a78c6f9e96a4ac65e853b5 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Fri, 23 Jan 2026 11:10:35 +0800 Subject: [PATCH 08/38] =?UTF-8?q?README=EF=BC=9A=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E7=BB=93=E6=9E=84=EF=BC=8C=E6=9B=B4=E7=BE=8E=E8=A7=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 133 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 88 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index fa753b7..9b84470 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,20 @@ Unlike traditional RPC frameworks that bind tightly to a single protocol or requ 1. **Use standard gRPC clients** (like Python, Go, Node.js) to call your Java services directly. 2. **Retain Java's dynamic flexibility** (Reflection/ByteBuddy) without generating separate `.proto` service stubs for every business class. + +## 🏗️ Project Architecture + +The project is organized into the following modules to ensure separation of concerns and maintainability: + +| Module | Description | +|--------|-------------| +| **`rpc-api`** | Defines service interfaces. Shared between Provider and Consumer. | +| **`rpc-common`** | Common utilities, Value Objects (`RpcRequest`, `RpcResponse`), and Protobuf definitions (`rpc_meta.proto`). | +| **`rpc-core`** | The core framework implementation. Contains Netty networking, Dynamic Proxy, Registry logic, and SPI loader. | +| **`rpc-provider`** | Example provider application that implements and exports services. | +| **`rpc-consumer`** | Example consumer application that imports and invokes services. | +| **`python_client`** | Python client implementation demonstrating cross-language gRPC interoperability. | + ## ✨ Key Features - **🔌 Plugin-based Architecture**: Leverages a custom SPI mechanism for maximum flexibility. @@ -31,37 +45,7 @@ Unlike traditional RPC frameworks that bind tightly to a single protocol or requ --- -## 🔬 Technical Deep Dive: gRPC Protocol Implementation - -The core of XiaoYu RPC's interoperability lies in its custom implementation of the gRPC wire protocol over Netty's HTTP/2 stack. - -![gRPC Data Processing Flow](docs/images/grpc_processing_flow.png) - -### 1. Wire Format (5-Byte Header) - -Every gRPC message is prefixed with a 5-byte header, handled directly in `GrpcServerHandler`: - -- **Compression Flag (1 Byte)**: `0` (Uncompressed) or `1` (Compressed). -- **Message Length (4 Bytes)**: Big-endian integer specifying the length of the following Protobuf payload. -- **Payload**: Standard Protobuf binary data, deserialized via `NativeProtobufSerializer`. - -### 2. Header Alignment - -Strict adherence to gRPC HTTP/2 headers ensures compatibility: - -- **:status**: `200` (HTTP level success) -- **content-type**: `application/grpc` (Crucial for client recognition) -- **te**: `trailers` - -### 3. Trailer & Status - -gRPC uses HTTP/2 Trailers to convey the final RPC status, distinct from the HTTP status code. - -- **HEADERS Frame (EndStream=true)**: Sent after the data payload. -- **grpc-status**: `0` for OK, non-zero for errors. -- **grpc-message**: Descriptive error message. - -## 🚀 Quick Start +## Quick Start ### 1. Prerequisites (Nacos) @@ -96,20 +80,20 @@ java -cp rpc-consumer/target/rpc-consumer-1.0-SNAPSHOT.jar:rpc-core/target/rpc-c ``` ### 4. Running Tests - - **Unit Tests**: - Run the comprehensive unit test suite covering SPI, serializers, load balancers, and more: - - ```bash - mvn test -pl rpc-core - ``` - - **Integration Tests**: - Run the full integration test suite: - - ```bash - mvn test -pl rpc-consumer -Dtest=FullIntegrationTest - ``` + +**Unit Tests**: +Run the comprehensive unit test suite covering SPI, serializers, load balancers, and more: + +```bash +mvn test -pl rpc-core +``` + +**Integration Tests**: +Run the full integration test suite: + +```bash +mvn test -pl rpc-consumer -Dtest=FullIntegrationTest +``` --- @@ -129,6 +113,33 @@ rpc: load-balancer: roundrobin # Load Balancer: roundrobin, random ``` +## 🔌 Extension Guide (SPI) + +XiaoYu RPC supports a powerful SPI (Service Provider Interface) mechanism, similar to Dubbo, allowing you to easily extend core functionality without modifying the source code. + +### Supported Extension Points + +- `com.xiaoyu.rpc.common.serialization.Serializer` +- `com.xiaoyu.rpc.core.loadbalancer.LoadBalancer` +- `com.xiaoyu.rpc.core.registry.ServiceRegistry` +- `com.xiaoyu.rpc.core.registry.ServiceDiscovery` + +### How to Add a New Extension + +1. **Implement the Interface**: Create a class that implements the target SPI interface (e.g., `Serializer`). +2. **Create SPI Configuration File**: + - Create a file in `src/main/resources/META-INF/rpc/` + - Filename must match the fully qualified interface name (e.g., `com.xiaoyu.rpc.common.serialization.Serializer`). +3. **Register the Implementation**: Add a key-value pair to the file: + ```properties + my-serializer=com.example.MyCustomSerializer + ``` +4. **Use It**: update `rpc-config.yaml`: + ```yaml + rpc: + serializer: my-serializer + ``` + ## ❓ FAQ **Q: Why ByteBuddy?** @@ -206,6 +217,38 @@ To ensure system reliability and code quality, this project integrates a robust ![CI/CD Workflow Result](docs/images/image.png) +--- + +## 🔬 Technical Deep Dive: gRPC Protocol Implementation + +The core of XiaoYu RPC's interoperability lies in its custom implementation of the gRPC wire protocol over Netty's HTTP/2 stack. + +![gRPC Data Processing Flow](docs/images/grpc_processing_flow.png) + +### 1. Wire Format (5-Byte Header) + +Every gRPC message is prefixed with a 5-byte header, handled directly in `GrpcServerHandler`: + +- **Compression Flag (1 Byte)**: `0` (Uncompressed) or `1` (Compressed). +- **Message Length (4 Bytes)**: Big-endian integer specifying the length of the following Protobuf payload. +- **Payload**: Standard Protobuf binary data, deserialized via `NativeProtobufSerializer`. + +### 2. Header Alignment + +Strict adherence to gRPC HTTP/2 headers ensures compatibility: + +- **:status**: `200` (HTTP level success) +- **content-type**: `application/grpc` (Crucial for client recognition) +- **te**: `trailers` + +### 3. Trailer & Status + +gRPC uses HTTP/2 Trailers to convey the final RPC status, distinct from the HTTP status code. + +- **HEADERS Frame (EndStream=true)**: Sent after the data payload. +- **grpc-status**: `0` for OK, non-zero for errors. +- **grpc-message**: Descriptive error message. + --- ## 🤝 Contributing From 8834cf21c35349cffa0c94f5c73474ce2911398e Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Fri, 23 Jan 2026 12:40:44 +0800 Subject: [PATCH 09/38] =?UTF-8?q?=E8=A7=A3=E8=80=A6=E4=BC=A0=E8=BE=93?= =?UTF-8?q?=E5=B1=82=EF=BC=8C=E5=87=8F=E5=B0=91=E5=AF=B9Netty=E7=9A=84?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=EF=BC=8C=E4=BD=BF=E7=94=A8SPI=E5=B0=81?= =?UTF-8?q?=E8=A3=85=EF=BC=8C=E4=BB=85=E9=9C=80=E8=A6=81=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?Transport,=20TransportServer,=20TransportClient=20=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=8D=B3=E5=8F=AF=E6=9B=B4=E6=8D=A2(=E4=BD=86?= =?UTF-8?q?=E6=98=AF=E8=BF=98=E6=B2=A1=E6=9C=89=E5=AE=8C=E5=85=A8=E8=A7=A3?= =?UTF-8?q?=E8=80=A6=E5=88=B0=E4=B8=80=E4=B8=AA=E6=A8=A1=E5=9D=97=E9=87=8C?= =?UTF-8?q?=E9=9D=A2=E5=8E=BB)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/xiaoyu/rpc/core/client/RpcClient.java | 88 +++++++------------ .../com/xiaoyu/rpc/core/config/RpcConfig.java | 20 ++++- .../com/xiaoyu/rpc/core/server/RpcServer.java | 54 ++++-------- .../xiaoyu/rpc/core/transport/Transport.java | 26 ++++++ .../rpc/core/transport/TransportClient.java | 20 +++++ .../rpc/core/transport/TransportServer.java | 19 ++++ .../core/transport/netty/NettyTransport.java | 21 +++++ .../transport/netty/NettyTransportClient.java | 87 ++++++++++++++++++ .../transport/netty/NettyTransportServer.java | 58 ++++++++++++ .../com.xiaoyu.rpc.core.transport.Transport | 1 + 10 files changed, 297 insertions(+), 97 deletions(-) create mode 100644 rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/Transport.java create mode 100644 rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/TransportClient.java create mode 100644 rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/TransportServer.java create mode 100644 rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransport.java create mode 100644 rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java create mode 100644 rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportServer.java create mode 100644 rpc-core/src/main/resources/META-INF/rpc/com.xiaoyu.rpc.core.transport.Transport diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/RpcClient.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/RpcClient.java index 30c9fa8..61eba07 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/RpcClient.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/RpcClient.java @@ -1,88 +1,62 @@ package com.xiaoyu.rpc.core.client; -import com.xiaoyu.rpc.common.serialization.Serializer; -import com.xiaoyu.rpc.common.serialization.SerializerCode; -import com.xiaoyu.rpc.common.vo.RpcRequest; -import com.xiaoyu.rpc.common.vo.RpcResponse; import com.xiaoyu.rpc.common.extension.ExtensionLoader; +import com.xiaoyu.rpc.common.vo.RpcRequest; import com.xiaoyu.rpc.core.config.RpcConfig; -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import com.xiaoyu.rpc.core.protocol.Protocol; -import com.xiaoyu.rpc.core.protocol.ProtocolFactory; import com.xiaoyu.rpc.core.registry.ServiceDiscovery; +import com.xiaoyu.rpc.core.transport.Transport; +import com.xiaoyu.rpc.core.transport.TransportClient; import java.net.InetSocketAddress; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; public class RpcClient { - private static final EventLoopGroup eventLoopGroup; - private static final Bootstrap bootstrap; + private final TransportClient transportClient; + private final ServiceDiscovery serviceDiscovery; + + public RpcClient() { + RpcConfig config = RpcConfig.getInstance(); + // 初始化服务发现 + this.serviceDiscovery = ExtensionLoader.getExtensionLoader(ServiceDiscovery.class) + .getExtension(config.getRegistryType()); - static { - eventLoopGroup = new NioEventLoopGroup(); - bootstrap = new Bootstrap(); - bootstrap.group(eventLoopGroup) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) { - String protocolName = RpcConfig.getInstance().getProtocol(); - Protocol protocol = ProtocolFactory.getProtocol(protocolName); - protocol.config(ch.pipeline(), false, null); - } - }); + // 初始化传输层客户端 + Transport transport = ExtensionLoader.getExtensionLoader(Transport.class).getExtension(config.getTransport()); + this.transportClient = transport.createClient(); } public Object sendRequest(RpcRequest request, Class returnType) { - String protocolName = RpcConfig.getInstance().getProtocol(); - NettyRpcClientHandler clientHandler = new NettyRpcClientHandler(); - try { - ServiceDiscovery serviceDiscovery = ExtensionLoader.getExtensionLoader(ServiceDiscovery.class) - .getExtension(RpcConfig.getInstance().getRegistryType()); + // 1. 服务发现 InetSocketAddress address = serviceDiscovery.lookupService(request.getInterfaceName()); if (address == null) { throw new RuntimeException("未发现服务: " + request.getInterfaceName()); } - // 使用 ChannelProvider 获取连接 - Channel channel = ChannelProvider.get(address, bootstrap); - if (channel == null || !channel.isActive()) { - throw new RuntimeException("无法连接到服务器: " + address); - } + // 2. 使用传输层发送请求 + // 注意: TransportClient 返回的是反序列化后的结果(RpcResponse)或者已经提取的数据 + // 在 NettyTransportClient 实现中,我们返回了 RpcResponse 对象 + Object result = transportClient.sendRequest(request, address); - CompletableFuture resultFuture = new CompletableFuture<>(); - clientHandler.setFuture(resultFuture); + // 3. 处理结果 (这一步逻辑如果 NettyTransportClient 已经做了反序列化,这里可能有点冗余,但保持检查是好的) + if (result instanceof com.xiaoyu.rpc.common.vo.RpcResponse) { + com.xiaoyu.rpc.common.vo.RpcResponse response = (com.xiaoyu.rpc.common.vo.RpcResponse) result; - Protocol protocol = ProtocolFactory.getProtocol(protocolName); - protocol.sendRequest(channel, request, clientHandler); + // 数据已经在 TransportClient 中反序列化了吗? + // 查看 NettyTransportClient 代码: + // return rpcResponse; -> 它并没有反序列化 data 字段成 returnType + // 所以这里需要反序列化 - Object result = resultFuture.get(5, TimeUnit.SECONDS); + byte[] data = response.getData().toByteArray(); - if (result instanceof RpcResponse) { - RpcResponse rpcResponse = (RpcResponse) result; - - if (!"Success".equals(rpcResponse.getMessage())) { - throw new RuntimeException("服务端报错: " + rpcResponse.getMessage()); - } - - byte[] data = rpcResponse.getData().toByteArray(); - - Serializer serializer = SerializerCode.getSerializerByCode(RpcConfig.getInstance().getSerializerCode()); + com.xiaoyu.rpc.common.serialization.Serializer serializer = com.xiaoyu.rpc.common.serialization.SerializerCode + .getSerializerByCode(RpcConfig.getInstance().getSerializerCode()); return serializer.deserialize(data, returnType); - } else { - throw new RuntimeException("服务端返回的不是 RpcResponse 类型"); } + throw new RuntimeException("Unexpected response type: " + result.getClass()); + } catch (Exception e) { throw new RuntimeException("RPC请求发送失败", e); } diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/config/RpcConfig.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/config/RpcConfig.java index b81bd30..bea6939 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/config/RpcConfig.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/config/RpcConfig.java @@ -32,6 +32,8 @@ public class RpcConfig { private String proxyType = "jdk"; // 负载均衡器 private String loadBalancer = "roundrobin"; + // 传输层类型 + private String transport = "netty"; private RpcConfig() { loadConfig(); @@ -71,9 +73,11 @@ private void loadConfig() { this.registryType = (String) rpcConfig.getOrDefault("registry", "nacos"); this.proxyType = (String) rpcConfig.getOrDefault("proxy", "jdk"); this.loadBalancer = (String) rpcConfig.getOrDefault("load-balancer", "roundrobin"); + this.transport = (String) rpcConfig.getOrDefault("transport", "netty"); - log.info("配置加载成功: 序列化方式={}, 服务器={}:{},使用的协议={}, 注册中心={}, 代理方式={}, 负载均衡={}", - serializerType, serverHost, serverPort, protocol, registryAddress, proxyType, loadBalancer); + log.info("配置加载成功: 序列化方式={}, 服务器={}:{},使用的协议={}, 注册中心={}, 代理方式={}, 负载均衡={}, 传输层={}", + serializerType, serverHost, serverPort, protocol, registryAddress, proxyType, loadBalancer, + transport); } else { log.warn("配置文件格式错误,使用默认配置"); setDefaultConfig(); @@ -105,6 +109,12 @@ private void loadConfig() { this.serializerType = serializerStr; log.info("检测到 System Property 覆盖序列化方式: {}", this.serializerType); } + + String transportStr = System.getProperty("rpc.transport"); + if (transportStr != null) { + this.transport = transportStr; + log.info("检测到 System Property 覆盖传输层: {}", this.transport); + } } /** @@ -115,6 +125,7 @@ private void setDefaultConfig() { this.serverPort = 8080; this.serverHost = "127.0.0.1"; this.protocol = "netty"; + this.transport = "netty"; } /** @@ -158,6 +169,10 @@ public String getLoadBalancer() { return loadBalancer; } + public String getTransport() { + return transport; + } + @Override public String toString() { return "RpcConfig{" + @@ -169,6 +184,7 @@ public String toString() { ", registryType='" + registryType + '\'' + ", proxyType='" + proxyType + '\'' + ", loadBalancer='" + loadBalancer + '\'' + + ", transport='" + transport + '\'' + '}'; } } diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/RpcServer.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/RpcServer.java index 8c83e7d..f9f79de 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/RpcServer.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/RpcServer.java @@ -2,43 +2,41 @@ import com.xiaoyu.rpc.core.config.RpcConfig; import com.xiaoyu.rpc.common.extension.ExtensionLoader; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import lombok.extern.slf4j.Slf4j; -import com.xiaoyu.rpc.core.protocol.Protocol; -import com.xiaoyu.rpc.core.protocol.ProtocolFactory; import com.xiaoyu.rpc.core.registry.ServiceRegistry; -import com.xiaoyu.rpc.common.vo.RpcRequest; +import com.xiaoyu.rpc.core.transport.Transport; +import com.xiaoyu.rpc.core.transport.TransportServer; +import lombok.extern.slf4j.Slf4j; import java.net.InetSocketAddress; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - +@Slf4j public class RpcServer { - private static final Logger log = LoggerFactory.getLogger(RpcServer.class); private final String serverHost; private final int serverPort; - private final String protocolName; private final ServiceRegistry serviceRegistry; + private final TransportServer transportServer; public RpcServer() { RpcConfig config = RpcConfig.getInstance(); this.serverHost = config.getServerHost(); this.serverPort = config.getServerPort(); - this.protocolName = config.getProtocol(); this.serviceRegistry = ExtensionLoader.getExtensionLoader(ServiceRegistry.class) .getExtension(config.getRegistryType()); + + // 获取传输层实现 + Transport transport = ExtensionLoader.getExtensionLoader(Transport.class).getExtension(config.getTransport()); + this.transportServer = transport.createServer(this.serverPort); } public void register(Class interfaceClass, T serviceImpl) { String serviceName = interfaceClass.getName(); - // 1. 本地注册 (Netty Handler) + // 1. 本地注册 (仍然使用 NettyRpcHandler 的静态方法? 这里需要注意) + // 为了兼容现有代码,NettyRpcHandler 仍然作为 Handler,但它的 handlerMap 是静态的 + // 理想情况下应该把 Handler 变成非静态的并传给 TransportServer + // 但目前 NettyTransportServer 内部硬编码了 new NettyRpcHandler(),而 NettyRpcHandler 使用静态 + // map + // 所以这里依然有效。后续应该优化 NettyRpcHandler 的状态管理。 NettyRpcHandler.registerService(serviceName, serviceImpl); // 2. 远程注册 (Nacos / Local) @@ -51,26 +49,6 @@ public void register(Class interfaceClass, T serviceImpl) { } public void start() throws InterruptedException { - EventLoopGroup bossGroup = new NioEventLoopGroup(); - EventLoopGroup workerGroup = new NioEventLoopGroup(); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .childHandler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) { - Protocol protocol = ProtocolFactory.getProtocol(protocolName); - protocol.config(ch.pipeline(), true, new NettyRpcHandler()); - } - }); - - log.info("RPC Server started on port {}...", serverPort); - b.bind(serverPort).sync().channel().closeFuture().sync(); - } finally { - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } + transportServer.start(); } - } \ No newline at end of file diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/Transport.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/Transport.java new file mode 100644 index 0000000..7b158b2 --- /dev/null +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/Transport.java @@ -0,0 +1,26 @@ +package com.xiaoyu.rpc.core.transport; + +import com.xiaoyu.rpc.common.extension.SPI; + +/** + * 传输层 SPI 接口 + * 用于屏蔽底层网络通信框架 (Netty, Mina, etc.) + */ +@SPI("netty") +public interface Transport { + + /** + * 创建服务端 + * + * @param port 监听端口 + * @return 服务端实例 + */ + TransportServer createServer(int port); + + /** + * 创建客户端 + * + * @return 客户端实例 + */ + TransportClient createClient(); +} diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/TransportClient.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/TransportClient.java new file mode 100644 index 0000000..2067c81 --- /dev/null +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/TransportClient.java @@ -0,0 +1,20 @@ +package com.xiaoyu.rpc.core.transport; + +import com.xiaoyu.rpc.common.vo.RpcRequest; +import java.net.InetSocketAddress; +import java.util.concurrent.CompletableFuture; + +/** + * 传输层客户端接口 + */ +public interface TransportClient { + + /** + * 发送 RPC 请求 + * + * @param request 请求对象 + * @param address 目标地址 + * @return 响应结果 (CompletableFuture 或 直接结果) + */ + Object sendRequest(RpcRequest request, InetSocketAddress address); +} diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/TransportServer.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/TransportServer.java new file mode 100644 index 0000000..80b04f2 --- /dev/null +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/TransportServer.java @@ -0,0 +1,19 @@ +package com.xiaoyu.rpc.core.transport; + +/** + * 传输层服务端接口 + */ +public interface TransportServer { + + /** + * 启动服务 + * + * @throws InterruptedException 如果启动过程被中断 + */ + void start() throws InterruptedException; + + /** + * 停止服务 + */ + void stop(); +} diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransport.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransport.java new file mode 100644 index 0000000..9af75bb --- /dev/null +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransport.java @@ -0,0 +1,21 @@ +package com.xiaoyu.rpc.core.transport.netty; + +import com.xiaoyu.rpc.core.transport.Transport; +import com.xiaoyu.rpc.core.transport.TransportClient; +import com.xiaoyu.rpc.core.transport.TransportServer; + +/** + * Netty 传输层实现 + */ +public class NettyTransport implements Transport { + + @Override + public TransportServer createServer(int port) { + return new NettyTransportServer(port); + } + + @Override + public TransportClient createClient() { + return new NettyTransportClient(); + } +} diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java new file mode 100644 index 0000000..7add58f --- /dev/null +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java @@ -0,0 +1,87 @@ +package com.xiaoyu.rpc.core.transport.netty; + +import com.xiaoyu.rpc.common.serialization.Serializer; +import com.xiaoyu.rpc.common.serialization.SerializerCode; +import com.xiaoyu.rpc.common.vo.RpcRequest; +import com.xiaoyu.rpc.common.vo.RpcResponse; +import com.xiaoyu.rpc.core.client.ChannelProvider; +import com.xiaoyu.rpc.core.client.NettyRpcClientHandler; +import com.xiaoyu.rpc.core.config.RpcConfig; +import com.xiaoyu.rpc.core.protocol.Protocol; +import com.xiaoyu.rpc.core.protocol.ProtocolFactory; +import com.xiaoyu.rpc.core.transport.TransportClient; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import lombok.extern.slf4j.Slf4j; + +import java.net.InetSocketAddress; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +@Slf4j +public class NettyTransportClient implements TransportClient { + + private static final EventLoopGroup eventLoopGroup; + private static final Bootstrap bootstrap; + + static { + eventLoopGroup = new NioEventLoopGroup(); + bootstrap = new Bootstrap(); + bootstrap.group(eventLoopGroup) + .channel(NioSocketChannel.class) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel ch) { + String protocolName = RpcConfig.getInstance().getProtocol(); + Protocol protocol = ProtocolFactory.getProtocol(protocolName); + protocol.config(ch.pipeline(), false, null); + } + }); + } + + @Override + public Object sendRequest(RpcRequest request, InetSocketAddress address) { + NettyRpcClientHandler clientHandler = new NettyRpcClientHandler(); + String protocolName = RpcConfig.getInstance().getProtocol(); + + try { + // 使用 ChannelProvider 获取连接 + Channel channel = ChannelProvider.get(address, bootstrap); + if (channel == null || !channel.isActive()) { + throw new RuntimeException("无法连接到服务器: " + address); + } + + CompletableFuture resultFuture = new CompletableFuture<>(); + clientHandler.setFuture(resultFuture); + + Protocol protocol = ProtocolFactory.getProtocol(protocolName); + protocol.sendRequest(channel, request, clientHandler); + + Object result = resultFuture.get(5, TimeUnit.SECONDS); + + if (result instanceof RpcResponse) { + RpcResponse rpcResponse = (RpcResponse) result; + + if (!"Success".equals(rpcResponse.getMessage())) { + throw new RuntimeException("服务端报错: " + rpcResponse.getMessage()); + } + + // 返回 Data 的 bytes,由上层可以通过配置的 Serializer 进行反序列化 + // 这里为了保持兼容性,还是直接返回 Response 对象或者 Data? + // 根据 RpcClient 的逻辑,它是在这里反序列化的。 + // 此时我们其实只负责传输,最好返回 RpcResponse 对象。 + return rpcResponse; + + } else { + throw new RuntimeException("服务端返回的不是 RpcResponse 类型"); + } + } catch (Exception e) { + throw new RuntimeException("RPC请求发送失败", e); + } + } +} diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportServer.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportServer.java new file mode 100644 index 0000000..29b5caa --- /dev/null +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportServer.java @@ -0,0 +1,58 @@ +package com.xiaoyu.rpc.core.transport.netty; + +import com.xiaoyu.rpc.core.config.RpcConfig; +import com.xiaoyu.rpc.core.protocol.Protocol; +import com.xiaoyu.rpc.core.protocol.ProtocolFactory; +import com.xiaoyu.rpc.core.server.NettyRpcHandler; +import com.xiaoyu.rpc.core.transport.TransportServer; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class NettyTransportServer implements TransportServer { + + private final int port; + private EventLoopGroup bossGroup; + private EventLoopGroup workerGroup; + + public NettyTransportServer(int port) { + this.port = port; + } + + @Override + public void start() throws InterruptedException { + bossGroup = new NioEventLoopGroup(); + workerGroup = new NioEventLoopGroup(); + try { + ServerBootstrap b = new ServerBootstrap(); + b.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .childHandler(new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel ch) { + String protocolName = RpcConfig.getInstance().getProtocol(); + Protocol protocol = ProtocolFactory.getProtocol(protocolName); + protocol.config(ch.pipeline(), true, new NettyRpcHandler()); + } + }); + + log.info("RPC Server (Netty) started on port {}...", port); + b.bind(port).sync().channel().closeFuture().sync(); + } finally { + stop(); + } + } + + @Override + public void stop() { + if (bossGroup != null) + bossGroup.shutdownGracefully(); + if (workerGroup != null) + workerGroup.shutdownGracefully(); + } +} diff --git a/rpc-core/src/main/resources/META-INF/rpc/com.xiaoyu.rpc.core.transport.Transport b/rpc-core/src/main/resources/META-INF/rpc/com.xiaoyu.rpc.core.transport.Transport new file mode 100644 index 0000000..b008ffa --- /dev/null +++ b/rpc-core/src/main/resources/META-INF/rpc/com.xiaoyu.rpc.core.transport.Transport @@ -0,0 +1 @@ +netty=com.xiaoyu.rpc.core.transport.netty.NettyTransport From 21de409bd0d9191eea1ad512e56355d8bcbaddf4 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Fri, 23 Jan 2026 12:53:01 +0800 Subject: [PATCH 10/38] =?UTF-8?q?=E5=AE=8C=E5=85=A8=E8=A7=A3=E8=80=A6?= =?UTF-8?q?=E4=BC=A0=E8=BE=93=E5=B1=82=EF=BC=8C=E5=B0=86core=E5=AE=8C?= =?UTF-8?q?=E5=85=A8=E5=89=94=E9=99=A4=E5=92=8CNetty=E7=9A=84=E5=BC=BA?= =?UTF-8?q?=E7=BB=91=E5=AE=9A=E5=85=B3=E7=B3=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 1 + rpc-consumer/pom.xml | 5 ++ .../rpc/consumer/FullIntegrationTest.java | 6 +- rpc-core/pom.xml | 36 +++++------ .../com/xiaoyu/rpc/core/server/RpcServer.java | 9 +-- .../rpc/core/server/ServiceRepository.java | 22 +++++++ .../core/extension/ExtensionLoaderTest.java | 16 +---- rpc-provider/pom.xml | 5 ++ rpc-transport-netty/pom.xml | 62 +++++++++++++++++++ .../rpc/core/client/ChannelProvider.java | 0 .../core/client/NettyRpcClientHandler.java | 0 .../xiaoyu/rpc/core/protocol/Protocol.java | 0 .../rpc/core/protocol/ProtocolFactory.java | 0 .../rpc/core/protocol/grpc/GrpcProtocol.java | 0 .../core/protocol/grpc/GrpcServerHandler.java | 0 .../rpc/core/protocol/http/HttpProtocol.java | 0 .../core/protocol/http/HttpRpcDecoder.java | 0 .../core/protocol/http/HttpRpcEncoder.java | 0 .../core/protocol/http2/Http2Protocol.java | 0 .../rpc/core/protocol/netty/MyRpcDecoder.java | 0 .../rpc/core/protocol/netty/MyRpcEncoder.java | 0 .../core/protocol/netty/NettyProtocol.java | 0 .../rpc/core/server/NettyRpcHandler.java | 11 +--- .../core/transport/netty/NettyTransport.java | 0 .../transport/netty/NettyTransportClient.java | 0 .../transport/netty/NettyTransportServer.java | 0 .../rpc/com.xiaoyu.rpc.core.protocol.Protocol | 0 .../com.xiaoyu.rpc.core.transport.Transport | 0 .../rpc/core/protocol/ProtocolTest.java | 0 29 files changed, 119 insertions(+), 54 deletions(-) create mode 100644 rpc-core/src/main/java/com/xiaoyu/rpc/core/server/ServiceRepository.java create mode 100644 rpc-transport-netty/pom.xml rename {rpc-core => rpc-transport-netty}/src/main/java/com/xiaoyu/rpc/core/client/ChannelProvider.java (100%) rename {rpc-core => rpc-transport-netty}/src/main/java/com/xiaoyu/rpc/core/client/NettyRpcClientHandler.java (100%) rename {rpc-core => rpc-transport-netty}/src/main/java/com/xiaoyu/rpc/core/protocol/Protocol.java (100%) rename {rpc-core => rpc-transport-netty}/src/main/java/com/xiaoyu/rpc/core/protocol/ProtocolFactory.java (100%) rename {rpc-core => rpc-transport-netty}/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcProtocol.java (100%) rename {rpc-core => rpc-transport-netty}/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcServerHandler.java (100%) rename {rpc-core => rpc-transport-netty}/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpProtocol.java (100%) rename {rpc-core => rpc-transport-netty}/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpRpcDecoder.java (100%) rename {rpc-core => rpc-transport-netty}/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpRpcEncoder.java (100%) rename {rpc-core => rpc-transport-netty}/src/main/java/com/xiaoyu/rpc/core/protocol/http2/Http2Protocol.java (100%) rename {rpc-core => rpc-transport-netty}/src/main/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcDecoder.java (100%) rename {rpc-core => rpc-transport-netty}/src/main/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcEncoder.java (100%) rename {rpc-core => rpc-transport-netty}/src/main/java/com/xiaoyu/rpc/core/protocol/netty/NettyProtocol.java (100%) rename {rpc-core => rpc-transport-netty}/src/main/java/com/xiaoyu/rpc/core/server/NettyRpcHandler.java (90%) rename {rpc-core => rpc-transport-netty}/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransport.java (100%) rename {rpc-core => rpc-transport-netty}/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java (100%) rename {rpc-core => rpc-transport-netty}/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportServer.java (100%) rename {rpc-core => rpc-transport-netty}/src/main/resources/META-INF/rpc/com.xiaoyu.rpc.core.protocol.Protocol (100%) rename {rpc-core => rpc-transport-netty}/src/main/resources/META-INF/rpc/com.xiaoyu.rpc.core.transport.Transport (100%) rename {rpc-core => rpc-transport-netty}/src/test/java/com/xiaoyu/rpc/core/protocol/ProtocolTest.java (100%) diff --git a/pom.xml b/pom.xml index 8c5bb09..7055b1a 100644 --- a/pom.xml +++ b/pom.xml @@ -13,6 +13,7 @@ rpc-common rpc-api rpc-core + rpc-transport-netty rpc-provider rpc-consumer diff --git a/rpc-consumer/pom.xml b/rpc-consumer/pom.xml index 4469038..dbb4e79 100644 --- a/rpc-consumer/pom.xml +++ b/rpc-consumer/pom.xml @@ -20,6 +20,11 @@ com.xiaoyu.rpc rpc-core + + com.xiaoyu.rpc + rpc-transport-netty + ${project.version} + org.junit.jupiter junit-jupiter diff --git a/rpc-consumer/src/test/java/com/xiaoyu/rpc/consumer/FullIntegrationTest.java b/rpc-consumer/src/test/java/com/xiaoyu/rpc/consumer/FullIntegrationTest.java index 796c41e..dc573a6 100644 --- a/rpc-consumer/src/test/java/com/xiaoyu/rpc/consumer/FullIntegrationTest.java +++ b/rpc-consumer/src/test/java/com/xiaoyu/rpc/consumer/FullIntegrationTest.java @@ -20,9 +20,9 @@ public String sayHello(String name) { public void testFullIntegration() throws InterruptedException { // Use Local Registry to avoid external dependency System.setProperty("rpc.registry", "local"); + System.setProperty("rpc.server-port", "9090"); // Use port 9090 // Ensure we use KRYO or JSON/Hessian serializer that supports mundane Java - // classes (String) - // because Protobuf serializer requires Protobuf generated classes. + // classes System.setProperty("rpc.serializer", "kryo"); // Start Server in a thread @@ -39,7 +39,7 @@ public void testFullIntegration() throws InterruptedException { serverThread.setDaemon(true); serverThread.start(); - Thread.sleep(2000); // Wait for server start + Thread.sleep(5000); // Wait for server start (increased) try { System.out.println("Starting Client..."); diff --git a/rpc-core/pom.xml b/rpc-core/pom.xml index d3bd67e..ab080e3 100644 --- a/rpc-core/pom.xml +++ b/rpc-core/pom.xml @@ -17,10 +17,8 @@ rpc-common - - io.netty - netty-all - + + org.projectlombok lombok @@ -29,6 +27,8 @@ ch.qos.logback logback-classic + + com.esotericsoftware kryo @@ -41,31 +41,25 @@ org.yaml snakeyaml + + com.google.code.gson + gson + 2.10.1 + + + com.alibaba.nacos nacos-client + + net.bytebuddy byte-buddy - - io.grpc - grpc-netty-shaded - - - io.grpc - grpc-protobuf - - - io.grpc - grpc-stub - - - com.google.code.gson - gson - 2.10.1 - + + org.junit.jupiter junit-jupiter diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/RpcServer.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/RpcServer.java index f9f79de..9e0f033 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/RpcServer.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/RpcServer.java @@ -31,13 +31,8 @@ public RpcServer() { public void register(Class interfaceClass, T serviceImpl) { String serviceName = interfaceClass.getName(); - // 1. 本地注册 (仍然使用 NettyRpcHandler 的静态方法? 这里需要注意) - // 为了兼容现有代码,NettyRpcHandler 仍然作为 Handler,但它的 handlerMap 是静态的 - // 理想情况下应该把 Handler 变成非静态的并传给 TransportServer - // 但目前 NettyTransportServer 内部硬编码了 new NettyRpcHandler(),而 NettyRpcHandler 使用静态 - // map - // 所以这里依然有效。后续应该优化 NettyRpcHandler 的状态管理。 - NettyRpcHandler.registerService(serviceName, serviceImpl); + // 1. 本地注册 (使用 ServiceRepository 解耦) + ServiceRepository.registerService(serviceName, serviceImpl); // 2. 远程注册 (Nacos / Local) try { diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/ServiceRepository.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/ServiceRepository.java new file mode 100644 index 0000000..77360c4 --- /dev/null +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/ServiceRepository.java @@ -0,0 +1,22 @@ +package com.xiaoyu.rpc.core.server; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 服务注册仓库 + * 用于存放本地已注册的服务实例 + */ +public class ServiceRepository { + + // 缓存服务实例: interfaceName -> serviceBean + private static final Map SERVICE_MAP = new ConcurrentHashMap<>(); + + public static void registerService(String interfaceName, Object serviceBean) { + SERVICE_MAP.put(interfaceName, serviceBean); + } + + public static Object getService(String interfaceName) { + return SERVICE_MAP.get(interfaceName); + } +} diff --git a/rpc-core/src/test/java/com/xiaoyu/rpc/core/extension/ExtensionLoaderTest.java b/rpc-core/src/test/java/com/xiaoyu/rpc/core/extension/ExtensionLoaderTest.java index 3cc0dcb..1a707b7 100644 --- a/rpc-core/src/test/java/com/xiaoyu/rpc/core/extension/ExtensionLoaderTest.java +++ b/rpc-core/src/test/java/com/xiaoyu/rpc/core/extension/ExtensionLoaderTest.java @@ -4,7 +4,7 @@ import com.xiaoyu.rpc.common.serialization.Serializer; import com.xiaoyu.rpc.core.loadbalancer.LoadBalancer; import com.xiaoyu.rpc.core.client.ProxyFactory; -import com.xiaoyu.rpc.core.protocol.Protocol; + import com.xiaoyu.rpc.core.registry.ServiceRegistry; import com.xiaoyu.rpc.core.registry.ServiceDiscovery; @@ -77,20 +77,6 @@ void testLoadBalancerExtensions() { assertEquals(2, extensions.size(), "Should have exactly 2 load balancer extensions"); } - @Test - @DisplayName("测试 Protocol 扩展加载") - void testProtocolExtensions() { - ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Protocol.class); - - assertNotNull(loader.getExtension("netty"), "Netty protocol should be loaded"); - assertNotNull(loader.getExtension("http"), "HTTP protocol should be loaded"); - assertNotNull(loader.getExtension("http2"), "HTTP2 protocol should be loaded"); - assertNotNull(loader.getExtension("grpc"), "gRPC protocol should be loaded"); - - var extensions = loader.getSupportedExtensions(); - assertEquals(4, extensions.size(), "Should have exactly 4 protocol extensions"); - } - @Test @DisplayName("测试 ProxyFactory 扩展加载") void testProxyFactoryExtensions() { diff --git a/rpc-provider/pom.xml b/rpc-provider/pom.xml index eef6629..b4752d2 100644 --- a/rpc-provider/pom.xml +++ b/rpc-provider/pom.xml @@ -20,6 +20,11 @@ com.xiaoyu.rpc rpc-core + + com.xiaoyu.rpc + rpc-transport-netty + ${project.version} + diff --git a/rpc-transport-netty/pom.xml b/rpc-transport-netty/pom.xml new file mode 100644 index 0000000..e9b9d62 --- /dev/null +++ b/rpc-transport-netty/pom.xml @@ -0,0 +1,62 @@ + + + + grpc-demo + com.xiaoyu.rpc + 1.0-SNAPSHOT + + 4.0.0 + + rpc-transport-netty + + + + com.xiaoyu.rpc + rpc-core + ${project.version} + + + io.netty + netty-all + + + org.projectlombok + lombok + provided + + + ch.qos.logback + logback-classic + + + + + io.grpc + grpc-netty-shaded + + + io.grpc + grpc-protobuf + + + io.grpc + grpc-stub + + + + + com.google.protobuf + protobuf-java + + + + + org.junit.jupiter + junit-jupiter + test + + + + diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/ChannelProvider.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/client/ChannelProvider.java similarity index 100% rename from rpc-core/src/main/java/com/xiaoyu/rpc/core/client/ChannelProvider.java rename to rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/client/ChannelProvider.java diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/NettyRpcClientHandler.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/client/NettyRpcClientHandler.java similarity index 100% rename from rpc-core/src/main/java/com/xiaoyu/rpc/core/client/NettyRpcClientHandler.java rename to rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/client/NettyRpcClientHandler.java diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/protocol/Protocol.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/Protocol.java similarity index 100% rename from rpc-core/src/main/java/com/xiaoyu/rpc/core/protocol/Protocol.java rename to rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/Protocol.java diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/protocol/ProtocolFactory.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/ProtocolFactory.java similarity index 100% rename from rpc-core/src/main/java/com/xiaoyu/rpc/core/protocol/ProtocolFactory.java rename to rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/ProtocolFactory.java diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcProtocol.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcProtocol.java similarity index 100% rename from rpc-core/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcProtocol.java rename to rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcProtocol.java diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcServerHandler.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcServerHandler.java similarity index 100% rename from rpc-core/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcServerHandler.java rename to rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcServerHandler.java diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpProtocol.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpProtocol.java similarity index 100% rename from rpc-core/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpProtocol.java rename to rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpProtocol.java diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpRpcDecoder.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpRpcDecoder.java similarity index 100% rename from rpc-core/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpRpcDecoder.java rename to rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpRpcDecoder.java diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpRpcEncoder.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpRpcEncoder.java similarity index 100% rename from rpc-core/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpRpcEncoder.java rename to rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpRpcEncoder.java diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/protocol/http2/Http2Protocol.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http2/Http2Protocol.java similarity index 100% rename from rpc-core/src/main/java/com/xiaoyu/rpc/core/protocol/http2/Http2Protocol.java rename to rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http2/Http2Protocol.java diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcDecoder.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcDecoder.java similarity index 100% rename from rpc-core/src/main/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcDecoder.java rename to rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcDecoder.java diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcEncoder.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcEncoder.java similarity index 100% rename from rpc-core/src/main/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcEncoder.java rename to rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcEncoder.java diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/protocol/netty/NettyProtocol.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/NettyProtocol.java similarity index 100% rename from rpc-core/src/main/java/com/xiaoyu/rpc/core/protocol/netty/NettyProtocol.java rename to rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/NettyProtocol.java diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/NettyRpcHandler.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/server/NettyRpcHandler.java similarity index 90% rename from rpc-core/src/main/java/com/xiaoyu/rpc/core/server/NettyRpcHandler.java rename to rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/server/NettyRpcHandler.java index 58e5528..c2eaea2 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/NettyRpcHandler.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/server/NettyRpcHandler.java @@ -20,20 +20,15 @@ @ChannelHandler.Sharable public class NettyRpcHandler extends SimpleChannelInboundHandler { - // 模拟注册中心 - private static final Map SERVICE_MAP = new ConcurrentHashMap<>(); - - public static void registerService(String interfaceName, Object serviceBean) { - SERVICE_MAP.put(interfaceName, serviceBean); - } + // 移除内部 Map,改用 ServiceRepository @Override protected void channelRead0(ChannelHandlerContext ctx, RpcRequest request) throws Exception { RpcResponse.Builder responseBuilder = RpcResponse.newBuilder(); try { - // 1. 获取实现类 - Object serviceBean = SERVICE_MAP.get(request.getInterfaceName()); + // 1. 获取实现类 (从 ServiceRepository 获取) + Object serviceBean = ServiceRepository.getService(request.getInterfaceName()); if (serviceBean == null) { throw new RuntimeException("未找到服务实现: " + request.getInterfaceName()); } diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransport.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransport.java similarity index 100% rename from rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransport.java rename to rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransport.java diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java similarity index 100% rename from rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java rename to rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportServer.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportServer.java similarity index 100% rename from rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportServer.java rename to rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportServer.java diff --git a/rpc-core/src/main/resources/META-INF/rpc/com.xiaoyu.rpc.core.protocol.Protocol b/rpc-transport-netty/src/main/resources/META-INF/rpc/com.xiaoyu.rpc.core.protocol.Protocol similarity index 100% rename from rpc-core/src/main/resources/META-INF/rpc/com.xiaoyu.rpc.core.protocol.Protocol rename to rpc-transport-netty/src/main/resources/META-INF/rpc/com.xiaoyu.rpc.core.protocol.Protocol diff --git a/rpc-core/src/main/resources/META-INF/rpc/com.xiaoyu.rpc.core.transport.Transport b/rpc-transport-netty/src/main/resources/META-INF/rpc/com.xiaoyu.rpc.core.transport.Transport similarity index 100% rename from rpc-core/src/main/resources/META-INF/rpc/com.xiaoyu.rpc.core.transport.Transport rename to rpc-transport-netty/src/main/resources/META-INF/rpc/com.xiaoyu.rpc.core.transport.Transport diff --git a/rpc-core/src/test/java/com/xiaoyu/rpc/core/protocol/ProtocolTest.java b/rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/protocol/ProtocolTest.java similarity index 100% rename from rpc-core/src/test/java/com/xiaoyu/rpc/core/protocol/ProtocolTest.java rename to rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/protocol/ProtocolTest.java From 93e491d6c3bbaaca584c66eaab149af3ccdf4b05 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Fri, 23 Jan 2026 12:59:03 +0800 Subject: [PATCH 11/38] =?UTF-8?q?README=EF=BC=9A=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=9C=89=E5=85=B3SPI=E7=9A=84=E9=83=A8=E5=88=86=E8=AF=B4?= =?UTF-8?q?=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 9b84470..85b4cf1 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,8 @@ The project is organized into the following modules to ensure separation of conc |--------|-------------| | **`rpc-api`** | Defines service interfaces. Shared between Provider and Consumer. | | **`rpc-common`** | Common utilities, Value Objects (`RpcRequest`, `RpcResponse`), and Protobuf definitions (`rpc_meta.proto`). | -| **`rpc-core`** | The core framework implementation. Contains Netty networking, Dynamic Proxy, Registry logic, and SPI loader. | +| **`rpc-core`** | The core framework implementation. Contains SPI interfaces, Dynamic Proxy, and Registry logic. **Netty-free**. | +| **`rpc-transport-netty`** | The default transport implementation based on **Netty**. | | **`rpc-provider`** | Example provider application that implements and exports services. | | **`rpc-consumer`** | Example consumer application that imports and invokes services. | | **`python_client`** | Python client implementation demonstrating cross-language gRPC interoperability. | @@ -37,6 +38,7 @@ The project is organized into the following modules to ensure separation of conc - **🔌 Plugin-based Architecture**: Leverages a custom SPI mechanism for maximum flexibility. - **🤝 Universal gRPC Compatibility**: A custom-implemented `GrpcProtocol` layer that runs standard gRPC on HTTP/2, proven to interoperate with official `grpc-python` clients. - **⚡ Dynamic-Static Hybrid**: Combines the performance of Protobuf serialization (with custom Type Wrappers) and the flexibility of Java dynamic proxies. +- **🚀 Pluggable Transport**: Fully decoupled transport layer. Default implementation is `rpc-transport-netty`, but can be swapped for Tomcat/Socket. - **📡 Multi-Protocol Support**: Choice of `Netty` (Custom), `HTTP/1.1`, or `gRPC` (HTTP/2) for communication. - **⚡ High-Performance Proxy**: Uses **ByteBuddy** for dynamic proxy generation, optimized for Java 17+. - **⚖️ Intelligent Load Balancing**: Includes `RoundRobin` and `Random` strategies. @@ -68,7 +70,7 @@ Execute the following commands to start the Java RPC Provider. This will build t mvn clean package -DskipTests # 2. Start Provider -java -cp rpc-provider/target/rpc-provider-1.0-SNAPSHOT.jar:rpc-core/target/rpc-core-1.0-SNAPSHOT.jar:rpc-common/target/rpc-common-1.0-SNAPSHOT.jar:rpc-api/target/rpc-api-1.0-SNAPSHOT.jar:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout -pl rpc-provider -am) com.xiaoyu.rpc.provider.ProviderApp +java -cp rpc-provider/target/rpc-provider-1.0-SNAPSHOT.jar:rpc-transport-netty/target/rpc-transport-netty-1.0-SNAPSHOT.jar:rpc-core/target/rpc-core-1.0-SNAPSHOT.jar:rpc-common/target/rpc-common-1.0-SNAPSHOT.jar:rpc-api/target/rpc-api-1.0-SNAPSHOT.jar:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout -pl rpc-provider -am) com.xiaoyu.rpc.provider.ProviderApp ``` ### 3. Run the Consumer @@ -76,16 +78,16 @@ java -cp rpc-provider/target/rpc-provider-1.0-SNAPSHOT.jar:rpc-core/target/rpc-c Execute the `ConsumerApp` in the `rpc-consumer` module to make calls to the provider. ```bash -java -cp rpc-consumer/target/rpc-consumer-1.0-SNAPSHOT.jar:rpc-core/target/rpc-core-1.0-SNAPSHOT.jar:rpc-common/target/rpc-common-1.0-SNAPSHOT.jar:rpc-api/target/rpc-api-1.0-SNAPSHOT.jar:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout -pl rpc-consumer -am) com.xiaoyu.rpc.consumer.ConsumerApp +java -cp rpc-consumer/target/rpc-consumer-1.0-SNAPSHOT.jar:rpc-transport-netty/target/rpc-transport-netty-1.0-SNAPSHOT.jar:rpc-core/target/rpc-core-1.0-SNAPSHOT.jar:rpc-common/target/rpc-common-1.0-SNAPSHOT.jar:rpc-api/target/rpc-api-1.0-SNAPSHOT.jar:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout -pl rpc-consumer -am) com.xiaoyu.rpc.consumer.ConsumerApp ``` ### 4. Running Tests **Unit Tests**: -Run the comprehensive unit test suite covering SPI, serializers, load balancers, and more: +Run the comprehensive unit test suite covering SPI, serializers, load balancers, and protocols: ```bash -mvn test -pl rpc-core +mvn test -pl rpc-core,rpc-transport-netty ``` **Integration Tests**: @@ -103,6 +105,7 @@ Configure the framework via `rpc-core/src/main/resources/rpc-config.yaml`. ```yaml rpc: + transport: "netty" # Transport: netty (default) protocol: "http2" # Protocol: netty, http, http2 server-host: 127.0.0.1 server-port: 8080 @@ -113,16 +116,26 @@ rpc: load-balancer: roundrobin # Load Balancer: roundrobin, random ``` -## 🔌 Extension Guide (SPI) +## 🔌 SPI Design & Ecosystem -XiaoYu RPC supports a powerful SPI (Service Provider Interface) mechanism, similar to Dubbo, allowing you to easily extend core functionality without modifying the source code. +XiaoYu RPC adheres to the **Microkernel Architecture**, where the core (`rpc-core`) only provides the lifecycle management and SPI (Service Provider Interface) definitions, while all specific functionalities are implemented as plugins. This design ensures the framework is highly extensible, lightweight, and follows the **Open-Closed Principle**. -### Supported Extension Points +### 🧩 Core Extension Points -- `com.xiaoyu.rpc.common.serialization.Serializer` -- `com.xiaoyu.rpc.core.loadbalancer.LoadBalancer` -- `com.xiaoyu.rpc.core.registry.ServiceRegistry` -- `com.xiaoyu.rpc.core.registry.ServiceDiscovery` +We strictly define interfaces to decouple every major component: + +| Interface | Description | Default Impl | Purpose | +|-----------|-------------|--------------|---------| +| **`Transport`** | Abstraction of network communication. Decouples the underlying I/O framework. | `NettyTransport` | Allow switching between Netty, Tomcat, or Socket without changing core logic. | +| **`Protocol`** | Message protocol definition. Controls how bytes are framed and processed. | `NettyProtocol` | Support multiple protocols (Custom RPC, gRPC, HTTP) on the same port. | +| **`Serializer`** | Object serialization strategy. | `ProtoBuf` | Balance performance (Protobuf/Kryo) vs Compatibility (JSON/Java). | +| **`LoadBalancer`** | Client-side load balancing strategy. | `RoundRobin` | Distribute traffic evenly or randomly to providers. | +| **`ServiceRegistry`** | Service registration and discovery. | `Nacos`, `Local` | Decouple from specific registry backend (swap Nacos for Zookeeper/Consul easily). | +| **`ProxyFactory`** | Dynamic proxy generation strategy. | `ByteBuddy` | Optimization for different JDK versions (ByteBuddy works best on Java 17+). | + +### 🛠️ ExtensionLoader + +We implemented a powerful loading mechanism similar to Dubbo's `ExtensionLoader`. It scans `META-INF/rpc/` for configuration files and loads implementation classes lazily by name. ### How to Add a New Extension @@ -179,7 +192,7 @@ This framework supports interoperability with standard gRPC clients (e.g., Pytho mvn clean package -DskipTests # Run the Provider - java -cp rpc-provider/target/rpc-provider-1.0-SNAPSHOT.jar:rpc-core/target/rpc-core-1.0-SNAPSHOT.jar:rpc-common/target/rpc-common-1.0-SNAPSHOT.jar:rpc-api/target/rpc-api-1.0-SNAPSHOT.jar:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout -pl rpc-provider -am) com.xiaoyu.rpc.provider.ProviderApp + java -cp rpc-provider/target/rpc-provider-1.0-SNAPSHOT.jar:rpc-transport-netty/target/rpc-transport-netty-1.0-SNAPSHOT.jar:rpc-core/target/rpc-core-1.0-SNAPSHOT.jar:rpc-common/target/rpc-common-1.0-SNAPSHOT.jar:rpc-api/target/rpc-api-1.0-SNAPSHOT.jar:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout -pl rpc-provider -am) com.xiaoyu.rpc.provider.ProviderApp ``` 3. **Run the Python Client**: Navigate to the `python_client` directory and set up the environment: From 35b674db240cd36308b2173961d20e4edcc554b0 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Fri, 23 Jan 2026 13:02:48 +0800 Subject: [PATCH 12/38] =?UTF-8?q?README-ZH=E6=9B=B4=E6=96=B0=EF=BC=8C?= =?UTF-8?q?=E6=96=B9=E4=BE=BF=E9=98=85=E8=AF=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README_ZH.md | 273 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 README_ZH.md diff --git a/README_ZH.md b/README_ZH.md new file mode 100644 index 0000000..e95de50 --- /dev/null +++ b/README_ZH.md @@ -0,0 +1,273 @@ +# 🚀 XiaoYu RPC Framework + +> 基于 **Netty**, **Nacos**, 和 **ByteBuddy** 构建的轻量级、高性能、可扩展 RPC 框架。 +> 支持多种协议,包括 **HTTP/2**, **HTTP/1.1**, 以及自定义 **Netty** 协议。 + +![Java](https://img.shields.io/badge/Java-17%2B-blue?style=flat-square&logo=java) +![Netty](https://img.shields.io/badge/Netty-4.1.x-green?style=flat-square) +![Nacos](https://img.shields.io/badge/Nacos-2.x-orange?style=flat-square) +![License](https://img.shields.io/badge/License-MIT-yellow?style=flat-square) + +--- + +## 📖 简介 + +本项目是一个旨在演示标准协议与动态调用融合的高性能插件化 RPC 框架。 +与传统强绑定单一协议或需要为每个服务生成代码的 RPC 框架不同,**XiaoYu RPC** 独创了 **"通用 gRPC 适配器 (Universal gRPC Adapter)"**。它实现了标准的 gRPC 协议 (HTTP/2 + Protobuf),但能够将请求动态路由到 Java 服务实现。这使得您可以: + +1. **直接使用标准 gRPC 客户端** (如 Python, Go, Node.js) 调用您的 Java 服务。 +2. **保留 Java 的动态灵活性** (反射/ByteBuddy),无需为每个业务类生成单独的 `.proto` Stub 代码。 + +## 🏗️ 项目架构 + +项目采用模块化设计,确保关注点分离和可维护性: + +| 模块 | 描述 | +|--------|-------------| +| **`rpc-api`** | 定义服务接口。由服务提供者(Provider)和消费者(Consumer)共享。 | +| **`rpc-common`** | 通用工具类、值对象 (`RpcRequest`, `RpcResponse`) 及 Protobuf 定义 (`rpc_meta.proto`)。 | +| **`rpc-core`** | 框架核心实现。包含 SPI 接口定义、动态代理和注册中心逻辑。**完全不含 Netty 依赖**。 | +| **`rpc-transport-netty`** | 基于 **Netty** 实现的默认传输层模块。 | +| **`rpc-provider`** | 示例服务提供者应用,用于实现并暴露服务。 | +| **`rpc-consumer`** | 示例服务消费者应用,用于引入并调用服务。 | +| **`python_client`** | Python 客户端实现,用于演示跨语言 gRPC 互操作性。 | + +## ✨ 核心特性 + +- **🔌 插件化架构**: 采用自定义 SPI 机制,实现最大程度的灵活性。 +- **🤝 通用 gRPC 兼容性**: 自研 `GrpcProtocol` 层,在 HTTP/2 上运行标准 gRPC 协议,实测可与官方 `grpc-python` 客户端完美互通。 +- **⚡ 动静混合模式**: 结合了 Protobuf 序列化(配合自定义类型包装器)的高性能和 Java 动态代理的灵活性。 +- **🚀 可插拔传输层**: 传输层完全解耦。默认实现为 `rpc-transport-netty`,但可无缝替换为 Tomcat 或 Socket 实现。 +- **📡 多协议支持**: 支持 `Netty` (自定义), `HTTP/1.1`, 或 `gRPC` (HTTP/2) 通信协议。 +- **⚡ 高性能代理**: 使用 **ByteBuddy** 生成动态代理,针对 Java 17+ 进行了优化。 +- **⚖️ 智能负载均衡**: 内置 `RoundRobin` (轮询) 和 `Random` (随机) 策略。 +- **📦 多样化序列化**: 支持 `Protobuf` (增强版), `Kryo`, `JSON`, 以及标准 `Java` 序列化。 +- **🔍 服务发现**: 集成 **Nacos** 实现健壮的服务注册与发现。 + +--- + +## 🔌 SPI 设计与生态 + +XiaoYu RPC 遵循 **微内核架构 (Microkernel Architecture)**,核心模块 (`rpc-core`) 仅提供生命周期管理和 SPI (Service Provider Interface) 定义,所有具体功能均作为插件实现。这种设计确保了框架的高度可扩展性和轻量化,并遵循 **开闭原则 (Open-Closed Principle)**。 + +### 🧩 核心扩展点 + +我们要严格定义接口以解耦每个主要组件: + +| 接口 | 描述 | 默认实现 | 设计目的 | +|-----------|-------------|--------------|---------| +| **`Transport`** | 网络通信抽象。解耦底层 I/O 框架。 | `NettyTransport` | 允许在不更改核心逻辑的情况下切换 Netty, Tomcat, 或 Socket。 | +| **`Protocol`** | 消息协议定义。控制字节流的帧处理方式。 | `NettyProtocol` | 支持在同一端口上运行多种协议 (自定义 RPC, gRPC, HTTP)。 | +| **`Serializer`** | 对象序列化策略。 | `ProtoBuf` | 平衡性能 (Protobuf/Kryo) 与兼容性 (JSON/Java)。 | +| **`LoadBalancer`** | 客户端负载均衡策略。 | `RoundRobin` | 均匀或随机地将流量分发给服务提供者。 | +| **`ServiceRegistry`** | 服务注册与发现。 | `Nacos`, `Local` | 解耦具体的注册中心后端 (可轻松替换为 Zookeeper/Consul)。 | +| **`ProxyFactory`** | 动态代理生成策略。 | `ByteBuddy` | 针对不同 JDK 版本的优化 (ByteBuddy 在 Java 17+ 表现更佳)。 | + +### 🛠️ ExtensionLoader + +我们实现了一套类似于 Dubbo 的强大加载机制 `ExtensionLoader`。它会扫描 `META-INF/rpc/` 目录下的配置文件,并按需懒加载实现类。 + +### 如何添加新扩展 + +1. **实现接口**: 创建一个类实现目标 SPI 接口 (例如 `Serializer`)。 +2. **创建 SPI 配置文件**: + * 在 `src/main/resources/META-INF/rpc/` 目录下创建文件。 + * 文件名必须与接口的全限定名一致 (例如 `com.xiaoyu.rpc.common.serialization.Serializer`)。 +3. **注册实现类**: 在文件中添加键值对: + ```properties + my-serializer=com.example.MyCustomSerializer + ``` +4. **使用扩展**: 更新 `rpc-config.yaml`: + ```yaml + rpc: + serializer: my-serializer + ``` + +--- + +## 🚀 快速开始 + +### 1. 前置条件 (Nacos) + +使用 Docker 启动 Nacos: + +```bash +docker run --name nacos-standalone \ + -e MODE=standalone \ + -p 8848:8848 \ + -p 9848:9848 \ + -d nacos/nacos-server:v2.3.1-slim +``` + +### 2. 运行 Provider + +执行以下命令启动 Java RPC Provider。这将编译项目并将 `HelloService` 注册到本地 Nacos 实例。 + +```bash +# 1. 构建项目 +mvn clean package -DskipTests + +# 2. 启动 Provider +java -cp rpc-provider/target/rpc-provider-1.0-SNAPSHOT.jar:rpc-transport-netty/target/rpc-transport-netty-1.0-SNAPSHOT.jar:rpc-core/target/rpc-core-1.0-SNAPSHOT.jar:rpc-common/target/rpc-common-1.0-SNAPSHOT.jar:rpc-api/target/rpc-api-1.0-SNAPSHOT.jar:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout -pl rpc-provider -am) com.xiaoyu.rpc.provider.ProviderApp +``` + +### 3. 运行 Consumer + +在 `rpc-consumer` 模块中执行 `ConsumerApp` 发起调用。 + +```bash +java -cp rpc-consumer/target/rpc-consumer-1.0-SNAPSHOT.jar:rpc-transport-netty/target/rpc-transport-netty-1.0-SNAPSHOT.jar:rpc-core/target/rpc-core-1.0-SNAPSHOT.jar:rpc-common/target/rpc-common-1.0-SNAPSHOT.jar:rpc-api/target/rpc-api-1.0-SNAPSHOT.jar:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout -pl rpc-consumer -am) com.xiaoyu.rpc.consumer.ConsumerApp +``` + +### 4. 运行测试 + +**单元测试**: +运行包含 SPI、序列化器、负载均衡器和协议的全套单元测试: + +```bash +mvn test -pl rpc-core,rpc-transport-netty +``` + +**集成测试**: +运行完整的集成测试套件: + +```bash +mvn test -pl rpc-consumer -Dtest=FullIntegrationTest +``` + +--- + +## 🛠️ 配置手册 + +通过 `rpc-core/src/main/resources/rpc-config.yaml` 配置框架。 + +```yaml +rpc: + transport: "netty" # 传输层: netty (默认) + protocol: "http2" # 协议: netty, http, http2 + server-host: 127.0.0.1 + server-port: 8080 + registry: "nacos" # 注册中心: nacos, local + registry-address: "127.0.0.1:8848" + serializer: KRYO # 序列化器: PROTOBUF, KRYO, JAVA, JSON + proxy: bytebuddy # 代理方式: jdk, bytebuddy + load-balancer: roundrobin # 负载均衡: roundrobin, random +``` + +--- + +## ❓ 常见问题 (FAQ) + +**Q: 为什么选择 ByteBuddy?** +A: CGLIB 在 Java 17+ 上由于深层反射限制存在问题。ByteBuddy 是目前字节码操作的行业标准。 + +**Q: 连接超时或被拒绝 (Connection Refused)?** +A: 请确保 Nacos 已运行且端口 `8848` 和 `9848` 可访问。检查 `rpc-config.yaml` 主机/端口配置是否正确。 + +--- + +## 🐍 跨语言 gRPC 支持 (Python) + +本框架支持与标准 gRPC 客户端(如 Python)互操作,允许非 Java 客户端调用本框架托管的服务。 + +### 特性 + +- **标准 gRPC 协议**: 实现了兼容主流 gRPC 库(如 `grpc-io`)的标准 HTTP/2 传输层。 +- **Protobuf 序列化**: 通过包装类型支持标准 Protobuf `Empty`, `StringValue`, `Int32Value` 等的数据交换。 +- **Nacos 集成**: 注册在 Nacos 中的服务可以被发现和调用。 + +### 使用指南 + +1. **配置 Java 服务端**: + 更新 `rpc-config.yaml` 启用 `grpc` 协议和 `protobuf` 序列化: + + ```yaml + rpc: + protocol: "grpc" + serializer: "protobuf" + registry: "nacos" + ``` + +2. **启动 Java Provider (gRPC 模式)**: + + ```bash + # 构建项目 (跳过测试以加速) + mvn clean package -DskipTests + + # 启动 Provider + java -cp rpc-provider/target/rpc-provider-1.0-SNAPSHOT.jar:rpc-transport-netty/target/rpc-transport-netty-1.0-SNAPSHOT.jar:rpc-core/target/rpc-core-1.0-SNAPSHOT.jar:rpc-common/target/rpc-common-1.0-SNAPSHOT.jar:rpc-api/target/rpc-api-1.0-SNAPSHOT.jar:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout -pl rpc-provider -am) com.xiaoyu.rpc.provider.ProviderApp + ``` + +3. **运行 Python 客户端**: + 进入 `python_client` 目录并配置环境: + + ```bash + cd python_client + + # 创建并激活虚拟环境 + python3 -m venv venv + source venv/bin/activate + + # 安装依赖 + pip install grpcio grpcio-tools protobuf + + # 运行客户端 + python3 client.py + ``` + + **预期输出**: + + ```text + RpcResponse received: + Data: Hello, World! (from Multi-Module Netty Server) + Message: Success + ``` + +## 🔄 持续集成与交付 (CI/CD) + +为了确保系统可靠性和代码质量,本项目集成了一套基于 **GitHub Actions** 的健壮 CI/CD 流水线。 + +**关键工作流:** +- **自动化测试**: 运行单元测试和集成测试以验证 RPC 功能。 +- **服务验证**: 在容器化环境中启动 Nacos, Java Provider, 和 Python Client 以测试跨语言互操作性。 +- **构建状态**: 通过 GitHub Actions 提供代码健康度的即时反馈。 + +![CI/CD Workflow Result](docs/images/image.png) + +--- + +## 🔬 技术深度解析: gRPC 协议实现 + +XiaoYu RPC 互操作性的核心在于基于 Netty HTTP/2 栈自定义实现的 gRPC 传输协议。 + +![gRPC Data Processing Flow](docs/images/grpc_processing_flow.png) + +### 1. 报文格式 (5字节头部) + +每个 gRPC 消息都以 5 字节的头部开始,由 `GrpcServerHandler` 直接处理: + +- **压缩标志 (1 字节)**: `0` (未压缩) 或 `1` (压缩)。 +- **消息长度 (4 字节)**: 大端序整数,指定后续 Protobuf 载荷的长度。 +- **载荷**: 标准 Protobuf 二进制数据,通过 `NativeProtobufSerializer` 反序列化。 + +### 2. 头部对齐 (Header Alignment) + +严格遵守 gRPC HTTP/2 头部规范以确保兼容性: + +- **:status**: `200` (HTTP 层面的成功) +- **content-type**: `application/grpc` (客户端识别的关键) +- **te**: `trailers` + +### 3. Trailer 与状态码 + +gRPC 使用 HTTP/2 Trailers 来传递最终的 RPC 状态,这与 HTTP 状态码是区分开的。 + +- **HEADERS 帧 (EndStream=true)**: 在数据载荷之后发送。 +- **grpc-status**: `0` 表示 OK,非零表示错误。 +- **grpc-message**: 描述性错误信息。 + +--- + +## 🤝 贡献 + +欢迎贡献代码!请随时提交 Issue 或 Pull Request 以改进框架。 From 67239a24b1eeb7661dc9c658c904c1c3891715ce Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Fri, 23 Jan 2026 13:05:00 +0800 Subject: [PATCH 13/38] =?UTF-8?q?README=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E4=BA=86FAQ=E9=83=A8=E5=88=86=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 +++++++++ README_ZH.md | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/README.md b/README.md index 85b4cf1..88602bc 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,15 @@ A: CGLIB is problematic on Java 17+ due to deep reflection restrictions. ByteBud **Q: Connection Timeout/Refusal?** A: Ensure Nacos is running and the ports `8848` and `9848` are accessible. Check your `rpc-config.yaml` for correct host/port settings. +**Q: How to switch to Local Registry for testing?** +A: Set `registry: "local"` in `rpc-config.yaml`. This bypasses Nacos and uses an in-memory map, useful for unit tests or offline development. + +**Q: Can I use this with Spring Boot?** +A: Yes. Although this is a standalone framework, you can wrap `RpcServer` as a Spring `@Bean` and use `@PostConstruct` to start it. + +**Q: Encountering "No Transport Found" error?** +A: Make sure you have included `rpc-transport-netty` (or custom transport module) in your runtime dependencies. `rpc-core` does not include a transport implementation by default to ensure modularity. + --- ## 🐍 Cross-Language gRPC Support (Python) diff --git a/README_ZH.md b/README_ZH.md index e95de50..2701beb 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -164,6 +164,15 @@ A: CGLIB 在 Java 17+ 上由于深层反射限制存在问题。ByteBuddy 是目 **Q: 连接超时或被拒绝 (Connection Refused)?** A: 请确保 Nacos 已运行且端口 `8848` 和 `9848` 可访问。检查 `rpc-config.yaml` 主机/端口配置是否正确。 +**Q: 如何切换到本地注册中心进行测试?** +A: 在 `rpc-config.yaml` 中设置 `registry: "local"`。这将绕过 Nacos,使用内存 Map 进行服务注册,非常适合单元测试或无网开发。 + +**Q: 我可以在 Spring Boot 中使用吗?** +A: 可以。虽然这是一个独立框架,但你可以将 `RpcServer` 封装为 Spring `@Bean` 并使用 `@PostConstruct` 启动它。 + +**Q: 遇到 "No Transport Found" 错误?** +A: 请确保你在运行时依赖中引入了 `rpc-transport-netty`(或其他传输模块)。为了保证轻量和解耦,`rpc-core` 默认不包含传输层实现。 + --- ## 🐍 跨语言 gRPC 支持 (Python) From 36ae6c6b3c13b1c08f9dbefe00bd4a81543ca7a4 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Fri, 23 Jan 2026 13:18:49 +0800 Subject: [PATCH 14/38] =?UTF-8?q?SpringBoot=EF=BC=9A=E5=BC=95=E5=85=A5?= =?UTF-8?q?=E4=BA=86rpc-spring-boot-starter=E6=A8=A1=E5=9D=97=EF=BC=8C?= =?UTF-8?q?=E7=94=A8=E4=BA=8E=E8=87=AA=E5=8A=A8=E4=B8=8ESpring=20Boot?= =?UTF-8?q?=E9=9B=86=E6=88=90RPC=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 60 +++++++++- README_ZH.md | 57 +++++++++- pom.xml | 8 +- rpc-spring-boot-starter/pom.xml | 58 ++++++++++ .../rpc/spring/RpcAutoConfiguration.java | 99 ++++++++++++++++ .../rpc/spring/annotation/RpcReference.java | 23 ++++ .../rpc/spring/annotation/RpcService.java | 24 ++++ .../rpc/spring/config/RpcProperties.java | 62 ++++++++++ .../spring/processor/RpcPostProcessor.java | 106 ++++++++++++++++++ ...ot.autoconfigure.AutoConfiguration.imports | 1 + 10 files changed, 488 insertions(+), 10 deletions(-) create mode 100644 rpc-spring-boot-starter/pom.xml create mode 100644 rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/RpcAutoConfiguration.java create mode 100644 rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/annotation/RpcReference.java create mode 100644 rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/annotation/RpcService.java create mode 100644 rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/config/RpcProperties.java create mode 100644 rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/processor/RpcPostProcessor.java create mode 100644 rpc-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports diff --git a/README.md b/README.md index 88602bc..0a769a4 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ ## 📖 Introduction This project is a high-performance, pluggable RPC framework designed to demonstrate the convergence of standard protocols and dynamic invocation. -Unlike traditional RPC frameworks that bind tightly to a single protocol or require strict code generation for every service, **XiaoYu RPC** features a unique **"Universal gRPC Adpater"**. It implements the standard gRPC protocol (HTTP/2 + Protobuf) but routes requests dynamically to Java service implementations. This allows you to: +Unlike traditional RPC frameworks that bind tightly to a single protocol or require strict code generation for every service, **XiaoYu RPC** features a unique **"Universal gRPC Adapter"**. It implements the standard gRPC protocol (HTTP/2 + Protobuf) but routes requests dynamically to Java service implementations. This allows you to: 1. **Use standard gRPC clients** (like Python, Go, Node.js) to call your Java services directly. 2. **Retain Java's dynamic flexibility** (Reflection/ByteBuddy) without generating separate `.proto` service stubs for every business class. @@ -164,14 +164,66 @@ A: Ensure Nacos is running and the ports `8848` and `9848` are accessible. Check **Q: How to switch to Local Registry for testing?** A: Set `registry: "local"` in `rpc-config.yaml`. This bypasses Nacos and uses an in-memory map, useful for unit tests or offline development. -**Q: Can I use this with Spring Boot?** -A: Yes. Although this is a standalone framework, you can wrap `RpcServer` as a Spring `@Bean` and use `@PostConstruct` to start it. **Q: Encountering "No Transport Found" error?** A: Make sure you have included `rpc-transport-netty` (or custom transport module) in your runtime dependencies. `rpc-core` does not include a transport implementation by default to ensure modularity. --- +## 🌱 Spring Boot Integration + +A dedicated Spring Boot Starter is available: `rpc-spring-boot-starter`. + +### Dependency + +```xml + + com.xiaoyu.rpc + rpc-spring-boot-starter + 1.0-SNAPSHOT + +``` + +### Provider Example + +```java +@RpcService +public class HelloServiceImpl implements HelloService { + @Override + public String sayHello(String name) { + return "Hello, " + name; + } +} +``` + +### Consumer Example + +```java +@RestController +public class HelloController { + @RpcReference + private HelloService helloService; + + @GetMapping("/hello") + public String hello(@RequestParam String name) { + return helloService.sayHello(name); + } +} +``` + +### Configuration (`application.yml`) + +```yaml +rpc: + server-port: 8080 + registry: nacos + registry-address: 127.0.0.1:8848 + serializer: kryo + server-enabled: true # Set to false for consumer-only apps +``` + +--- + ## 🐍 Cross-Language gRPC Support (Python) This framework supports interoperability with standard gRPC clients (e.g., Python), allowing non-Java clients to invoke services hosted by the RPC framework. @@ -180,7 +232,7 @@ This framework supports interoperability with standard gRPC clients (e.g., Pytho - **Standard gRPC Protocol**: Implements standard HTTP/2 transport compatible with widespread gRPC libraries (via `grpc-io`). - **Protobuf Serialization**: Supports standard Protobuf `Empty`, `StringValue`, `Int32Value`, etc., via wrapper types for seamless data exchange. -- **Nacos Integation**: Services registered in Nacos can be discovered and invoked. +- **Nacos Integration**: Services registered in Nacos can be discovered and invoked. ### Usage Guide diff --git a/README_ZH.md b/README_ZH.md index 2701beb..80a17e3 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -167,14 +167,67 @@ A: 请确保 Nacos 已运行且端口 `8848` 和 `9848` 可访问。检查 `rpc- **Q: 如何切换到本地注册中心进行测试?** A: 在 `rpc-config.yaml` 中设置 `registry: "local"`。这将绕过 Nacos,使用内存 Map 进行服务注册,非常适合单元测试或无网开发。 -**Q: 我可以在 Spring Boot 中使用吗?** -A: 可以。虽然这是一个独立框架,但你可以将 `RpcServer` 封装为 Spring `@Bean` 并使用 `@PostConstruct` 启动它。 + **Q: 遇到 "No Transport Found" 错误?** A: 请确保你在运行时依赖中引入了 `rpc-transport-netty`(或其他传输模块)。为了保证轻量和解耦,`rpc-core` 默认不包含传输层实现。 --- +## 🌱 Spring Boot 集成 + +我们提供了一个专用的 Spring Boot Starter: `rpc-spring-boot-starter`。 + +### 依赖引入 + +```xml + + com.xiaoyu.rpc + rpc-spring-boot-starter + 1.0-SNAPSHOT + +``` + +### 服务提供者示例 + +```java +@RpcService +public class HelloServiceImpl implements HelloService { + @Override + public String sayHello(String name) { + return "Hello, " + name; + } +} +``` + +### 服务消费者示例 + +```java +@RestController +public class HelloController { + @RpcReference + private HelloService helloService; + + @GetMapping("/hello") + public String hello(@RequestParam String name) { + return helloService.sayHello(name); + } +} +``` + +### 配置 (`application.yml`) + +```yaml +rpc: + server-port: 8080 + registry: nacos + registry-address: 127.0.0.1:8848 + serializer: kryo + server-enabled: true # 对于纯消费者应用,设置为 false +``` + +--- + ## 🐍 跨语言 gRPC 支持 (Python) 本框架支持与标准 gRPC 客户端(如 Python)互操作,允许非 Java 客户端调用本框架托管的服务。 diff --git a/pom.xml b/pom.xml index 7055b1a..e5f30ef 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,6 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.xiaoyu.rpc @@ -14,6 +13,7 @@ rpc-api rpc-core rpc-transport-netty + rpc-spring-boot-starter rpc-provider rpc-consumer @@ -117,7 +117,7 @@ grpc-stub 1.58.0 - + org.apache.tomcat annotations-api 6.0.53 @@ -134,7 +134,7 @@ 1.7.1 - + org.apache.maven.plugins diff --git a/rpc-spring-boot-starter/pom.xml b/rpc-spring-boot-starter/pom.xml new file mode 100644 index 0000000..0ba800a --- /dev/null +++ b/rpc-spring-boot-starter/pom.xml @@ -0,0 +1,58 @@ + + + + grpc-demo + com.xiaoyu.rpc + 1.0-SNAPSHOT + + 4.0.0 + + rpc-spring-boot-starter + + + 3.2.0 + + + + + + com.xiaoyu.rpc + rpc-core + ${project.version} + + + + + com.xiaoyu.rpc + rpc-transport-netty + ${project.version} + + + + + org.springframework.boot + spring-boot-starter + ${spring-boot.version} + + + org.springframework.boot + spring-boot-autoconfigure + ${spring-boot.version} + + + org.springframework.boot + spring-boot-configuration-processor + ${spring-boot.version} + true + + + + + org.projectlombok + lombok + provided + + + + diff --git a/rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/RpcAutoConfiguration.java b/rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/RpcAutoConfiguration.java new file mode 100644 index 0000000..cc7e6f8 --- /dev/null +++ b/rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/RpcAutoConfiguration.java @@ -0,0 +1,99 @@ +package com.xiaoyu.rpc.spring; + +import com.xiaoyu.rpc.core.config.RpcConfig; +import com.xiaoyu.rpc.core.server.RpcServer; +import com.xiaoyu.rpc.spring.config.RpcProperties; +import com.xiaoyu.rpc.spring.processor.RpcPostProcessor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * RPC 自动配置类 + */ +@Slf4j +@Configuration +@EnableConfigurationProperties(RpcProperties.class) +public class RpcAutoConfiguration { + + /** + * 将 Spring Boot 配置同步到 RpcConfig (核心框架配置) + */ + @Bean + public RpcConfig rpcConfig(RpcProperties properties) { + // 通过 System Properties 传递配置,让 RpcConfig 能够读取 + System.setProperty("rpc.transport", properties.getTransport()); + System.setProperty("rpc.protocol", properties.getProtocol()); + System.setProperty("rpc.server-host", properties.getServerHost()); + System.setProperty("rpc.server-port", String.valueOf(properties.getServerPort())); + System.setProperty("rpc.registry", properties.getRegistry()); + System.setProperty("rpc.registry-address", properties.getRegistryAddress()); + System.setProperty("rpc.serializer", properties.getSerializer()); + System.setProperty("rpc.proxy", properties.getProxy()); + System.setProperty("rpc.load-balancer", properties.getLoadBalancer()); + + log.info("RPC 配置已从 Spring Boot 同步: registry={}, port={}", + properties.getRegistry(), properties.getServerPort()); + + return RpcConfig.getInstance(); + } + + /** + * 创建 RpcServer Bean (仅当 serverEnabled=true 时) + */ + @Bean + @ConditionalOnProperty(prefix = "rpc", name = "server-enabled", havingValue = "true", matchIfMissing = true) + @ConditionalOnMissingBean + public RpcServer rpcServer(RpcConfig rpcConfig) { + log.info("创建 RpcServer Bean"); + return new RpcServer(); + } + + /** + * 创建 RPC Bean 后处理器 + */ + @Bean + public RpcPostProcessor rpcPostProcessor() { + return new RpcPostProcessor(); + } + + /** + * 启动 RpcServer + */ + @Bean + @ConditionalOnProperty(prefix = "rpc", name = "server-enabled", havingValue = "true", matchIfMissing = true) + public RpcServerRunner rpcServerRunner(RpcServer rpcServer) { + return new RpcServerRunner(rpcServer); + } + + /** + * 使用 CommandLineRunner 启动 RpcServer + */ + @Slf4j + public static class RpcServerRunner implements org.springframework.boot.CommandLineRunner { + private final RpcServer rpcServer; + + public RpcServerRunner(RpcServer rpcServer) { + this.rpcServer = rpcServer; + } + + @Override + public void run(String... args) throws Exception { + log.info("启动 RPC Server..."); + // 在新线程中启动,避免阻塞 Spring Boot 主线程 + Thread serverThread = new Thread(() -> { + try { + rpcServer.start(); + } catch (InterruptedException e) { + log.error("RPC Server 启动失败", e); + Thread.currentThread().interrupt(); + } + }, "rpc-server-thread"); + serverThread.setDaemon(true); + serverThread.start(); + } + } +} diff --git a/rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/annotation/RpcReference.java b/rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/annotation/RpcReference.java new file mode 100644 index 0000000..6dc6f8e --- /dev/null +++ b/rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/annotation/RpcReference.java @@ -0,0 +1,23 @@ +package com.xiaoyu.rpc.spring.annotation; + +import java.lang.annotation.*; + +/** + * 标注在字段上,表示需要注入 RPC 服务代理。 + * Spring Boot Starter 会自动创建代理并注入。 + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RpcReference { + + /** + * 服务版本号 + */ + String version() default "1.0"; + + /** + * 负载均衡策略 (roundrobin, random) + */ + String loadBalancer() default ""; +} diff --git a/rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/annotation/RpcService.java b/rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/annotation/RpcService.java new file mode 100644 index 0000000..98fef44 --- /dev/null +++ b/rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/annotation/RpcService.java @@ -0,0 +1,24 @@ +package com.xiaoyu.rpc.spring.annotation; + +import java.lang.annotation.*; + +/** + * 标注在服务实现类上,表示该类是一个 RPC 服务提供者。 + * Spring Boot Starter 会自动扫描并注册到 RPC 注册中心。 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface RpcService { + + /** + * 服务接口类。如果不指定,则默认使用实现类的第一个接口。 + */ + Class interfaceClass() default void.class; + + /** + * 服务版本号 + */ + String version() default "1.0"; +} diff --git a/rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/config/RpcProperties.java b/rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/config/RpcProperties.java new file mode 100644 index 0000000..1f4f134 --- /dev/null +++ b/rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/config/RpcProperties.java @@ -0,0 +1,62 @@ +package com.xiaoyu.rpc.spring.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * RPC 配置属性,映射 application.yml 中的 rpc.* 配置 + */ +@Data +@ConfigurationProperties(prefix = "rpc") +public class RpcProperties { + + /** + * 传输层实现 (netty) + */ + private String transport = "netty"; + + /** + * 协议类型 (netty, http, http2, grpc) + */ + private String protocol = "netty"; + + /** + * 服务端绑定主机 + */ + private String serverHost = "127.0.0.1"; + + /** + * 服务端绑定端口 + */ + private int serverPort = 8080; + + /** + * 注册中心类型 (nacos, local) + */ + private String registry = "nacos"; + + /** + * 注册中心地址 + */ + private String registryAddress = "127.0.0.1:8848"; + + /** + * 序列化方式 (kryo, protobuf, json, java) + */ + private String serializer = "kryo"; + + /** + * 代理方式 (jdk, bytebuddy) + */ + private String proxy = "bytebuddy"; + + /** + * 负载均衡策略 (roundrobin, random) + */ + private String loadBalancer = "roundrobin"; + + /** + * 是否启用服务端 (Provider 模式) + */ + private boolean serverEnabled = true; +} diff --git a/rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/processor/RpcPostProcessor.java b/rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/processor/RpcPostProcessor.java new file mode 100644 index 0000000..7545abd --- /dev/null +++ b/rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/processor/RpcPostProcessor.java @@ -0,0 +1,106 @@ +package com.xiaoyu.rpc.spring.processor; + +import com.xiaoyu.rpc.core.client.RpcClientProxy; +import com.xiaoyu.rpc.core.server.RpcServer; +import com.xiaoyu.rpc.spring.annotation.RpcReference; +import com.xiaoyu.rpc.spring.annotation.RpcService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +import java.lang.reflect.Field; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * RPC Bean 后处理器 + * 1. 扫描 @RpcService 注解,自动注册服务 + * 2. 扫描 @RpcReference 注解,自动注入代理 + */ +@Slf4j +public class RpcPostProcessor implements BeanPostProcessor, ApplicationContextAware { + + private ApplicationContext applicationContext; + private RpcServer rpcServer; + + // 代理缓存,避免重复创建 + private final Map, Object> proxyCache = new ConcurrentHashMap<>(); + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + // 1. 处理 @RpcService 注解 (服务注册) + Class beanClass = bean.getClass(); + if (beanClass.isAnnotationPresent(RpcService.class)) { + registerService(bean, beanClass); + } + + // 2. 处理 @RpcReference 注解 (代理注入) + injectRpcReferences(bean, beanClass); + + return bean; + } + + /** + * 注册服务到 RpcServer + */ + @SuppressWarnings("unchecked") + private void registerService(Object bean, Class beanClass) { + RpcService rpcService = beanClass.getAnnotation(RpcService.class); + Class interfaceClass = rpcService.interfaceClass(); + + // 如果未指定接口,则使用第一个实现的接口 + if (interfaceClass == void.class) { + Class[] interfaces = beanClass.getInterfaces(); + if (interfaces.length == 0) { + throw new RuntimeException("@RpcService 必须实现至少一个接口: " + beanClass.getName()); + } + interfaceClass = interfaces[0]; + } + + // 延迟获取 RpcServer (因为可能还没初始化) + if (rpcServer == null) { + rpcServer = applicationContext.getBean(RpcServer.class); + } + + log.info("注册 RPC 服务: {} -> {}", interfaceClass.getName(), beanClass.getName()); + rpcServer.register((Class) interfaceClass, bean); + } + + /** + * 注入 @RpcReference 标注的字段 + */ + private void injectRpcReferences(Object bean, Class beanClass) { + Field[] fields = beanClass.getDeclaredFields(); + for (Field field : fields) { + if (field.isAnnotationPresent(RpcReference.class)) { + Class fieldType = field.getType(); + + // 从缓存获取或创建代理 + Object proxy = proxyCache.computeIfAbsent(fieldType, this::createProxy); + + // 注入 + field.setAccessible(true); + try { + field.set(bean, proxy); + log.info("注入 RPC 代理: {}.{}", beanClass.getSimpleName(), field.getName()); + } catch (IllegalAccessException e) { + throw new RuntimeException("无法注入 RPC 代理到字段: " + field.getName(), e); + } + } + } + } + + /** + * 创建 RPC 客户端代理 + */ + private Object createProxy(Class interfaceClass) { + return RpcClientProxy.create(interfaceClass); + } +} diff --git a/rpc-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/rpc-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..78a58bb --- /dev/null +++ b/rpc-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.xiaoyu.rpc.spring.RpcAutoConfiguration From 29f89ad86f75b018266a961a25ccb0a42f730b04 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Thu, 29 Jan 2026 10:57:40 +0800 Subject: [PATCH 15/38] =?UTF-8?q?JMH:=20=E6=96=B0=E5=A2=9E=20rpc-benchmark?= =?UTF-8?q?=20=E6=A8=A1=E5=9D=97=E5=B9=B6=E4=BC=98=E5=8C=96=20Kryo=20?= =?UTF-8?q?=E5=BA=8F=E5=88=97=E5=8C=96=E5=99=A8=E4=BB=A5=E6=8F=90=E9=AB=98?= =?UTF-8?q?=20Protobuf=20=E6=80=A7=E8=83=BD=E5=92=8C=E8=B5=84=E6=BA=90?= =?UTF-8?q?=E5=A4=8D=E7=94=A8=EF=BC=8C=E7=84=B6=E5=90=8E=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?=E4=BA=86JMH=E6=80=A7=E8=83=BD=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- grpc-demo.iml | 2 +- pom.xml | 6 +- rpc-benchmark/pom.xml | 105 ++++++++++++++++ .../rpc/benchmark/ProtocolBenchmark.java | 117 ++++++++++++++++++ .../rpc/benchmark/SerializationBenchmark.java | 96 ++++++++++++++ rpc-benchmark/src/main/resources/logback.xml | 14 +++ .../serialization/JsonSerializerImpl.java | 19 +++ .../serialization/KryoSerializerImpl.java | 81 ++++++++---- .../rpc/core/protocol/http/HttpProtocol.java | 5 +- .../core/protocol/http/HttpRpcDecoder.java | 3 +- .../core/protocol/http/HttpRpcEncoder.java | 2 +- .../transport/netty/NettyTransportClient.java | 40 +++--- 12 files changed, 441 insertions(+), 49 deletions(-) create mode 100644 rpc-benchmark/pom.xml create mode 100644 rpc-benchmark/src/main/java/com/xiaoyu/rpc/benchmark/ProtocolBenchmark.java create mode 100644 rpc-benchmark/src/main/java/com/xiaoyu/rpc/benchmark/SerializationBenchmark.java create mode 100644 rpc-benchmark/src/main/resources/logback.xml diff --git a/grpc-demo.iml b/grpc-demo.iml index 883f51f..0d3a756 100644 --- a/grpc-demo.iml +++ b/grpc-demo.iml @@ -2,8 +2,8 @@ - + diff --git a/pom.xml b/pom.xml index e5f30ef..7645c9e 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,7 @@ rpc-spring-boot-starter rpc-provider rpc-consumer + rpc-benchmark @@ -23,7 +24,7 @@ 17 UTF-8 4.1.86.Final - 1.18.42 + 1.18.36 1.5.20 5.6.2 3.24.0 @@ -141,8 +142,7 @@ maven-compiler-plugin 3.11.0 - 17 - 17 + 17 org.projectlombok diff --git a/rpc-benchmark/pom.xml b/rpc-benchmark/pom.xml new file mode 100644 index 0000000..92c1a25 --- /dev/null +++ b/rpc-benchmark/pom.xml @@ -0,0 +1,105 @@ + + + + com.xiaoyu.rpc + grpc-demo + 1.0-SNAPSHOT + + 4.0.0 + + rpc-benchmark + + + 1.37 + + + + + org.openjdk.jmh + jmh-core + ${jmh.version} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + provided + + + + com.xiaoyu.rpc + rpc-core + ${project.version} + + + com.xiaoyu.rpc + rpc-api + ${project.version} + + + com.xiaoyu.rpc + rpc-transport-netty + ${project.version} + + + + + ch.qos.logback + logback-classic + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 17 + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + + package + + shade + + + benchmarks + + + org.openjdk.jmh.Main + + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + + + diff --git a/rpc-benchmark/src/main/java/com/xiaoyu/rpc/benchmark/ProtocolBenchmark.java b/rpc-benchmark/src/main/java/com/xiaoyu/rpc/benchmark/ProtocolBenchmark.java new file mode 100644 index 0000000..9043242 --- /dev/null +++ b/rpc-benchmark/src/main/java/com/xiaoyu/rpc/benchmark/ProtocolBenchmark.java @@ -0,0 +1,117 @@ +package com.xiaoyu.rpc.benchmark; + +import com.google.protobuf.ByteString; +import com.xiaoyu.rpc.api.HelloService; +import com.xiaoyu.rpc.common.extension.ExtensionLoader; +import com.xiaoyu.rpc.common.serialization.Serializer; +import com.xiaoyu.rpc.common.vo.RpcRequest; +import com.xiaoyu.rpc.common.vo.RpcResponse; +import com.xiaoyu.rpc.core.config.RpcConfig; +import com.xiaoyu.rpc.core.server.ServiceRepository; +import com.xiaoyu.rpc.core.transport.TransportClient; +import com.xiaoyu.rpc.core.transport.netty.NettyTransportClient; +import com.xiaoyu.rpc.core.transport.netty.NettyTransportServer; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.lang.reflect.Field; +import java.net.InetSocketAddress; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode({ Mode.Throughput, Mode.AverageTime }) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@State(Scope.Benchmark) +@Fork(value = 1, warmups = 0) +@Warmup(iterations = 1, time = 1) +@Measurement(iterations = 2, time = 1) +public class ProtocolBenchmark { + + @Param({ "netty", "http", "http2" }) + private String protocol; + + private NettyTransportServer server; + private TransportClient client; + private InetSocketAddress address; + private RpcRequest request; + private int port = 9091; + + @Setup + public void setup() throws Exception { + // Use a random port to avoid conflicts (Address already in use / TIME_WAIT) + this.port = 10000 + new java.util.Random().nextInt(50000); + + // 1. Set Protocol via Reflection + RpcConfig config = RpcConfig.getInstance(); + Field protocolField = RpcConfig.class.getDeclaredField("protocol"); + protocolField.setAccessible(true); + protocolField.set(config, protocol); + + // Ensure Serializer is set to something known, e.g., "java" for arg + // serialization + Field serializerField = RpcConfig.class.getDeclaredField("serializerType"); + serializerField.setAccessible(true); + serializerField.set(config, "java"); + + // 2. Register Service + ServiceRepository.registerService(HelloService.class.getName(), new HelloServiceImpl()); + + // 3. Start Server + server = new NettyTransportServer(port); + Thread serverThread = new Thread(() -> { + try { + server.start(); + } catch (Exception e) { + e.printStackTrace(); // Log server startup errors + } + }); + serverThread.setDaemon(true); + serverThread.start(); + + // Wait for server to start + TimeUnit.SECONDS.sleep(2); + + // 4. Setup Client + client = new NettyTransportClient(); + address = new InetSocketAddress("127.0.0.1", port); + + // 5. Build Request + Serializer serializer = ExtensionLoader.getExtensionLoader(Serializer.class).getExtension("java"); + byte[] argBytes = serializer.serialize("Benchmark"); + + request = RpcRequest.newBuilder() + .setInterfaceName(HelloService.class.getName()) + .setMethodName("sayHello") + .addParamTypes("java.lang.String") + .addParameters(ByteString.copyFrom(argBytes)) + .build(); + } + + @TearDown + public void teardown() { + if (server != null) { + server.stop(); + } + } + + @Benchmark + public Object benchmarkCall() { + return client.sendRequest(request, address); + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(ProtocolBenchmark.class.getSimpleName()) + .build(); + new Runner(opt).run(); + } + + public static class HelloServiceImpl implements HelloService { + @Override + public String sayHello(String name) { + return "Hello, " + name; + } + } +} diff --git a/rpc-benchmark/src/main/java/com/xiaoyu/rpc/benchmark/SerializationBenchmark.java b/rpc-benchmark/src/main/java/com/xiaoyu/rpc/benchmark/SerializationBenchmark.java new file mode 100644 index 0000000..31c656d --- /dev/null +++ b/rpc-benchmark/src/main/java/com/xiaoyu/rpc/benchmark/SerializationBenchmark.java @@ -0,0 +1,96 @@ +package com.xiaoyu.rpc.benchmark; + +import com.xiaoyu.rpc.common.extension.ExtensionLoader; +import com.xiaoyu.rpc.common.serialization.Serializer; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import com.xiaoyu.rpc.common.vo.RpcRequest; +import java.io.Serializable; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode({ Mode.AverageTime, Mode.Throughput }) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Benchmark) +@Fork(value = 1, warmups = 1) +@Warmup(iterations = 1, time = 1) +@Measurement(iterations = 2, time = 1) +public class SerializationBenchmark { + + @Param({ "java", "kryo", "json", "protobuf" }) + private String serializerName; + + private Serializer serializer; + private RpcRequest rpcRequest; + private String testString; + + private byte[] serializedRequestBytes; + private byte[] serializedStringBytes; + + @Setup + public void setup() { + serializer = ExtensionLoader.getExtensionLoader(Serializer.class).getExtension(serializerName); + + // Setup String + testString = "Hello, Benchmark! This is a test string for RPC serialization comparison."; + try { + serializedStringBytes = serializer.serialize(testString); + } catch (Exception e) { + System.err.println("Serializer [" + serializerName + "] failed to serialize String: " + e.getMessage()); + } + + // Setup RpcRequest (Protobuf Message) + // Note: Java/Kryo/Json can also serialize this since it implements Serializable + // (via GeneratedMessageV3) + // or effectively acts as a POJO for them. + RpcRequest.Builder builder = RpcRequest.newBuilder() + .setInterfaceName("com.example.HelloService") + .setMethodName("sayHello") + .addParamTypes("java.lang.String"); + + // Add dummy bytes parameter + builder.addParameters(com.google.protobuf.ByteString.copyFromUtf8("Benchmark")); + + rpcRequest = builder.build(); + + try { + serializedRequestBytes = serializer.serialize(rpcRequest); + System.out.println( + "Serializer [" + serializerName + "] POJO (RpcRequest) Size: " + serializedRequestBytes.length + + " bytes"); + } catch (Exception e) { + System.err.println("Serializer [" + serializerName + "] failed to serialize POJO: " + e.getMessage()); + } + } + + @Benchmark + public void serializePojo(org.openjdk.jmh.infra.Blackhole bh) { + bh.consume(serializer.serialize(rpcRequest)); + } + + @Benchmark + public void deserializePojo(org.openjdk.jmh.infra.Blackhole bh) { + bh.consume(serializer.deserialize(serializedRequestBytes, RpcRequest.class)); + } + + @Benchmark + public void serializeString(org.openjdk.jmh.infra.Blackhole bh) { + bh.consume(serializer.serialize(testString)); + } + + @Benchmark + public void deserializeString(org.openjdk.jmh.infra.Blackhole bh) { + bh.consume(serializer.deserialize(serializedStringBytes, String.class)); + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(SerializationBenchmark.class.getSimpleName()) + .build(); + new Runner(opt).run(); + } +} diff --git a/rpc-benchmark/src/main/resources/logback.xml b/rpc-benchmark/src/main/resources/logback.xml new file mode 100644 index 0000000..2a299a7 --- /dev/null +++ b/rpc-benchmark/src/main/resources/logback.xml @@ -0,0 +1,14 @@ + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/serialization/JsonSerializerImpl.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/serialization/JsonSerializerImpl.java index 9c97acd..90ff37d 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/serialization/JsonSerializerImpl.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/serialization/JsonSerializerImpl.java @@ -12,6 +12,25 @@ public class JsonSerializerImpl implements Serializer { public JsonSerializerImpl() { this.gson = new GsonBuilder() + .registerTypeHierarchyAdapter(com.google.protobuf.ByteString.class, + new TypeAdapter() { + @Override + public void write(com.google.gson.stream.JsonWriter out, + com.google.protobuf.ByteString value) throws java.io.IOException { + if (value == null) { + out.nullValue(); + return; + } + out.value(java.util.Base64.getEncoder().encodeToString(value.toByteArray())); + } + + @Override + public com.google.protobuf.ByteString read(com.google.gson.stream.JsonReader in) + throws java.io.IOException { + String s = in.nextString(); + return com.google.protobuf.ByteString.copyFrom(java.util.Base64.getDecoder().decode(s)); + } + }) .setDateFormat("yyyy-MM-dd HH:mm:ss") .create(); } diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/serialization/KryoSerializerImpl.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/serialization/KryoSerializerImpl.java index bb46f55..95c4b4f 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/serialization/KryoSerializerImpl.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/serialization/KryoSerializerImpl.java @@ -3,11 +3,6 @@ import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; -import com.esotericsoftware.kryo.serializers.JavaSerializer; -import lombok.extern.slf4j.Slf4j; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import com.xiaoyu.rpc.common.serialization.Serializer; import com.xiaoyu.rpc.common.serialization.SerializerCode; @@ -19,52 +14,88 @@ public class KryoSerializerImpl implements Serializer { private static final Logger log = LoggerFactory.getLogger(KryoSerializerImpl.class); - // 确保每个线程只创建一个 Kryo 对象并在该线程内复用,避免了并发冲突,也避免了每次序列化都 new Kryo() 的昂贵开销 + + // ThreadLocal for Kryo instances to ensure thread safety and reuse private static final ThreadLocal kryoThreadLocal = ThreadLocal.withInitial(() -> { Kryo kryo = new Kryo(); + kryo.setReferences(true); + kryo.setRegistrationRequired(false); - kryo.setReferences(true);// 处理循环引用的类 + // Register custom serializer for Protobuf objects (RpcRequest, RpcResponse) + // This avoids using JavaSerializer which is slow and uses efficient Protobuf + // methods directly. + com.esotericsoftware.kryo.Serializer protobufSerializer = new com.esotericsoftware.kryo.Serializer() { + @Override + public void write(Kryo kryo, Output output, Object object) { + if (object instanceof com.google.protobuf.AbstractMessage) { + byte[] bytes = ((com.google.protobuf.AbstractMessage) object).toByteArray(); + output.writeInt(bytes.length, true); + output.writeBytes(bytes); + } else { + // Fallback should not happen if registered correctly, but safe to have + throw new RuntimeException( + "Unsupported Protobuf message type for custom serializer: " + object.getClass()); + } + } - // 关闭注册行为(为了开发方便,不强制要求注册类,虽然性能略低但在 RPC 场景通用性更好) - kryo.setRegistrationRequired(false); + @Override + public Object read(Kryo kryo, Input input, Class type) { + try { + int length = input.readInt(true); + byte[] bytes = input.readBytes(length); + // Use reflection to call static parseFrom(byte[]) method + // Caching the method would be even faster but this is already much faster than + // Java serialization + return type.getMethod("parseFrom", byte[].class).invoke(null, (Object) bytes); + } catch (Exception e) { + throw new RuntimeException("Failed to deserialize protobuf: " + type.getName(), e); + } + } + }; - // 对于 Protobuf 类,使用 Java 序列化作为后备方案 - kryo.addDefaultSerializer(com.google.protobuf.GeneratedMessageV3.class, JavaSerializer.class); + kryo.register(RpcRequest.class, protobufSerializer); + kryo.register(RpcResponse.class, protobufSerializer); + + // Standard registrations + kryo.register(String.class); + kryo.register(Object[].class); + kryo.register(Class[].class); return kryo; }); + // Reuse Output buffer to avoid repeated allocation of ByteArrayOutputStream + private static final ThreadLocal outputThreadLocal = ThreadLocal.withInitial(() -> new Output(4096, -1)); + @Override public byte[] serialize(Object obj) { - try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - Output output = new Output(byteArrayOutputStream)) { + Kryo kryo = kryoThreadLocal.get(); + Output output = outputThreadLocal.get(); + output.reset(); // Clear buffer for new serialization - Kryo kryo = kryoThreadLocal.get(); - // 使用 writeClassAndObject 写入类型信息和对象数据 + try { kryo.writeClassAndObject(output, obj); - - output.flush(); - log.debug("Kryo 序列化成功: {}", obj.getClass().getName()); - return byteArrayOutputStream.toByteArray(); + return output.toBytes(); } catch (Exception e) { log.error("Kryo 序列化失败: {}", obj.getClass().getName(), e); throw new RuntimeException("Kryo 序列化失败: " + obj.getClass().getName(), e); + } finally { + kryo.reset(); // Reset Kryo references } } @Override public T deserialize(byte[] bytes, Class clazz) { - try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); - Input input = new Input(byteArrayInputStream)) { - - Kryo kryo = kryoThreadLocal.get(); - // 使用 readClassAndObject 读取类型信息和对象数据 + Kryo kryo = kryoThreadLocal.get(); + // Input is lightweight, passing byte array directly + try (Input input = new Input(bytes)) { Object obj = kryo.readClassAndObject(input); - log.debug("Kryo 反序列化成功: {}", obj.getClass().getName()); return clazz.cast(obj); } catch (Exception e) { log.error("Kryo 反序列化失败, 目标类型: {}", clazz.getName(), e); throw new RuntimeException("Kryo 反序列化失败: " + clazz.getName(), e); + } finally { + kryo.reset(); // Reset Kryo references } } diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpProtocol.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpProtocol.java index 6c23ea9..ded6f54 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpProtocol.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpProtocol.java @@ -52,9 +52,10 @@ public void config(ChannelPipeline pipeline, boolean isServer, io.netty.channel. public void sendRequest(io.netty.channel.Channel channel, RpcRequest request, com.xiaoyu.rpc.core.client.NettyRpcClientHandler clientHandler) throws Exception { // HTTP 1.1 协议也复用主通道 - if (channel.pipeline().get(com.xiaoyu.rpc.core.client.NettyRpcClientHandler.class) == null) { - channel.pipeline().addLast(clientHandler); + if (channel.pipeline().get(com.xiaoyu.rpc.core.client.NettyRpcClientHandler.class) != null) { + channel.pipeline().remove(com.xiaoyu.rpc.core.client.NettyRpcClientHandler.class); } + channel.pipeline().addLast(clientHandler); channel.writeAndFlush(request).addListener(future -> { if (!future.isSuccess()) { diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpRpcDecoder.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpRpcDecoder.java index d75b987..a2cebf8 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpRpcDecoder.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpRpcDecoder.java @@ -9,6 +9,7 @@ import lombok.extern.slf4j.Slf4j; import java.util.List; + @Slf4j public class HttpRpcDecoder extends MessageToMessageDecoder { private final Serializer serializer; @@ -32,7 +33,7 @@ protected void decode(ChannelHandlerContext ctx, FullHttpMessage msg, List out) { Unpooled.wrappedBuffer(body)); // 将方法名放入 Header httpRequest.headers().set("Rpc-Method", request.getMethodName()); - log.info("使用 HTTP 协议发送请求,方法名: {},正在Encode", request.getMethodName()); + // log.info("使用 HTTP 协议发送请求,方法名: {},正在Encode", request.getMethodName()); httpRequest.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/x-rpc"); httpMessage = httpRequest; } else { diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java index 7add58f..6476ff9 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java @@ -26,22 +26,30 @@ @Slf4j public class NettyTransportClient implements TransportClient { - private static final EventLoopGroup eventLoopGroup; - private static final Bootstrap bootstrap; + private static volatile EventLoopGroup eventLoopGroup; + private static volatile Bootstrap bootstrap; - static { - eventLoopGroup = new NioEventLoopGroup(); - bootstrap = new Bootstrap(); - bootstrap.group(eventLoopGroup) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) { - String protocolName = RpcConfig.getInstance().getProtocol(); - Protocol protocol = ProtocolFactory.getProtocol(protocolName); - protocol.config(ch.pipeline(), false, null); - } - }); + private static Bootstrap getBootstrap() { + if (bootstrap == null) { + synchronized (NettyTransportClient.class) { + if (bootstrap == null) { + eventLoopGroup = new NioEventLoopGroup(); + Bootstrap newBootstrap = new Bootstrap(); + newBootstrap.group(eventLoopGroup) + .channel(NioSocketChannel.class) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel ch) { + String protocolName = RpcConfig.getInstance().getProtocol(); + Protocol protocol = ProtocolFactory.getProtocol(protocolName); + protocol.config(ch.pipeline(), false, null); + } + }); + bootstrap = newBootstrap; + } + } + } + return bootstrap; } @Override @@ -51,7 +59,7 @@ public Object sendRequest(RpcRequest request, InetSocketAddress address) { try { // 使用 ChannelProvider 获取连接 - Channel channel = ChannelProvider.get(address, bootstrap); + Channel channel = ChannelProvider.get(address, getBootstrap()); if (channel == null || !channel.isActive()) { throw new RuntimeException("无法连接到服务器: " + address); } From 3fa0d0fa776b9752e94cb14e08ded915373f1656 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Thu, 29 Jan 2026 11:29:19 +0800 Subject: [PATCH 16/38] =?UTF-8?q?fix:=E5=A4=9A=E8=B7=AF=E5=A4=8D=E7=94=A8?= =?UTF-8?q?=E9=87=8D=E6=9E=84=E5=92=8C=E5=BC=82=E6=AD=A5=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rpc/benchmark/ProtocolBenchmark.java | 5 +- rpc-common/src/main/proto/rpc_meta.proto | 5 ++ .../core/client/NettyRpcClientHandler.java | 46 ++++++++++++++----- .../rpc/core/protocol/http/HttpProtocol.java | 7 ++- .../core/protocol/http2/Http2Protocol.java | 33 +++++++------ .../core/protocol/netty/NettyProtocol.java | 9 ++-- .../rpc/core/server/NettyRpcHandler.java | 1 + .../transport/netty/NettyTransportClient.java | 33 +++++++++---- 8 files changed, 94 insertions(+), 45 deletions(-) diff --git a/rpc-benchmark/src/main/java/com/xiaoyu/rpc/benchmark/ProtocolBenchmark.java b/rpc-benchmark/src/main/java/com/xiaoyu/rpc/benchmark/ProtocolBenchmark.java index 9043242..33ba1d0 100644 --- a/rpc-benchmark/src/main/java/com/xiaoyu/rpc/benchmark/ProtocolBenchmark.java +++ b/rpc-benchmark/src/main/java/com/xiaoyu/rpc/benchmark/ProtocolBenchmark.java @@ -25,8 +25,9 @@ @OutputTimeUnit(TimeUnit.MILLISECONDS) @State(Scope.Benchmark) @Fork(value = 1, warmups = 0) -@Warmup(iterations = 1, time = 1) -@Measurement(iterations = 2, time = 1) +@Warmup(iterations = 3, time = 1) +@Measurement(iterations = 5, time = 1) +@Threads(8) public class ProtocolBenchmark { @Param({ "netty", "http", "http2" }) diff --git a/rpc-common/src/main/proto/rpc_meta.proto b/rpc-common/src/main/proto/rpc_meta.proto index a047375..614e421 100644 --- a/rpc-common/src/main/proto/rpc_meta.proto +++ b/rpc-common/src/main/proto/rpc_meta.proto @@ -21,6 +21,9 @@ message RpcRequest { // Proto存不了Object,必须存成二进制(bytes)。 // 这里用 repeated 表示数组 repeated bytes parameters = 4; + + // 请求ID,用于多路复用 + string request_id = 5; } // 4. 对应你的 RpcResponse @@ -32,6 +35,8 @@ message RpcResponse { // 对应 String message string message = 2; + // 请求ID,用于多路复用 + string request_id = 3; } service GrpcService { diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/client/NettyRpcClientHandler.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/client/NettyRpcClientHandler.java index 4762c73..ee82b27 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/client/NettyRpcClientHandler.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/client/NettyRpcClientHandler.java @@ -11,32 +11,54 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.netty.channel.ChannelHandler; + // 这是一个 Netty 的 Handler,专门负责“收信” +@ChannelHandler.Sharable public class NettyRpcClientHandler extends SimpleChannelInboundHandler { private static final Logger log = LoggerFactory.getLogger(NettyRpcClientHandler.class); - private CompletableFuture future; + // Key: RequestId, Value: Future + private final java.util.Map> pendingRequests = new java.util.concurrent.ConcurrentHashMap<>(); + + public void addFuture(String requestId, CompletableFuture future) { + pendingRequests.put(requestId, future); + } - public void setFuture(CompletableFuture future) { - this.future = future; + public void removeFuture(String requestId) { + pendingRequests.remove(requestId); + } + + public void failRequest(String requestId, Throwable cause) { + CompletableFuture future = pendingRequests.remove(requestId); + if (future != null) { + future.completeExceptionally(cause); + } } @Override protected void channelRead0(ChannelHandlerContext ctx, RpcResponse response) { - // 【关键修复点】 - // 之前你写的是 future.complete(response.getData()); 导致传回去的是 ByteString - // 现在我们把整个 response 对象传回去,让 Proxy 去判断状态和拆包 - log.info("客户端收到响应状态: {}", response.getMessage()); - future.complete(response); + String requestId = response.getRequestId(); + CompletableFuture future = pendingRequests.remove(requestId); + + if (future != null) { + log.info("Client received response for requestId: {}, status: {}", requestId, response.getMessage()); + future.complete(response); + } else { + log.warn("Client received response for unknown or timed-out requestId: {}", requestId); + } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); + log.error("Client caught exception", cause); + // Fail all pending requests + for (CompletableFuture future : pendingRequests.values()) { + future.completeExceptionally(cause); + } + pendingRequests.clear(); ctx.close(); } - public CompletableFuture getFuture() { - return future; - } + // public CompletableFuture getFuture() { ... } // Removed single getter } \ No newline at end of file diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpProtocol.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpProtocol.java index ded6f54..b83cb17 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpProtocol.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpProtocol.java @@ -52,14 +52,13 @@ public void config(ChannelPipeline pipeline, boolean isServer, io.netty.channel. public void sendRequest(io.netty.channel.Channel channel, RpcRequest request, com.xiaoyu.rpc.core.client.NettyRpcClientHandler clientHandler) throws Exception { // HTTP 1.1 协议也复用主通道 - if (channel.pipeline().get(com.xiaoyu.rpc.core.client.NettyRpcClientHandler.class) != null) { - channel.pipeline().remove(com.xiaoyu.rpc.core.client.NettyRpcClientHandler.class); + if (channel.pipeline().get(com.xiaoyu.rpc.core.client.NettyRpcClientHandler.class) == null) { + channel.pipeline().addLast(clientHandler); } - channel.pipeline().addLast(clientHandler); channel.writeAndFlush(request).addListener(future -> { if (!future.isSuccess()) { - clientHandler.getFuture().completeExceptionally(future.cause()); + clientHandler.failRequest(request.getRequestId(), future.cause()); } }); } diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http2/Http2Protocol.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http2/Http2Protocol.java index d3f4613..5709daa 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http2/Http2Protocol.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http2/Http2Protocol.java @@ -86,22 +86,29 @@ public void sendRequest(io.netty.channel.Channel channel, RpcRequest request, RpcConfig rpcConfig = RpcConfig.getInstance(); Serializer serializer = SerializerCode.getSerializerByCode(rpcConfig.getSerializerCode()); - // 使用 bootstrap 创建新流 + // 使用 bootstrap 异步创建新流 io.netty.handler.codec.http2.Http2StreamChannelBootstrap streamBootstrap = new io.netty.handler.codec.http2.Http2StreamChannelBootstrap( channel); - Http2StreamChannel streamChannel = streamBootstrap.open().get(5, java.util.concurrent.TimeUnit.SECONDS); - - // 在流通道中构建完整的处理链 - streamChannel.pipeline().addLast(new Http2StreamFrameToHttpObjectCodec(false)); - streamChannel.pipeline().addLast(new io.netty.handler.codec.http.HttpObjectAggregator(512 * 1024)); - streamChannel.pipeline().addLast(new HttpRpcEncoder(serializer)); - streamChannel.pipeline().addLast(new HttpRpcDecoder(serializer, RpcResponse.class)); - streamChannel.pipeline().addLast(clientHandler); - - streamChannel.writeAndFlush(request).addListener(future -> { - if (!future.isSuccess()) { - clientHandler.getFuture().completeExceptionally(future.cause()); + + streamBootstrap.open().addListener(f -> { + if (!f.isSuccess()) { + clientHandler.failRequest(request.getRequestId(), f.cause()); + return; } + + Http2StreamChannel streamChannel = (Http2StreamChannel) f.getNow(); + // 在流通道中构建完整的处理链 + streamChannel.pipeline().addLast(new Http2StreamFrameToHttpObjectCodec(false)); + streamChannel.pipeline().addLast(new io.netty.handler.codec.http.HttpObjectAggregator(512 * 1024)); + streamChannel.pipeline().addLast(new HttpRpcEncoder(serializer)); + streamChannel.pipeline().addLast(new HttpRpcDecoder(serializer, RpcResponse.class)); + streamChannel.pipeline().addLast(clientHandler); + + streamChannel.writeAndFlush(request).addListener(writeFuture -> { + if (!writeFuture.isSuccess()) { + clientHandler.failRequest(request.getRequestId(), writeFuture.cause()); + } + }); }); } } \ No newline at end of file diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/NettyProtocol.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/NettyProtocol.java index ad54936..5087983 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/NettyProtocol.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/NettyProtocol.java @@ -43,16 +43,13 @@ public void config(ChannelPipeline pipeline, boolean isServer, io.netty.channel. public void sendRequest(io.netty.channel.Channel channel, RpcRequest request, com.xiaoyu.rpc.core.client.NettyRpcClientHandler clientHandler) throws Exception { // Netty 协议直接复用主通道 - // 如果 pipeline 里还没有 handler (第一次),加上它 - if (channel.pipeline().get(com.xiaoyu.rpc.core.client.NettyRpcClientHandler.class) != null) { - channel.pipeline().replace(com.xiaoyu.rpc.core.client.NettyRpcClientHandler.class, "handler", clientHandler); - } else { - channel.pipeline().addLast("handler", clientHandler); + if (channel.pipeline().get(com.xiaoyu.rpc.core.client.NettyRpcClientHandler.class) == null) { + channel.pipeline().addLast(clientHandler); } channel.writeAndFlush(request).addListener(future -> { if (!future.isSuccess()) { - clientHandler.getFuture().completeExceptionally(future.cause()); + clientHandler.failRequest(request.getRequestId(), future.cause()); } }); } diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/server/NettyRpcHandler.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/server/NettyRpcHandler.java index c2eaea2..4438ef0 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/server/NettyRpcHandler.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/server/NettyRpcHandler.java @@ -25,6 +25,7 @@ public class NettyRpcHandler extends SimpleChannelInboundHandler { @Override protected void channelRead0(ChannelHandlerContext ctx, RpcRequest request) throws Exception { RpcResponse.Builder responseBuilder = RpcResponse.newBuilder(); + responseBuilder.setRequestId(request.getRequestId()); try { // 1. 获取实现类 (从 ServiceRepository 获取) diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java index 6476ff9..ca23c51 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java @@ -54,7 +54,6 @@ protected void initChannel(SocketChannel ch) { @Override public Object sendRequest(RpcRequest request, InetSocketAddress address) { - NettyRpcClientHandler clientHandler = new NettyRpcClientHandler(); String protocolName = RpcConfig.getInstance().getProtocol(); try { @@ -64,11 +63,34 @@ public Object sendRequest(RpcRequest request, InetSocketAddress address) { throw new RuntimeException("无法连接到服务器: " + address); } + // Reuse handler from pipeline + NettyRpcClientHandler clientHandler = channel.pipeline().get(NettyRpcClientHandler.class); + if (clientHandler == null) { + // Should be added by initChannel, but for safety in some custom protocols: + clientHandler = new NettyRpcClientHandler(); + channel.pipeline().addLast(clientHandler); + } + + // Generate ID and set to request + String requestId = java.util.UUID.randomUUID().toString(); + // Use reflection or builder if setter not available, but since we regenerated + // proto, we should use builder properly + // However, RpcRequest is immutable if generated by Proto? + // Wait, RpcRequest.java is generated. It has a Builder. + // But the signature of sendRequest takes RpcRequest, which is already built. + // We need to rebuild it with the ID. + + RpcRequest.Builder builder = request.toBuilder(); + builder.setRequestId(requestId); + RpcRequest newRequest = builder.build(); + CompletableFuture resultFuture = new CompletableFuture<>(); - clientHandler.setFuture(resultFuture); + clientHandler.addFuture(requestId, resultFuture); Protocol protocol = ProtocolFactory.getProtocol(protocolName); - protocol.sendRequest(channel, request, clientHandler); + // Pass the handler so protocol can ensure it's in pipeline if needed (double + // check) + protocol.sendRequest(channel, newRequest, clientHandler); Object result = resultFuture.get(5, TimeUnit.SECONDS); @@ -78,11 +100,6 @@ public Object sendRequest(RpcRequest request, InetSocketAddress address) { if (!"Success".equals(rpcResponse.getMessage())) { throw new RuntimeException("服务端报错: " + rpcResponse.getMessage()); } - - // 返回 Data 的 bytes,由上层可以通过配置的 Serializer 进行反序列化 - // 这里为了保持兼容性,还是直接返回 Response 对象或者 Data? - // 根据 RpcClient 的逻辑,它是在这里反序列化的。 - // 此时我们其实只负责传输,最好返回 RpcResponse 对象。 return rpcResponse; } else { From ef72b2739627c35b2d3939105511401bc83d0c2c Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Thu, 29 Jan 2026 11:50:34 +0800 Subject: [PATCH 17/38] =?UTF-8?q?README=EF=BC=9A=E6=9B=B4=E6=96=B0readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 35 ++++++++++++++++++++++++++++++++++- README_ZH.md | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0a769a4..a4ce88e 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ The project is organized into the following modules to ensure separation of conc | **`rpc-transport-netty`** | The default transport implementation based on **Netty**. | | **`rpc-provider`** | Example provider application that implements and exports services. | | **`rpc-consumer`** | Example consumer application that imports and invokes services. | +| **`rpc-benchmark`** | Performance benchmarking module using JMH (Java Microbenchmark Harness). | | **`python_client`** | Python client implementation demonstrating cross-language gRPC interoperability. | ## ✨ Key Features @@ -42,7 +43,8 @@ The project is organized into the following modules to ensure separation of conc - **📡 Multi-Protocol Support**: Choice of `Netty` (Custom), `HTTP/1.1`, or `gRPC` (HTTP/2) for communication. - **⚡ High-Performance Proxy**: Uses **ByteBuddy** for dynamic proxy generation, optimized for Java 17+. - **⚖️ Intelligent Load Balancing**: Includes `RoundRobin` and `Random` strategies. -- **📦 Diverse Serialization**: Supports `Protobuf` (Enhanced with Scalar Wrappers), `Kryo`, `JSON`, and standard `Java` serialization. +- **📦 Diverse Serialization**: Supports `Protobuf` (Enhanced with Scalar Wrappers), **Kryo** (Optimized), `JSON`, and standard `Java` serialization. +- **🔄 Request Multiplexing**: True asynchronous request/response correlation using `request_id`, enabling a single connection to handle thousands of concurrent streams (especially for HTTP/2). - **🔍 Service Discovery**: Integrated with **Nacos** for robust service registry and discovery. --- @@ -97,6 +99,37 @@ Run the full integration test suite: mvn test -pl rpc-consumer -Dtest=FullIntegrationTest ``` +### 5. Performance & Benchmark Results + +XiaoYu RPC is designed for high performance. Below are the verified results from our JMH benchmark suite. + +#### 5.1 Protocol Performance (Throughput & Latency) +Tested with **8 concurrent threads** on local loopback (127.0.0.1). + +| Protocol | Throughput (ops/ms) | Latency (ms/op) | Characteristics | +| :--- | :--- | :--- | :--- | +| **Netty (Custom)** | **84.245** | **0.093** | **Champion.** Pure binary, minimal overhead. | +| **HTTP/1.1** | 76.853 | 0.104 | Robust, but limited by serial processing per connection. | +| **HTTP/2** | 58.847 | 0.137 | **Multiplexing Power.** Higher overhead but stable under load. | + +> [!TIP] +> **Why HTTP/2?** +> While HTTP/1.1 is slightly faster in zero-latency local loopback tests due to its simplicity, HTTP/2's **Multiplexing** allows it to handle massive concurrent requests over a single connection without Head-of-Line (HoL) blocking, which is critical for real-world distributed systems. + +#### 5.2 Serialization Efficiency +Comparison of processing a standard POJO (`RpcRequest`). + +| Serializer | Throughput (ops/us) | Latency (us/op) | Payload Size | +| :--- | :---: | :---: | :---: | +| **Protobuf** | **34.429** | **0.029** | **65 bytes** | +| **Kryo (Optimized)** | 11.932 | 0.066 | 68 bytes | +| **JSON** | 2.050 | 0.497 | 231 bytes | +| **Java** | 1.102 | 0.895 | 652 bytes | + +**Key Insights:** +- **Protobuf vs. Java**: Protobuf is **77x faster** and **10x smaller** than standard Java serialization. +- **Binary vs. Text**: Kryo (Binary) provides **6x higher throughput** than JSON (Text) for complex objects due to Varint compression and omission of field names. + --- ## 🛠️ Configuration diff --git a/README_ZH.md b/README_ZH.md index 80a17e3..b068dce 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -30,6 +30,7 @@ | **`rpc-transport-netty`** | 基于 **Netty** 实现的默认传输层模块。 | | **`rpc-provider`** | 示例服务提供者应用,用于实现并暴露服务。 | | **`rpc-consumer`** | 示例服务消费者应用,用于引入并调用服务。 | +| **`rpc-benchmark`** | 基于 JMH (Java Microbenchmark Harness) 的性能基准测试模块。 | | **`python_client`** | Python 客户端实现,用于演示跨语言 gRPC 互操作性。 | ## ✨ 核心特性 @@ -41,7 +42,8 @@ - **📡 多协议支持**: 支持 `Netty` (自定义), `HTTP/1.1`, 或 `gRPC` (HTTP/2) 通信协议。 - **⚡ 高性能代理**: 使用 **ByteBuddy** 生成动态代理,针对 Java 17+ 进行了优化。 - **⚖️ 智能负载均衡**: 内置 `RoundRobin` (轮询) 和 `Random` (随机) 策略。 -- **📦 多样化序列化**: 支持 `Protobuf` (增强版), `Kryo`, `JSON`, 以及标准 `Java` 序列化。 +- **📦 多样化序列化**: 支持 `Protobuf` (增强版), **Kryo** (针对 POJO 优化), `JSON`, 以及标准 `Java` 序列化。 +- **🔄 请求多路复用**: 真正的异步请求/响应关联,通过 `request_id` 实现单条连接处理数千个并发流(尤其在 HTTP/2 下性能卓越)。 - **🔍 服务发现**: 集成 **Nacos** 实现健壮的服务注册与发现。 --- @@ -135,6 +137,36 @@ mvn test -pl rpc-core,rpc-transport-netty mvn test -pl rpc-consumer -Dtest=FullIntegrationTest ``` +### 5. 基准测试与性能结果 + +XiaoYu RPC 专注于极致性能。以下是使用 **JMH** 测得的真实数据(基于 8 线程并发,本地回路 127.0.0.1 压测)。 + +#### 5.1 协议性能对比(吞吐量与延迟) + +| 通信协议 | 吞吐量 (ops/ms) | 平均延迟 (ms/op) | 性能简评 | +| :--- | :--- | :--- | :--- | +| **Netty (自定义)** | **84.245** | **0.093** | **性能冠军。** 纯二进制协议,开销极小。 | +| **HTTP/1.1** | 76.853 | 0.104 | 表现稳健,但在单连接高并发下受限于串行处理。 | +| **HTTP/2** | 58.847 | 0.137 | **多路复用利器。** 虽然协议头较重,但支持高并发流。 | + +> [!TIP] +> **为什么多路复用很重要?** +> 在零延迟的本地测试中,简单的 HTTP/1.1 略快;但在真实生产环境下,由于网络波动和高延迟,HTTP/2 通过消除**队头阻塞 (HoL)** 能显著提升系统吞吐量和稳定性。 + +#### 5.2 序列化效率对比 +针对标准 POJO 对象 (`RpcRequest`) 的处理能力。 + +| 序列化器 | 吞吐量 (ops/us) | 延迟 (us/op) | 报文大小 | +| :--- | :---: | :---: | :---: | +| **Protobuf** | **34.429** | **0.029** | **65 bytes** | +| **Kryo (已优化)** | 11.932 | 0.066 | 68 bytes | +| **JSON** | 2.050 | 0.497 | 231 bytes | +| **Java** | 1.102 | 0.895 | 652 bytes | + +**核心洞察:** +- **Protobuf vs. Java**: Protobuf 的处理速度比 Java 原生序列化快 **77 倍**,且体积缩小了 **10 倍**。 +- **二进制 vs. 文本**: 在处理复杂 POJO 时,二进制协议 (Kryo/Protobuf) 的吞吐量比文本协议 (JSON) 高出约 **6 倍**,这归功于 Varint 压缩和去除字段名存储。 + --- ## 🛠️ 配置手册 From 5b7f27bd8d8eac5d3cf846fc3b42060f9ea07fb3 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Sun, 1 Feb 2026 13:13:34 +0800 Subject: [PATCH 18/38] =?UTF-8?q?feat:=E5=BC=95=E5=85=A5go=E7=9A=84?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 25 ++-- README_ZH.md | 23 ++- go_client/go.mod | 17 +++ go_client/go.sum | 34 +++++ go_client/main.go | 69 +++++++++ go_client/pb/rpc_meta.pb.go | 249 +++++++++++++++++++++++++++++++ go_client/pb/rpc_meta_grpc.pb.go | 121 +++++++++++++++ go_client/proto/rpc_meta.proto | 45 ++++++ go_client/run_client.sh | 7 + 9 files changed, 575 insertions(+), 15 deletions(-) create mode 100644 go_client/go.mod create mode 100644 go_client/go.sum create mode 100644 go_client/main.go create mode 100644 go_client/pb/rpc_meta.pb.go create mode 100644 go_client/pb/rpc_meta_grpc.pb.go create mode 100644 go_client/proto/rpc_meta.proto create mode 100644 go_client/run_client.sh diff --git a/README.md b/README.md index a4ce88e..ffdf88e 100644 --- a/README.md +++ b/README.md @@ -257,9 +257,9 @@ rpc: --- -## 🐍 Cross-Language gRPC Support (Python) +## 🌐 Multi-Language gRPC Support (Python & Go) -This framework supports interoperability with standard gRPC clients (e.g., Python), allowing non-Java clients to invoke services hosted by the RPC framework. +This framework supports interoperability with standard gRPC clients (e.g., Python, Go), allowing non-Java clients to invoke services hosted by the RPC framework. ### Features @@ -289,27 +289,28 @@ This framework supports interoperability with standard gRPC clients (e.g., Pytho java -cp rpc-provider/target/rpc-provider-1.0-SNAPSHOT.jar:rpc-transport-netty/target/rpc-transport-netty-1.0-SNAPSHOT.jar:rpc-core/target/rpc-core-1.0-SNAPSHOT.jar:rpc-common/target/rpc-common-1.0-SNAPSHOT.jar:rpc-api/target/rpc-api-1.0-SNAPSHOT.jar:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout -pl rpc-provider -am) com.xiaoyu.rpc.provider.ProviderApp ``` 3. **Run the Python Client**: - Navigate to the `python_client` directory and set up the environment: + Refer to [python_client/client.py](file:///Users/yaoyao/Dev/JAVA_Dev_Project/gRPC/python_client/client.py) for details. ```bash cd python_client + # ... (existing steps) + python3 client.py + ``` - # Create and valid virtual environment - python3 -m venv venv - source venv/bin/activate - - # Install dependencies - pip install grpcio grpcio-tools protobuf +4. **Run the Go Client**: + Navigate to the `go_client` directory and run: - # Run the client - python3 client.py + ```bash + cd go_client + go run main.go ``` **Expected Output**: ```text + Sending RpcRequest: interface=com.xiaoyu.rpc.api.HelloService, method=sayHello, param=World RpcResponse received: - Data: Hello, World! (from Multi-Module Netty Server) + Data: Hello, World! Message: Success ``` ## 🔄 Continuous Integration & Delivery (CI/CD) diff --git a/README_ZH.md b/README_ZH.md index b068dce..f66d6f6 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -260,9 +260,9 @@ rpc: --- -## 🐍 跨语言 gRPC 支持 (Python) +## 🌐 多语言 gRPC 支持 (Python & Go) -本框架支持与标准 gRPC 客户端(如 Python)互操作,允许非 Java 客户端调用本框架托管的服务。 +本框架支持与标准 gRPC 客户端(如 Python, Go)互操作,允许非 Java 客户端调用本框架托管的服务。 ### 特性 @@ -293,7 +293,7 @@ rpc: ``` 3. **运行 Python 客户端**: - 进入 `python_client` 目录并配置环境: + 详情请参考 [python_client/client.py](file:///Users/yaoyao/Dev/JAVA_Dev_Project/gRPC/python_client/client.py)。 ```bash cd python_client @@ -317,6 +317,23 @@ rpc: Message: Success ``` +4. **运行 Go 客户端**: + 进入 `go_client` 目录并运行: + + ```bash + cd go_client + go run main.go + ``` + + **预期输出**: + + ```text + Sending RpcRequest: interface=com.xiaoyu.rpc.api.HelloService, method=sayHello, param=World + RpcResponse received: + Data: Hello, World! + Message: Success + ``` + ## 🔄 持续集成与交付 (CI/CD) 为了确保系统可靠性和代码质量,本项目集成了一套基于 **GitHub Actions** 的健壮 CI/CD 流水线。 diff --git a/go_client/go.mod b/go_client/go.mod new file mode 100644 index 0000000..697a3e7 --- /dev/null +++ b/go_client/go.mod @@ -0,0 +1,17 @@ +module go_client + +go 1.25.6 + +require ( + google.golang.org/grpc v1.70.0 + google.golang.org/protobuf v1.36.11 +) + +require ( + go.opentelemetry.io/otel v1.34.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect + golang.org/x/net v0.38.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect +) diff --git a/go_client/go.sum b/go_client/go.sum new file mode 100644 index 0000000..29fdc61 --- /dev/null +++ b/go_client/go.sum @@ -0,0 +1,34 @@ +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= diff --git a/go_client/main.go b/go_client/main.go new file mode 100644 index 0000000..2f4b1b3 --- /dev/null +++ b/go_client/main.go @@ -0,0 +1,69 @@ +package main + +import ( + "context" + "fmt" + "log" + "time" + + "go_client/pb" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +func main() { + // Connect to the Java gRPC server + addr := "localhost:8080" + conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + log.Fatalf("did not connect: %v", err) + } + defer conn.Close() + + client := pb.NewGrpcServiceClient(conn) + + // Wrap the parameter in a Protobuf StringValue + param := wrapperspb.String( "World") + paramBytes, err := proto.Marshal(param) + if err != nil { + log.Fatalf("failed to marshal param: %v", err) + } + + // Prepare the RPC request + req := &pb.RpcRequest{ + InterfaceName: "com.xiaoyu.rpc.api.HelloService", + MethodName: "sayHello", + ParamTypes: []string{"java.lang.String"}, + Parameters: [][]byte{paramBytes}, + RequestId: fmt.Sprintf("go-req-%d", time.Now().UnixNano()), + } + + // Set a timeout for the call + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + + // Call the handle method + fmt.Printf("Sending RpcRequest: interface=%s, method=%s, param=%s\n", req.InterfaceName, req.MethodName, "World") + resp, err := client.Handle(ctx, req) + if err != nil { + log.Fatalf("could not call handle: %v", err) + } + + fmt.Println("RpcResponse received:") + if resp.Message == "Success" { + resultVal := &wrapperspb.StringValue{} + if err := proto.Unmarshal(resp.Data, resultVal); err != nil { + log.Printf("failed to unmarshal data: %v", err) + fmt.Printf("Data (Raw): %v\n", resp.Data) + } else { + fmt.Printf("Data: %s\n", resultVal.Value) + } + } else { + fmt.Printf("Data (Raw): %v\n", resp.Data) + } + fmt.Printf("Message: %s\n", resp.Message) + fmt.Printf("RequestID: %s\n", resp.RequestId) +} diff --git a/go_client/pb/rpc_meta.pb.go b/go_client/pb/rpc_meta.pb.go new file mode 100644 index 0000000..e5c6cae --- /dev/null +++ b/go_client/pb/rpc_meta.pb.go @@ -0,0 +1,249 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v6.33.0 +// source: rpc_meta.proto + +package pb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// 3. 对应你的 RpcRequest +type RpcRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // 对应 String interfaceName + InterfaceName string `protobuf:"bytes,1,opt,name=interface_name,json=interfaceName,proto3" json:"interface_name,omitempty"` + // 对应 String methodName + MethodName string `protobuf:"bytes,2,opt,name=method_name,json=methodName,proto3" json:"method_name,omitempty"` + // 对应 Class[] paramTypes + // Proto存不了Class对象,只能存全类名(String),比如 "java.lang.String" + ParamTypes []string `protobuf:"bytes,3,rep,name=param_types,json=paramTypes,proto3" json:"param_types,omitempty"` + // 对应 Object[] parameters + // Proto存不了Object,必须存成二进制(bytes)。 + // 这里用 repeated 表示数组 + Parameters [][]byte `protobuf:"bytes,4,rep,name=parameters,proto3" json:"parameters,omitempty"` + // 请求ID,用于多路复用 + RequestId string `protobuf:"bytes,5,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RpcRequest) Reset() { + *x = RpcRequest{} + mi := &file_rpc_meta_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RpcRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RpcRequest) ProtoMessage() {} + +func (x *RpcRequest) ProtoReflect() protoreflect.Message { + mi := &file_rpc_meta_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RpcRequest.ProtoReflect.Descriptor instead. +func (*RpcRequest) Descriptor() ([]byte, []int) { + return file_rpc_meta_proto_rawDescGZIP(), []int{0} +} + +func (x *RpcRequest) GetInterfaceName() string { + if x != nil { + return x.InterfaceName + } + return "" +} + +func (x *RpcRequest) GetMethodName() string { + if x != nil { + return x.MethodName + } + return "" +} + +func (x *RpcRequest) GetParamTypes() []string { + if x != nil { + return x.ParamTypes + } + return nil +} + +func (x *RpcRequest) GetParameters() [][]byte { + if x != nil { + return x.Parameters + } + return nil +} + +func (x *RpcRequest) GetRequestId() string { + if x != nil { + return x.RequestId + } + return "" +} + +// 4. 对应你的 RpcResponse +type RpcResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // 对应 Object data + // 同样无法存Object,只能存序列化后的二进制 + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + // 对应 String message + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + // 请求ID,用于多路复用 + RequestId string `protobuf:"bytes,3,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RpcResponse) Reset() { + *x = RpcResponse{} + mi := &file_rpc_meta_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RpcResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RpcResponse) ProtoMessage() {} + +func (x *RpcResponse) ProtoReflect() protoreflect.Message { + mi := &file_rpc_meta_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RpcResponse.ProtoReflect.Descriptor instead. +func (*RpcResponse) Descriptor() ([]byte, []int) { + return file_rpc_meta_proto_rawDescGZIP(), []int{1} +} + +func (x *RpcResponse) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +func (x *RpcResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *RpcResponse) GetRequestId() string { + if x != nil { + return x.RequestId + } + return "" +} + +var File_rpc_meta_proto protoreflect.FileDescriptor + +const file_rpc_meta_proto_rawDesc = "" + + "\n" + + "\x0erpc_meta.proto\"\xb4\x01\n" + + "\n" + + "RpcRequest\x12%\n" + + "\x0einterface_name\x18\x01 \x01(\tR\rinterfaceName\x12\x1f\n" + + "\vmethod_name\x18\x02 \x01(\tR\n" + + "methodName\x12\x1f\n" + + "\vparam_types\x18\x03 \x03(\tR\n" + + "paramTypes\x12\x1e\n" + + "\n" + + "parameters\x18\x04 \x03(\fR\n" + + "parameters\x12\x1d\n" + + "\n" + + "request_id\x18\x05 \x01(\tR\trequestId\"Z\n" + + "\vRpcResponse\x12\x12\n" + + "\x04data\x18\x01 \x01(\fR\x04data\x12\x18\n" + + "\amessage\x18\x02 \x01(\tR\amessage\x12\x1d\n" + + "\n" + + "request_id\x18\x03 \x01(\tR\trequestId22\n" + + "\vGrpcService\x12#\n" + + "\x06handle\x12\v.RpcRequest\x1a\f.RpcResponseB*\n" + + "\x18com.xiaoyu.rpc.common.voP\x01Z\fgo_client/pbb\x06proto3" + +var ( + file_rpc_meta_proto_rawDescOnce sync.Once + file_rpc_meta_proto_rawDescData []byte +) + +func file_rpc_meta_proto_rawDescGZIP() []byte { + file_rpc_meta_proto_rawDescOnce.Do(func() { + file_rpc_meta_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_rpc_meta_proto_rawDesc), len(file_rpc_meta_proto_rawDesc))) + }) + return file_rpc_meta_proto_rawDescData +} + +var file_rpc_meta_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_rpc_meta_proto_goTypes = []any{ + (*RpcRequest)(nil), // 0: RpcRequest + (*RpcResponse)(nil), // 1: RpcResponse +} +var file_rpc_meta_proto_depIdxs = []int32{ + 0, // 0: GrpcService.handle:input_type -> RpcRequest + 1, // 1: GrpcService.handle:output_type -> RpcResponse + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_rpc_meta_proto_init() } +func file_rpc_meta_proto_init() { + if File_rpc_meta_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_rpc_meta_proto_rawDesc), len(file_rpc_meta_proto_rawDesc)), + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_rpc_meta_proto_goTypes, + DependencyIndexes: file_rpc_meta_proto_depIdxs, + MessageInfos: file_rpc_meta_proto_msgTypes, + }.Build() + File_rpc_meta_proto = out.File + file_rpc_meta_proto_goTypes = nil + file_rpc_meta_proto_depIdxs = nil +} diff --git a/go_client/pb/rpc_meta_grpc.pb.go b/go_client/pb/rpc_meta_grpc.pb.go new file mode 100644 index 0000000..cb68918 --- /dev/null +++ b/go_client/pb/rpc_meta_grpc.pb.go @@ -0,0 +1,121 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.0 +// - protoc v6.33.0 +// source: rpc_meta.proto + +package pb + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + GrpcService_Handle_FullMethodName = "/GrpcService/handle" +) + +// GrpcServiceClient is the client API for GrpcService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type GrpcServiceClient interface { + Handle(ctx context.Context, in *RpcRequest, opts ...grpc.CallOption) (*RpcResponse, error) +} + +type grpcServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewGrpcServiceClient(cc grpc.ClientConnInterface) GrpcServiceClient { + return &grpcServiceClient{cc} +} + +func (c *grpcServiceClient) Handle(ctx context.Context, in *RpcRequest, opts ...grpc.CallOption) (*RpcResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(RpcResponse) + err := c.cc.Invoke(ctx, GrpcService_Handle_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// GrpcServiceServer is the server API for GrpcService service. +// All implementations must embed UnimplementedGrpcServiceServer +// for forward compatibility. +type GrpcServiceServer interface { + Handle(context.Context, *RpcRequest) (*RpcResponse, error) + mustEmbedUnimplementedGrpcServiceServer() +} + +// UnimplementedGrpcServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedGrpcServiceServer struct{} + +func (UnimplementedGrpcServiceServer) Handle(context.Context, *RpcRequest) (*RpcResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Handle not implemented") +} +func (UnimplementedGrpcServiceServer) mustEmbedUnimplementedGrpcServiceServer() {} +func (UnimplementedGrpcServiceServer) testEmbeddedByValue() {} + +// UnsafeGrpcServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to GrpcServiceServer will +// result in compilation errors. +type UnsafeGrpcServiceServer interface { + mustEmbedUnimplementedGrpcServiceServer() +} + +func RegisterGrpcServiceServer(s grpc.ServiceRegistrar, srv GrpcServiceServer) { + // If the following call panics, it indicates UnimplementedGrpcServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&GrpcService_ServiceDesc, srv) +} + +func _GrpcService_Handle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RpcRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GrpcServiceServer).Handle(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GrpcService_Handle_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GrpcServiceServer).Handle(ctx, req.(*RpcRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// GrpcService_ServiceDesc is the grpc.ServiceDesc for GrpcService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var GrpcService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "GrpcService", + HandlerType: (*GrpcServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "handle", + Handler: _GrpcService_Handle_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "rpc_meta.proto", +} diff --git a/go_client/proto/rpc_meta.proto b/go_client/proto/rpc_meta.proto new file mode 100644 index 0000000..0f7e9ee --- /dev/null +++ b/go_client/proto/rpc_meta.proto @@ -0,0 +1,45 @@ +syntax = "proto3"; + +option java_package = "com.xiaoyu.rpc.common.vo";//对应的路径名称 + +// 2. 必须设为 true,这样才会生成 RpcRequest.java 和 RpcResponse.java 两个文件 +option java_multiple_files = true; +option go_package = "go_client/pb"; + +// 3. 对应你的 RpcRequest +message RpcRequest { + // 对应 String interfaceName + string interface_name = 1; + + // 对应 String methodName + string method_name = 2; + + // 对应 Class[] paramTypes + // Proto存不了Class对象,只能存全类名(String),比如 "java.lang.String" + repeated string param_types = 3; + + // 对应 Object[] parameters + // Proto存不了Object,必须存成二进制(bytes)。 + // 这里用 repeated 表示数组 + repeated bytes parameters = 4; + + // 请求ID,用于多路复用 + string request_id = 5; +} + +// 4. 对应你的 RpcResponse +message RpcResponse { + // 对应 Object data + // 同样无法存Object,只能存序列化后的二进制 + bytes data = 1; + + // 对应 String message + string message = 2; + + // 请求ID,用于多路复用 + string request_id = 3; +} + +service GrpcService { + rpc handle (RpcRequest) returns (RpcResponse); +} \ No newline at end of file diff --git a/go_client/run_client.sh b/go_client/run_client.sh new file mode 100644 index 0000000..c0045e5 --- /dev/null +++ b/go_client/run_client.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Ensure dependencies are installed +go mod tidy + +# Run the Go client +go run main.go From 154a21ae9c41ec17dbdab05a77c6321591a314d7 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Tue, 3 Feb 2026 15:40:40 +0800 Subject: [PATCH 19/38] =?UTF-8?q?fix:ByteBuddyProxyFactory=E5=88=9B?= =?UTF-8?q?=E5=BB=BARpcClient=E7=9A=84=E6=97=B6=E5=80=99=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?=E5=A4=8D=E7=94=A8=EF=BC=8C=E5=87=8F=E5=B0=8F=E4=BA=86=E5=AF=B9?= =?UTF-8?q?=E8=B1=A1=E5=8F=8D=E5=A4=8D=E8=BF=9B=E8=A1=8C=E5=88=86=E9=85=8D?= =?UTF-8?q?(=E5=B0=A4=E5=85=B6=E6=98=AF=E5=9C=A8=E9=AB=98=E9=A2=91?= =?UTF-8?q?=E7=9A=84RPC=E5=9C=BA=E6=99=AF=E4=B8=8B)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/client/ByteBuddyProxyFactory.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/ByteBuddyProxyFactory.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/ByteBuddyProxyFactory.java index 6db82bb..7ceb563 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/ByteBuddyProxyFactory.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/ByteBuddyProxyFactory.java @@ -14,13 +14,17 @@ public class ByteBuddyProxyFactory implements ProxyFactory { + private final RpcClient rpcClient; + + public ByteBuddyProxyFactory() { + this.rpcClient = new RpcClient(); + } + @Override @SuppressWarnings("unchecked") public T getProxy(Class clazz) { try { - return (T) new ByteBuddy() - .subclass(clazz) - .method(ElementMatchers.any()) + return (T) new ByteBuddy().subclass(clazz).method(ElementMatchers.any()) .intercept(InvocationHandlerAdapter.of(new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { @@ -46,14 +50,9 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl } RpcRequest request = builder.build(); - return new RpcClient().sendRequest(request, method.getReturnType()); + return rpcClient.sendRequest(request, method.getReturnType()); } - })) - .make() - .load(clazz.getClassLoader()) - .getLoaded() - .getConstructor() - .newInstance(); + })).make().load(clazz.getClassLoader()).getLoaded().getConstructor().newInstance(); } catch (Exception e) { throw new RuntimeException("ByteBuddy代理创建失败", e); } From 6b5202f3c120b98c52042cb436f17630b6552eb8 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Tue, 3 Feb 2026 15:45:27 +0800 Subject: [PATCH 20/38] =?UTF-8?q?feat:=20=E5=BC=95=E5=85=A5=E6=9C=80?= =?UTF-8?q?=E5=A4=A7=E6=8A=A5=E6=96=87=E9=95=BF=E5=BA=A6=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=EF=BC=8C=E9=98=B2=E5=BE=A1=E6=81=B6=E6=84=8F=E6=8A=A5=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/xiaoyu/rpc/core/config/RpcConfig.java | 13 +++++++++++-- rpc-core/src/main/resources/rpc-config.yaml | 1 + .../rpc/core/protocol/netty/MyRpcDecoder.java | 6 ++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/config/RpcConfig.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/config/RpcConfig.java index bea6939..5ea7c7c 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/config/RpcConfig.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/config/RpcConfig.java @@ -34,6 +34,8 @@ public class RpcConfig { private String loadBalancer = "roundrobin"; // 传输层类型 private String transport = "netty"; + // 最大报文长度 + private Integer maxMessageSize = 8 * 1024 * 1024; private RpcConfig() { loadConfig(); @@ -74,10 +76,11 @@ private void loadConfig() { this.proxyType = (String) rpcConfig.getOrDefault("proxy", "jdk"); this.loadBalancer = (String) rpcConfig.getOrDefault("load-balancer", "roundrobin"); this.transport = (String) rpcConfig.getOrDefault("transport", "netty"); + this.maxMessageSize = (Integer) rpcConfig.getOrDefault("max-message-size", 8 * 1024 * 1024); - log.info("配置加载成功: 序列化方式={}, 服务器={}:{},使用的协议={}, 注册中心={}, 代理方式={}, 负载均衡={}, 传输层={}", + log.info("配置加载成功: 序列化方式={}, 服务器={}:{},使用的协议={}, 注册中心={}, 代理方式={}, 负载均衡={}, 传输层={}, 最大报文={}", serializerType, serverHost, serverPort, protocol, registryAddress, proxyType, loadBalancer, - transport); + transport, maxMessageSize); } else { log.warn("配置文件格式错误,使用默认配置"); setDefaultConfig(); @@ -126,6 +129,7 @@ private void setDefaultConfig() { this.serverHost = "127.0.0.1"; this.protocol = "netty"; this.transport = "netty"; + this.maxMessageSize = 8 * 1024 * 1024; } /** @@ -173,6 +177,10 @@ public String getTransport() { return transport; } + public Integer getMaxMessageSize() { + return maxMessageSize; + } + @Override public String toString() { return "RpcConfig{" + @@ -185,6 +193,7 @@ public String toString() { ", proxyType='" + proxyType + '\'' + ", loadBalancer='" + loadBalancer + '\'' + ", transport='" + transport + '\'' + + ", maxMessageSize=" + maxMessageSize + '}'; } } diff --git a/rpc-core/src/main/resources/rpc-config.yaml b/rpc-core/src/main/resources/rpc-config.yaml index 89f7256..819268c 100644 --- a/rpc-core/src/main/resources/rpc-config.yaml +++ b/rpc-core/src/main/resources/rpc-config.yaml @@ -7,3 +7,4 @@ rpc: serializer: "protobuf" proxy: "bytebuddy" load-balancer: "roundrobin" + max-message-size: 8388608 # 8MB diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcDecoder.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcDecoder.java index 6873329..dd1cf0b 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcDecoder.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcDecoder.java @@ -43,6 +43,12 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { // 4. 读取 Body 长度 int length = in.readInt(); + int maxFrameSize = com.xiaoyu.rpc.core.config.RpcConfig.getInstance().getMaxMessageSize(); + if (length > maxFrameSize || length < 0) { + log.error("拒绝过大的报文或非法长度: {} bytes, 远程地址: {}", length, ctx.channel().remoteAddress()); + ctx.close(); // 直接断开物理连接,防止持续攻击 + throw new RuntimeException("拒绝过大的报文: " + length); + } // 5. 读取 Body 数据 byte[] body = new byte[length]; From d726c31b62850f412ccd6cb35ac550f964e34ae3 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Tue, 3 Feb 2026 16:07:14 +0800 Subject: [PATCH 21/38] =?UTF-8?q?feat:=20=E5=B0=86=20RPC=20=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E5=A4=84=E7=90=86=E6=94=B9=E4=B8=BA=E5=BC=82=E6=AD=A5?= =?UTF-8?q?=20CompletableFuture=20=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/client/ByteBuddyProxyFactory.java | 2 + .../com/xiaoyu/rpc/core/client/RpcClient.java | 45 +++++++++---------- .../rpc/core/transport/TransportClient.java | 4 +- .../transport/netty/NettyTransportClient.java | 39 +++++++--------- 4 files changed, 42 insertions(+), 48 deletions(-) diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/ByteBuddyProxyFactory.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/ByteBuddyProxyFactory.java index 7ceb563..4a07a5e 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/ByteBuddyProxyFactory.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/ByteBuddyProxyFactory.java @@ -50,6 +50,8 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl } RpcRequest request = builder.build(); + // 注意:这里直接返回 Future。 + // 此时要求业务接口 Method 的返回类型必须是 CompletableFuture,否则会发生类型转换异常。 return rpcClient.sendRequest(request, method.getReturnType()); } })).make().load(clazz.getClassLoader()).getLoaded().getConstructor().newInstance(); diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/RpcClient.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/RpcClient.java index 61eba07..1941b96 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/RpcClient.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/RpcClient.java @@ -25,40 +25,39 @@ public RpcClient() { this.transportClient = transport.createClient(); } - public Object sendRequest(RpcRequest request, Class returnType) { + public java.util.concurrent.CompletableFuture sendRequest(RpcRequest request, Class returnType) { try { - // 1. 服务发现 + // 1. 服务发现 (同步查找,通常有本地缓存) InetSocketAddress address = serviceDiscovery.lookupService(request.getInterfaceName()); if (address == null) { - throw new RuntimeException("未发现服务: " + request.getInterfaceName()); + java.util.concurrent.CompletableFuture future = new java.util.concurrent.CompletableFuture<>(); + future.completeExceptionally(new RuntimeException("未发现服务: " + request.getInterfaceName())); + return future; } - // 2. 使用传输层发送请求 - // 注意: TransportClient 返回的是反序列化后的结果(RpcResponse)或者已经提取的数据 - // 在 NettyTransportClient 实现中,我们返回了 RpcResponse 对象 - Object result = transportClient.sendRequest(request, address); + // 2. 使用传输层发送请求 (返回的是异步 Future) + java.util.concurrent.CompletableFuture transportFuture = transportClient.sendRequest(request, + address); - // 3. 处理结果 (这一步逻辑如果 NettyTransportClient 已经做了反序列化,这里可能有点冗余,但保持检查是好的) - if (result instanceof com.xiaoyu.rpc.common.vo.RpcResponse) { - com.xiaoyu.rpc.common.vo.RpcResponse response = (com.xiaoyu.rpc.common.vo.RpcResponse) result; + // 3. 异步处理结果 (链式调用 thenApply) + return transportFuture.thenApply(result -> { + if (result instanceof com.xiaoyu.rpc.common.vo.RpcResponse) { + com.xiaoyu.rpc.common.vo.RpcResponse response = (com.xiaoyu.rpc.common.vo.RpcResponse) result; - // 数据已经在 TransportClient 中反序列化了吗? - // 查看 NettyTransportClient 代码: - // return rpcResponse; -> 它并没有反序列化 data 字段成 returnType - // 所以这里需要反序列化 + byte[] data = response.getData().toByteArray(); - byte[] data = response.getData().toByteArray(); - - com.xiaoyu.rpc.common.serialization.Serializer serializer = com.xiaoyu.rpc.common.serialization.SerializerCode - .getSerializerByCode(RpcConfig.getInstance().getSerializerCode()); - return serializer.deserialize(data, returnType); - } - - throw new RuntimeException("Unexpected response type: " + result.getClass()); + com.xiaoyu.rpc.common.serialization.Serializer serializer = com.xiaoyu.rpc.common.serialization.SerializerCode + .getSerializerByCode(RpcConfig.getInstance().getSerializerCode()); + return serializer.deserialize(data, returnType); + } + throw new RuntimeException("Unexpected response type: " + result.getClass()); + }); } catch (Exception e) { - throw new RuntimeException("RPC请求发送失败", e); + java.util.concurrent.CompletableFuture future = new java.util.concurrent.CompletableFuture<>(); + future.completeExceptionally(e); + return future; } } } diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/TransportClient.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/TransportClient.java index 2067c81..0fe8fea 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/TransportClient.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/transport/TransportClient.java @@ -14,7 +14,7 @@ public interface TransportClient { * * @param request 请求对象 * @param address 目标地址 - * @return 响应结果 (CompletableFuture 或 直接结果) + * @return 响应结果 (CompletableFuture) */ - Object sendRequest(RpcRequest request, InetSocketAddress address); + CompletableFuture sendRequest(RpcRequest request, InetSocketAddress address); } diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java index ca23c51..36542a3 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java @@ -53,7 +53,7 @@ protected void initChannel(SocketChannel ch) { } @Override - public Object sendRequest(RpcRequest request, InetSocketAddress address) { + public CompletableFuture sendRequest(RpcRequest request, InetSocketAddress address) { String protocolName = RpcConfig.getInstance().getProtocol(); try { @@ -73,13 +73,6 @@ public Object sendRequest(RpcRequest request, InetSocketAddress address) { // Generate ID and set to request String requestId = java.util.UUID.randomUUID().toString(); - // Use reflection or builder if setter not available, but since we regenerated - // proto, we should use builder properly - // However, RpcRequest is immutable if generated by Proto? - // Wait, RpcRequest.java is generated. It has a Builder. - // But the signature of sendRequest takes RpcRequest, which is already built. - // We need to rebuild it with the ID. - RpcRequest.Builder builder = request.toBuilder(); builder.setRequestId(requestId); RpcRequest newRequest = builder.build(); @@ -88,25 +81,25 @@ public Object sendRequest(RpcRequest request, InetSocketAddress address) { clientHandler.addFuture(requestId, resultFuture); Protocol protocol = ProtocolFactory.getProtocol(protocolName); - // Pass the handler so protocol can ensure it's in pipeline if needed (double - // check) protocol.sendRequest(channel, newRequest, clientHandler); - Object result = resultFuture.get(5, TimeUnit.SECONDS); - - if (result instanceof RpcResponse) { - RpcResponse rpcResponse = (RpcResponse) result; - - if (!"Success".equals(rpcResponse.getMessage())) { - throw new RuntimeException("服务端报错: " + rpcResponse.getMessage()); + // 彻底移除 resultFuture.get(),直接返回异步 Future + return resultFuture.thenApply(result -> { + if (result instanceof RpcResponse) { + RpcResponse rpcResponse = (RpcResponse) result; + if (!"Success".equals(rpcResponse.getMessage())) { + throw new RuntimeException("服务端报错: " + rpcResponse.getMessage()); + } + return rpcResponse; + } else { + throw new RuntimeException("服务端返回的不是 RpcResponse 类型"); } - return rpcResponse; - - } else { - throw new RuntimeException("服务端返回的不是 RpcResponse 类型"); - } + }); } catch (Exception e) { - throw new RuntimeException("RPC请求发送失败", e); + log.error("RPC请求发起失败", e); + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(e); + return future; } } } From 41d313b01d2cfcc365dd0ebb62f323bf13673aa3 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Tue, 3 Feb 2026 16:31:17 +0800 Subject: [PATCH 22/38] =?UTF-8?q?feat=EF=BC=9A=E5=BC=95=E5=85=A5shutdownGr?= =?UTF-8?q?acefully()=E6=9D=A5=E8=BF=9B=E8=A1=8C=E4=BC=98=E9=9B=85?= =?UTF-8?q?=E4=B8=8B=E7=BA=BF=EF=BC=8C=E4=BF=9D=E6=8A=A4=E5=AD=98=E9=87=8F?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E6=89=A7=E8=A1=8C=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/xiaoyu/rpc/core/registry/LocalRegistry.java | 6 ++++++ .../com/xiaoyu/rpc/core/registry/ServiceRegistry.java | 5 +++++ .../rpc/core/registry/nacos/NacosServiceRegistry.java | 5 +++++ .../java/com/xiaoyu/rpc/core/server/RpcServer.java | 10 ++++++++++ 4 files changed, 26 insertions(+) diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/LocalRegistry.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/LocalRegistry.java index 24a7874..3185946 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/LocalRegistry.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/LocalRegistry.java @@ -19,4 +19,10 @@ public InetSocketAddress lookupService(String serviceName) { System.out.println("LocalRegistry: Looking up " + serviceName); return SERVICES.get(serviceName); } + + @Override + public void clearRegistry() { + SERVICES.clear(); + System.out.println("LocalRegistry: Cleared all services."); + } } diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/ServiceRegistry.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/ServiceRegistry.java index 97dda86..a0b0992 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/ServiceRegistry.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/ServiceRegistry.java @@ -16,4 +16,9 @@ public interface ServiceRegistry { * @param inetSocketAddress 服务地址 */ void registerService(String serviceName, InetSocketAddress inetSocketAddress); + + /** + * 注销所有服务 (用于优雅下线) + */ + void clearRegistry(); } diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/nacos/NacosServiceRegistry.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/nacos/NacosServiceRegistry.java index 91186f5..67a13ca 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/nacos/NacosServiceRegistry.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/nacos/NacosServiceRegistry.java @@ -22,4 +22,9 @@ public void registerService(String serviceName, InetSocketAddress inetSocketAddr throw new RuntimeException("注册服务失败", e); } } + + @Override + public void clearRegistry() { + NacosUtils.clearRegistry(); + } } diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/RpcServer.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/RpcServer.java index 9e0f033..2558af0 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/RpcServer.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/RpcServer.java @@ -27,6 +27,16 @@ public RpcServer() { // 获取传输层实现 Transport transport = ExtensionLoader.getExtensionLoader(Transport.class).getExtension(config.getTransport()); this.transportServer = transport.createServer(this.serverPort); + + // 注册 JVM 关闭挂钩 (优雅下线) + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + log.info("检测到 JVM 关闭信号,正在执行优雅下线..."); + // 1. 注销服务 (防止新流量进入) + serviceRegistry.clearRegistry(); + // 2. 关闭网络层 (处理完存量请求) + transportServer.stop(); + log.info("优雅下线完成。"); + })); } public void register(Class interfaceClass, T serviceImpl) { From e1970bcc16daa7b45dadce85e4d06b484a87d5d9 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Tue, 3 Feb 2026 16:40:44 +0800 Subject: [PATCH 23/38] =?UTF-8?q?feat=EF=BC=9ANacos=E7=9A=84=E9=98=B2?= =?UTF-8?q?=E6=8A=96=E2=80=94=E2=80=94=E8=8E=B7=E5=8F=96=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=E4=B8=B4=E6=97=B6=E4=BD=BF=E7=94=A8=E6=9C=AC=E5=9C=B0=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E7=9A=84=E5=88=97=E8=A1=A8=E9=A1=B6=E4=B8=80=E4=B8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../registry/nacos/NacosServiceDiscovery.java | 80 +++++++++++++++---- 1 file changed, 65 insertions(+), 15 deletions(-) diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/nacos/NacosServiceDiscovery.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/nacos/NacosServiceDiscovery.java index d580772..6611342 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/nacos/NacosServiceDiscovery.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/nacos/NacosServiceDiscovery.java @@ -3,24 +3,33 @@ import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.pojo.Instance; -import lombok.extern.slf4j.Slf4j; -import com.xiaoyu.rpc.core.registry.ServiceDiscovery; - -import java.net.InetSocketAddress; -import com.xiaoyu.rpc.core.config.RpcConfig; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.NamingEvent; import com.xiaoyu.rpc.common.extension.ExtensionLoader; +import com.xiaoyu.rpc.core.config.RpcConfig; import com.xiaoyu.rpc.core.loadbalancer.LoadBalancer; -import java.net.InetSocketAddress; -import java.util.List; - +import com.xiaoyu.rpc.core.registry.ServiceDiscovery; +import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.net.InetSocketAddress; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + public class NacosServiceDiscovery implements ServiceDiscovery { private static final Logger log = LoggerFactory.getLogger(NacosServiceDiscovery.class); private final NamingService namingService; private final LoadBalancer loadBalancer; + // 本地缓存,用于容错和防抖 + private static final java.util.Map> serviceCache = new java.util.concurrent.ConcurrentHashMap<>(); + // 已订阅的服务集合 + private static final java.util.Set subscribedServices = java.util.concurrent.ConcurrentHashMap.newKeySet(); public NacosServiceDiscovery() { this.namingService = NacosUtils.getNacosNamingService(); @@ -31,9 +40,24 @@ public NacosServiceDiscovery() { @Override public InetSocketAddress lookupService(String serviceName) { try { + // 第一次查找时订阅服务变更 + if (subscribedServices.add(serviceName)) { + subscribeService(serviceName); + } + + // 1. 优先尝试从 Nacos 获取最新实例 List instances = namingService.getAllInstances(serviceName); - if (instances.size() == 0) { - log.error("未找到服务: {}", serviceName); + + if (instances.isEmpty()) { + log.warn("Nacos 返回实例列表为空,尝试使用本地缓存: {}", serviceName); + instances = serviceCache.get(serviceName); + } else { + // 更新本地缓存 + serviceCache.put(serviceName, instances); + } + + if (instances == null || instances.isEmpty()) { + log.error("未找到服务且本地无缓存: {}", serviceName); throw new RuntimeException("未找到服务: " + serviceName); } @@ -47,13 +71,39 @@ public InetSocketAddress lookupService(String serviceName) { log.info("负载均衡选择服务地址: {}", targetAddress); String[] array = targetAddress.split(":"); - String host = array[0]; - int port = Integer.parseInt(array[1]); + return new InetSocketAddress(array[0], Integer.parseInt(array[1])); - return new InetSocketAddress(host, port); } catch (NacosException e) { - log.error("获取服务实例时发生错误:", e); + log.error("获取服务实例时发生网络异常,尝试回滚到本地缓存:", e); + List cachedInstances = serviceCache.get(serviceName); + if (cachedInstances != null && !cachedInstances.isEmpty()) { + List addressList = cachedInstances.stream() + .map(instance -> instance.getIp() + ":" + instance.getPort()) + .collect(java.util.stream.Collectors.toList()); + String targetAddress = loadBalancer.select(addressList); + String[] array = targetAddress.split(":"); + return new InetSocketAddress(array[0], Integer.parseInt(array[1])); + } + throw new RuntimeException("服务发现失败且无缓存可用: " + serviceName, e); } - return null; + } + + /** + * 订阅服务变更,实现本地缓存的实时更新 + */ + private void subscribeService(String serviceName) throws NacosException { + namingService.subscribe(serviceName, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + List instances = namingEvent.getInstances(); + log.info("监听到服务变更,更新本地缓存: {} -> 实例数 {}", serviceName, instances.size()); + if (instances != null && !instances.isEmpty()) { + serviceCache.put(serviceName, instances); + } + } + } + }); } } From aa8a1ddc5932c81c624d7a2a03a5c3b4a38b4f47 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Thu, 12 Feb 2026 17:38:45 +0800 Subject: [PATCH 24/38] =?UTF-8?q?=E4=BD=BF=E7=94=A8AI=E8=BE=85=E5=8A=A9?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95(?= =?UTF-8?q?=E8=BE=B9=E7=95=8C=E6=9D=A1=E4=BB=B6=E6=9B=B4=E6=96=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go_client/proto/rpc_meta.proto | 10 +-- .../rpc/benchmark/ProtocolBenchmark.java | 10 +-- .../rpc/common/extension/ExtensionLoader.java | 4 +- rpc-common/src/main/proto/rpc_meta.proto | 10 +-- .../rpc/consumer/FullIntegrationTest.java | 63 +++++++++++++++++-- .../core/client/ByteBuddyProxyFactory.java | 5 +- .../com/xiaoyu/rpc/core/client/RpcClient.java | 6 +- .../rpc/core/client/RpcClientProxy.java | 3 +- .../registry/nacos/NacosServiceDiscovery.java | 4 +- .../rpc/core/registry/nacos/NacosUtils.java | 3 +- .../com/xiaoyu/rpc/core/server/RpcServer.java | 10 +-- .../rpc/core/server/ServiceRepository.java | 3 +- .../core/extension/ExtensionLoaderTest.java | 5 +- .../spring/processor/RpcPostProcessor.java | 8 +-- .../rpc/core/client/ChannelProvider.java | 6 +- .../core/client/NettyRpcClientHandler.java | 16 +++-- .../rpc/core/protocol/ProtocolFactory.java | 11 ++-- .../rpc/core/protocol/grpc/GrpcProtocol.java | 4 +- .../core/protocol/grpc/GrpcServerHandler.java | 16 ++--- .../rpc/core/protocol/http/HttpProtocol.java | 8 +-- .../core/protocol/http/HttpRpcDecoder.java | 8 +-- .../core/protocol/http/HttpRpcEncoder.java | 7 ++- .../core/protocol/http2/Http2Protocol.java | 8 +-- .../rpc/core/protocol/netty/MyRpcDecoder.java | 14 ++--- .../rpc/core/protocol/netty/MyRpcEncoder.java | 14 ++--- .../core/protocol/netty/NettyProtocol.java | 10 ++- .../rpc/core/server/NettyRpcHandler.java | 14 ++--- .../transport/netty/NettyTransportClient.java | 3 + .../transport/netty/NettyTransportServer.java | 3 + 29 files changed, 173 insertions(+), 113 deletions(-) diff --git a/go_client/proto/rpc_meta.proto b/go_client/proto/rpc_meta.proto index 0f7e9ee..6596c78 100644 --- a/go_client/proto/rpc_meta.proto +++ b/go_client/proto/rpc_meta.proto @@ -1,12 +1,12 @@ syntax = "proto3"; -option java_package = "com.xiaoyu.rpc.common.vo";//对应的路径名称 +option java_package = "com.xiaoyu.rpc.common.vo"; // 生成 Java 类的包名 -// 2. 必须设为 true,这样才会生成 RpcRequest.java 和 RpcResponse.java 两个文件 +// 拆分生成多个 Java 文件,避免全部消息挤在一个类里 option java_multiple_files = true; option go_package = "go_client/pb"; -// 3. 对应你的 RpcRequest +// 请求对象 message RpcRequest { // 对应 String interfaceName string interface_name = 1; @@ -27,7 +27,7 @@ message RpcRequest { string request_id = 5; } -// 4. 对应你的 RpcResponse +// 响应对象 message RpcResponse { // 对应 Object data // 同样无法存Object,只能存序列化后的二进制 @@ -42,4 +42,4 @@ message RpcResponse { service GrpcService { rpc handle (RpcRequest) returns (RpcResponse); -} \ No newline at end of file +} diff --git a/rpc-benchmark/src/main/java/com/xiaoyu/rpc/benchmark/ProtocolBenchmark.java b/rpc-benchmark/src/main/java/com/xiaoyu/rpc/benchmark/ProtocolBenchmark.java index 33ba1d0..8e0baeb 100644 --- a/rpc-benchmark/src/main/java/com/xiaoyu/rpc/benchmark/ProtocolBenchmark.java +++ b/rpc-benchmark/src/main/java/com/xiaoyu/rpc/benchmark/ProtocolBenchmark.java @@ -44,7 +44,7 @@ public void setup() throws Exception { // Use a random port to avoid conflicts (Address already in use / TIME_WAIT) this.port = 10000 + new java.util.Random().nextInt(50000); - // 1. Set Protocol via Reflection + // 通过反射覆盖本次基准测试的协议配置 RpcConfig config = RpcConfig.getInstance(); Field protocolField = RpcConfig.class.getDeclaredField("protocol"); protocolField.setAccessible(true); @@ -56,10 +56,10 @@ public void setup() throws Exception { serializerField.setAccessible(true); serializerField.set(config, "java"); - // 2. Register Service + // 注册测试服务实现 ServiceRepository.registerService(HelloService.class.getName(), new HelloServiceImpl()); - // 3. Start Server + // 启动服务端 server = new NettyTransportServer(port); Thread serverThread = new Thread(() -> { try { @@ -74,11 +74,11 @@ public void setup() throws Exception { // Wait for server to start TimeUnit.SECONDS.sleep(2); - // 4. Setup Client + // 初始化客户端 client = new NettyTransportClient(); address = new InetSocketAddress("127.0.0.1", port); - // 5. Build Request + // 组装测试请求 Serializer serializer = ExtensionLoader.getExtensionLoader(Serializer.class).getExtension("java"); byte[] argBytes = serializer.serialize("Benchmark"); diff --git a/rpc-common/src/main/java/com/xiaoyu/rpc/common/extension/ExtensionLoader.java b/rpc-common/src/main/java/com/xiaoyu/rpc/common/extension/ExtensionLoader.java index 6ffa78d..0285fbc 100644 --- a/rpc-common/src/main/java/com/xiaoyu/rpc/common/extension/ExtensionLoader.java +++ b/rpc-common/src/main/java/com/xiaoyu/rpc/common/extension/ExtensionLoader.java @@ -67,14 +67,14 @@ public T getExtension(String name) { throw new IllegalArgumentException("Extension name should not be null or empty."); } - // 1. 获取或创建单例Holder + // 先拿到对应名称的缓存槽位,不存在就补一个 Holder holder = cachedInstances.get(name); if (holder == null) { cachedInstances.putIfAbsent(name, new Holder<>()); holder = cachedInstances.get(name); } - // 2. 双重检查锁创建实例 + // 实例按需创建,使用双重检查避免重复初始化 Object instance = holder.get(); if (instance == null) { synchronized (holder) { diff --git a/rpc-common/src/main/proto/rpc_meta.proto b/rpc-common/src/main/proto/rpc_meta.proto index 614e421..c53164a 100644 --- a/rpc-common/src/main/proto/rpc_meta.proto +++ b/rpc-common/src/main/proto/rpc_meta.proto @@ -1,11 +1,11 @@ syntax = "proto3"; -option java_package = "com.xiaoyu.rpc.common.vo";//对应的路径名称 +option java_package = "com.xiaoyu.rpc.common.vo"; // 生成 Java 类的包名 -// 2. 必须设为 true,这样才会生成 RpcRequest.java 和 RpcResponse.java 两个文件 +// 拆分生成多个 Java 文件,避免全部消息挤在一个类里 option java_multiple_files = true; -// 3. 对应你的 RpcRequest +// 请求对象 message RpcRequest { // 对应 String interfaceName string interface_name = 1; @@ -26,7 +26,7 @@ message RpcRequest { string request_id = 5; } -// 4. 对应你的 RpcResponse +// 响应对象 message RpcResponse { // 对应 Object data // 同样无法存Object,只能存序列化后的二进制 @@ -41,4 +41,4 @@ message RpcResponse { service GrpcService { rpc handle (RpcRequest) returns (RpcResponse); -} \ No newline at end of file +} diff --git a/rpc-consumer/src/test/java/com/xiaoyu/rpc/consumer/FullIntegrationTest.java b/rpc-consumer/src/test/java/com/xiaoyu/rpc/consumer/FullIntegrationTest.java index dc573a6..ea74566 100644 --- a/rpc-consumer/src/test/java/com/xiaoyu/rpc/consumer/FullIntegrationTest.java +++ b/rpc-consumer/src/test/java/com/xiaoyu/rpc/consumer/FullIntegrationTest.java @@ -1,9 +1,21 @@ package com.xiaoyu.rpc.consumer; -import com.xiaoyu.rpc.core.client.RpcClientProxy; import com.xiaoyu.rpc.api.HelloService; +import com.xiaoyu.rpc.common.serialization.Serializer; +import com.xiaoyu.rpc.common.serialization.SerializerCode; +import com.xiaoyu.rpc.common.vo.RpcRequest; +import com.xiaoyu.rpc.core.client.RpcClient; +import com.xiaoyu.rpc.core.config.RpcConfig; import com.xiaoyu.rpc.core.server.RpcServer; +import com.google.protobuf.ByteString; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.util.concurrent.TimeUnit; + import static org.junit.jupiter.api.Assertions.*; public class FullIntegrationTest { @@ -24,6 +36,13 @@ public void testFullIntegration() throws InterruptedException { // Ensure we use KRYO or JSON/Hessian serializer that supports mundane Java // classes System.setProperty("rpc.serializer", "kryo"); + System.setProperty("rpc.transport", "netty"); + + // 沙箱或受限环境无法监听端口时,跳过此集成测试,避免把环境问题算成代码失败 + Assumptions.assumeTrue(canBindLocalPort(9090), "No permission to bind local test port 9090"); + + // 当前默认配置可能是 grpc,但客户端泛化 grpc 尚未支持,测试里强制切到 netty + forceConfig("protocol", "netty"); // Start Server in a thread Thread serverThread = new Thread(() -> { @@ -39,18 +58,23 @@ public void testFullIntegration() throws InterruptedException { serverThread.setDaemon(true); serverThread.start(); - Thread.sleep(5000); // Wait for server start (increased) + Thread.sleep(1500); // Wait for server start try { System.out.println("Starting Client..."); - HelloService helloService = RpcClientProxy.create(HelloService.class); + RpcClient rpcClient = new RpcClient(); + Serializer serializer = SerializerCode.getSerializerByCode(RpcConfig.getInstance().getSerializerCode()); System.out.println(">>> First Call"); - String result1 = helloService.sayHello("World1"); + String result1 = (String) rpcClient + .sendRequest(buildRequest("World1", serializer), String.class) + .get(5, TimeUnit.SECONDS); System.out.println("Result1: " + result1); System.out.println(">>> Second Call (Should reuse connection)"); - String result2 = helloService.sayHello("World2"); + String result2 = (String) rpcClient + .sendRequest(buildRequest("World2", serializer), String.class) + .get(5, TimeUnit.SECONDS); System.out.println("Result2: " + result2); assertNotNull(result1, "Result1 should not be null"); @@ -64,4 +88,33 @@ public void testFullIntegration() throws InterruptedException { fail("Test failed with exception: " + e.getMessage()); } } + + private static RpcRequest buildRequest(String name, Serializer serializer) { + byte[] argBytes = serializer.serialize(name); + return RpcRequest.newBuilder() + .setInterfaceName(HelloService.class.getName()) + .setMethodName("sayHello") + .addParamTypes(String.class.getName()) + .addParameters(ByteString.copyFrom(argBytes)) + .build(); + } + + private static boolean canBindLocalPort(int port) { + try (ServerSocket ignored = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"))) { + return true; + } catch (Exception e) { + return false; + } + } + + private static void forceConfig(String fieldName, Object value) { + try { + RpcConfig config = RpcConfig.getInstance(); + Field field = RpcConfig.class.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(config, value); + } catch (Exception e) { + throw new RuntimeException("Failed to force RpcConfig field: " + fieldName, e); + } + } } diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/ByteBuddyProxyFactory.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/ByteBuddyProxyFactory.java index 4a07a5e..d7b2617 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/ByteBuddyProxyFactory.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/ByteBuddyProxyFactory.java @@ -28,6 +28,7 @@ public T getProxy(Class clazz) { .intercept(InvocationHandlerAdapter.of(new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + // 请求中记录接口名 + 方法名,服务端靠这两项定位目标方法 RpcRequest.Builder builder = RpcRequest.newBuilder() .setInterfaceName(method.getDeclaringClass().getName()) .setMethodName(method.getName()); @@ -50,8 +51,8 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl } RpcRequest request = builder.build(); - // 注意:这里直接返回 Future。 - // 此时要求业务接口 Method 的返回类型必须是 CompletableFuture,否则会发生类型转换异常。 + // 这里不阻塞等待结果,直接把 CompletableFuture 返回给上层调用方。 + // 如果业务接口不是异步返回类型,运行时会出现类型不匹配。 return rpcClient.sendRequest(request, method.getReturnType()); } })).make().load(clazz.getClassLoader()).getLoaded().getConstructor().newInstance(); diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/RpcClient.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/RpcClient.java index 1941b96..69ecbc9 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/RpcClient.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/RpcClient.java @@ -27,7 +27,7 @@ public RpcClient() { public java.util.concurrent.CompletableFuture sendRequest(RpcRequest request, Class returnType) { try { - // 1. 服务发现 (同步查找,通常有本地缓存) + // 先做一次服务发现(同步查找,通常会命中本地缓存) InetSocketAddress address = serviceDiscovery.lookupService(request.getInterfaceName()); if (address == null) { @@ -36,11 +36,11 @@ public java.util.concurrent.CompletableFuture sendRequest(RpcRequest req return future; } - // 2. 使用传输层发送请求 (返回的是异步 Future) + // 交给传输层发送,返回异步 Future java.util.concurrent.CompletableFuture transportFuture = transportClient.sendRequest(request, address); - // 3. 异步处理结果 (链式调用 thenApply) + // 在回调里把响应体反序列化成目标返回类型 return transportFuture.thenApply(result -> { if (result instanceof com.xiaoyu.rpc.common.vo.RpcResponse) { com.xiaoyu.rpc.common.vo.RpcResponse response = (com.xiaoyu.rpc.common.vo.RpcResponse) result; diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/RpcClientProxy.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/RpcClientProxy.java index 47bd130..6bf87db 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/RpcClientProxy.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/RpcClientProxy.java @@ -6,8 +6,9 @@ public class RpcClientProxy { public static T create(Class clazz) { + // 代理类型由配置决定(jdk / bytebuddy 等),这里统一走 SPI 扩展点加载 String proxyType = RpcConfig.getInstance().getProxyType(); ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension(proxyType); return proxyFactory.getProxy(clazz); } -} \ No newline at end of file +} diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/nacos/NacosServiceDiscovery.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/nacos/NacosServiceDiscovery.java index 6611342..67085ce 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/nacos/NacosServiceDiscovery.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/nacos/NacosServiceDiscovery.java @@ -42,10 +42,11 @@ public InetSocketAddress lookupService(String serviceName) { try { // 第一次查找时订阅服务变更 if (subscribedServices.add(serviceName)) { + // add 返回 true 说明此前未订阅,避免同一个服务被重复订阅 subscribeService(serviceName); } - // 1. 优先尝试从 Nacos 获取最新实例 + // 优先从 Nacos 拉取最新实例列表 List instances = namingService.getAllInstances(serviceName); if (instances.isEmpty()) { @@ -75,6 +76,7 @@ public InetSocketAddress lookupService(String serviceName) { } catch (NacosException e) { log.error("获取服务实例时发生网络异常,尝试回滚到本地缓存:", e); + // Nacos 短暂不可用时,优先用最近一次成功拉取到的实例兜底 List cachedInstances = serviceCache.get(serviceName); if (cachedInstances != null && !cachedInstances.isEmpty()) { List addressList = cachedInstances.stream() diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/nacos/NacosUtils.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/nacos/NacosUtils.java index a5704fa..bbc3708 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/nacos/NacosUtils.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/nacos/NacosUtils.java @@ -29,8 +29,7 @@ public class NacosUtils { public static NamingService getNacosNamingService() { try { - // 从配置中获取 Nacos 地址,暂时先硬编码或者后续从 RpcConfig 获取 - // 这里我们先假定 RpcConfig 会提供 registryAddress,如果没提供就默认 + // 优先读取配置中的 Nacos 地址,未配置时退回本地默认地址 String registryAddress = RpcConfig.getInstance().getRegistryAddress(); if (registryAddress == null || registryAddress.isEmpty()) { registryAddress = "127.0.0.1:8848"; diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/RpcServer.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/RpcServer.java index 2558af0..beac1b6 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/RpcServer.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/RpcServer.java @@ -31,9 +31,9 @@ public RpcServer() { // 注册 JVM 关闭挂钩 (优雅下线) Runtime.getRuntime().addShutdownHook(new Thread(() -> { log.info("检测到 JVM 关闭信号,正在执行优雅下线..."); - // 1. 注销服务 (防止新流量进入) + // 先注销服务,阻止新流量进入 serviceRegistry.clearRegistry(); - // 2. 关闭网络层 (处理完存量请求) + // 再关闭网络层,让存量请求有机会处理完成 transportServer.stop(); log.info("优雅下线完成。"); })); @@ -41,10 +41,10 @@ public RpcServer() { public void register(Class interfaceClass, T serviceImpl) { String serviceName = interfaceClass.getName(); - // 1. 本地注册 (使用 ServiceRepository 解耦) + // 先做本地注册,便于请求分发时快速定位实现类 ServiceRepository.registerService(serviceName, serviceImpl); - // 2. 远程注册 (Nacos / Local) + // 再注册到注册中心(Nacos / Local) try { serviceRegistry.registerService(serviceName, new InetSocketAddress(serverHost, serverPort)); log.info("Service registered: {}", serviceName); @@ -56,4 +56,4 @@ public void register(Class interfaceClass, T serviceImpl) { public void start() throws InterruptedException { transportServer.start(); } -} \ No newline at end of file +} diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/ServiceRepository.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/ServiceRepository.java index 77360c4..b6c9cd1 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/ServiceRepository.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/server/ServiceRepository.java @@ -9,10 +9,11 @@ */ public class ServiceRepository { - // 缓存服务实例: interfaceName -> serviceBean + // 进程内服务表:key 是接口全限定名,value 是具体实现对象 private static final Map SERVICE_MAP = new ConcurrentHashMap<>(); public static void registerService(String interfaceName, Object serviceBean) { + // 同一接口重复注册时以后一次为准,便于启动期覆盖旧实现 SERVICE_MAP.put(interfaceName, serviceBean); } diff --git a/rpc-core/src/test/java/com/xiaoyu/rpc/core/extension/ExtensionLoaderTest.java b/rpc-core/src/test/java/com/xiaoyu/rpc/core/extension/ExtensionLoaderTest.java index 1a707b7..c238114 100644 --- a/rpc-core/src/test/java/com/xiaoyu/rpc/core/extension/ExtensionLoaderTest.java +++ b/rpc-core/src/test/java/com/xiaoyu/rpc/core/extension/ExtensionLoaderTest.java @@ -82,10 +82,13 @@ void testLoadBalancerExtensions() { void testProxyFactoryExtensions() { ExtensionLoader loader = ExtensionLoader.getExtensionLoader(ProxyFactory.class); + // jdk 代理实现不依赖网络与注册中心,单元测试里直接实例化即可 assertNotNull(loader.getExtension("jdk"), "JDK proxy factory should be loaded"); - assertNotNull(loader.getExtension("bytebuddy"), "ByteBuddy proxy factory should be loaded"); + // bytebuddy 在当前实现里会进一步初始化 RpcClient(包含注册中心/传输层依赖), + // 这里仅校验其扩展声明已被正确加载,避免把单元测试耦合到外部环境 var extensions = loader.getSupportedExtensions(); + assertTrue(extensions.contains("bytebuddy"), "Should contain 'bytebuddy' extension"); assertEquals(2, extensions.size(), "Should have exactly 2 proxy factory extensions"); } diff --git a/rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/processor/RpcPostProcessor.java b/rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/processor/RpcPostProcessor.java index 7545abd..78cc43e 100644 --- a/rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/processor/RpcPostProcessor.java +++ b/rpc-spring-boot-starter/src/main/java/com/xiaoyu/rpc/spring/processor/RpcPostProcessor.java @@ -16,8 +16,8 @@ /** * RPC Bean 后处理器 - * 1. 扫描 @RpcService 注解,自动注册服务 - * 2. 扫描 @RpcReference 注解,自动注入代理 + * 负责扫描 @RpcService 并自动注册服务, + * 同时处理 @RpcReference 字段并注入客户端代理。 */ @Slf4j public class RpcPostProcessor implements BeanPostProcessor, ApplicationContextAware { @@ -35,13 +35,13 @@ public void setApplicationContext(ApplicationContext applicationContext) throws @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - // 1. 处理 @RpcService 注解 (服务注册) + // 处理 @RpcService:把服务注册到 RpcServer Class beanClass = bean.getClass(); if (beanClass.isAnnotationPresent(RpcService.class)) { registerService(bean, beanClass); } - // 2. 处理 @RpcReference 注解 (代理注入) + // 处理 @RpcReference:给字段注入代理对象 injectRpcReferences(bean, beanClass); return bean; diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/client/ChannelProvider.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/client/ChannelProvider.java index 343a812..a9ab2ff 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/client/ChannelProvider.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/client/ChannelProvider.java @@ -21,7 +21,7 @@ public class ChannelProvider { public static Channel get(InetSocketAddress inetSocketAddress, Bootstrap bootstrap) { String key = inetSocketAddress.toString(); - // 1. 尝试从缓存获取 + // 先尝试复用已有连接 if (channels.containsKey(key)) { Channel channel = channels.get(key); if (channel != null && channel.isActive()) { @@ -31,10 +31,10 @@ public static Channel get(InetSocketAddress inetSocketAddress, Bootstrap bootstr } } - // 2. 建立新连接 + // 缓存不可用时再新建连接 Channel channel = connect(bootstrap, inetSocketAddress); - // 3. 放入缓存 + // 新连接建立成功后放回缓存 if (channel != null) { channels.put(key, channel); } diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/client/NettyRpcClientHandler.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/client/NettyRpcClientHandler.java index ee82b27..e97146c 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/client/NettyRpcClientHandler.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/client/NettyRpcClientHandler.java @@ -3,22 +3,22 @@ import com.xiaoyu.rpc.common.vo.RpcResponse; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; -import lombok.extern.slf4j.Slf4j; - import java.util.concurrent.CompletableFuture; -// 这是一个 Netty 的 Handler,专门负责“收信” import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.netty.channel.ChannelHandler; -// 这是一个 Netty 的 Handler,专门负责“收信” +/** + * 客户端响应处理器。 + * 通过 requestId 将响应路由回对应的 CompletableFuture。 + */ @ChannelHandler.Sharable public class NettyRpcClientHandler extends SimpleChannelInboundHandler { private static final Logger log = LoggerFactory.getLogger(NettyRpcClientHandler.class); - // Key: RequestId, Value: Future + // 一个连接上可以并发多个请求,靠 requestId 区分各自回调 private final java.util.Map> pendingRequests = new java.util.concurrent.ConcurrentHashMap<>(); public void addFuture(String requestId, CompletableFuture future) { @@ -52,13 +52,11 @@ protected void channelRead0(ChannelHandlerContext ctx, RpcResponse response) { @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { log.error("Client caught exception", cause); - // Fail all pending requests + // 连接级异常通常影响当前连接上的全部在途请求,统一失败返回给上层 for (CompletableFuture future : pendingRequests.values()) { future.completeExceptionally(cause); } pendingRequests.clear(); ctx.close(); } - - // public CompletableFuture getFuture() { ... } // Removed single getter -} \ No newline at end of file +} diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/ProtocolFactory.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/ProtocolFactory.java index 62eed19..3677fce 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/ProtocolFactory.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/ProtocolFactory.java @@ -9,7 +9,7 @@ public class ProtocolFactory { private static final Logger log = LoggerFactory.getLogger(ProtocolFactory.class); public static Protocol getProtocol(String name) { - // 默认使用 Netty + // 配置为空时使用默认协议,保证最小可用 if (name == null || name.trim().isEmpty()) { name = "netty"; } @@ -18,16 +18,13 @@ public static Protocol getProtocol(String name) { return ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name); } catch (Exception e) { log.error("Failed to load protocol: " + name, e); - // Fallback or rethrow? Let's use Netty as fallback for robustness if strictness - // isn't required - // OR rethrow to fail fast. - // Given the original code had a default case, let's keep the fail-safe behavior - // for now but log error. + // 非默认协议加载失败时,兜底回退到 Netty,避免服务直接不可用 if (!"netty".equalsIgnoreCase(name)) { log.warn("Falling back to default NettyProtocol"); return new NettyProtocol(); } + // 默认协议都加载失败,继续抛出异常交给上层处理 throw e; } } -} \ No newline at end of file +} diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcProtocol.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcProtocol.java index 510d7f2..611411d 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcProtocol.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcProtocol.java @@ -20,10 +20,10 @@ public String getName() { @Override public void config(ChannelPipeline pipeline, boolean isServer, ChannelHandler serverHandler) { if (isServer) { - // 1. Http2FrameCodec (处理握手、并转为 Frame 对象) + // 先接入 HTTP/2 帧编解码,处理握手并产出 Frame pipeline.addLast(Http2FrameCodecBuilder.forServer().build()); - // 2. MultiplexHandler (为每个 Stream 创建子 Channel) + // 再通过 MultiplexHandler 为每个 Stream 创建子 Channel pipeline.addLast(new Http2MultiplexHandler(new ChannelInitializer() { @Override protected void initChannel(Channel ch) throws Exception { diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcServerHandler.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcServerHandler.java index 8a57a6e..2468985 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcServerHandler.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcServerHandler.java @@ -13,6 +13,7 @@ @Slf4j public class GrpcServerHandler extends ChannelDuplexHandler { + // 透传的业务处理器(例如 NettyRpcHandler) private final io.netty.channel.ChannelHandler busineesHandler; public GrpcServerHandler(io.netty.channel.ChannelHandler busineesHandler) { @@ -23,6 +24,7 @@ public GrpcServerHandler(io.netty.channel.ChannelHandler busineesHandler) { public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof Http2Frame) { try { + // 只在这里处理 gRPC 对应的 HTTP/2 Frame,转换成内部 RpcRequest processFrame(ctx, (Http2Frame) msg); } finally { ReferenceCountUtil.release(msg); @@ -46,6 +48,7 @@ private void processFrame(ChannelHandlerContext ctx, Http2Frame frame) throws Ex Http2DataFrame dataFrame = (Http2DataFrame) frame; ByteBuf content = dataFrame.content(); + // gRPC 数据帧固定前缀:1 字节压缩标记 + 4 字节消息长度 if (content.readableBytes() < 5) return; @@ -53,24 +56,20 @@ private void processFrame(ChannelHandlerContext ctx, Http2Frame frame) throws Ex int length = content.readInt(); if (content.readableBytes() < length) { - // Return reader index to start if not enough data (simplified, normally - // requires buffering) + // 当前帧数据不足,回退读指针等待后续数据(简化处理,生产环境建议引入缓冲聚合) content.resetReaderIndex(); return; } - // Zero-Copy Optimization: - // Use slicing to avoid allocating an intermediate byte[] + // 尽量避免中间大数组拷贝:先切片,再按底层存储类型选择解析路径 ByteBuf slice = content.readSlice(length); RpcRequest rpcRequest; if (slice.nioBufferCount() > 0) { - // Zero-Copy: Direct access via NIO ByteBuffer + // 直接走 NIO Buffer 解析,少一次复制 rpcRequest = RpcRequest.parseFrom(slice.nioBuffer()); } else { - // Fallback: Use InputStream (avoids large byte[] allocation) - // Note: Protobuf CodedInputStream still copies when string/bytes parsed, but we - // avoid the big chunk copy + // 兜底路径:内存布局不支持 NIO Buffer 时退回字节数组解析 byte[] bytes = new byte[length]; slice.readBytes(bytes); rpcRequest = RpcRequest.parseFrom(bytes); @@ -86,6 +85,7 @@ public void write(ChannelHandlerContext ctx, Object msg, io.netty.channel.Channe RpcResponse response = (RpcResponse) msg; try { byte[] bytes = response.toByteArray(); + // gRPC 响应体同样要补上 5 字节前缀(压缩位 + 长度) ByteBuf out = ctx.alloc().buffer(); out.writeByte(0); out.writeInt(bytes.length); diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpProtocol.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpProtocol.java index b83cb17..866ecbd 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpProtocol.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpProtocol.java @@ -20,11 +20,11 @@ public String getName() { @Override public void config(ChannelPipeline pipeline, boolean isServer, io.netty.channel.ChannelHandler serverHandler) { - // 1. 获取序列化器 + // 读取当前配置的序列化器 RpcConfig rpcConfig = RpcConfig.getInstance(); Serializer serializer = SerializerCode.getSerializerByCode(rpcConfig.getSerializerCode()); - // 2. HTTP 编解码基础 + // 先挂载 HTTP 基础编解码器 if (isServer) { pipeline.addLast(new HttpServerCodec()); } else { @@ -32,7 +32,7 @@ public void config(ChannelPipeline pipeline, boolean isServer, io.netty.channel. } pipeline.addLast(new HttpObjectAggregator(512 * 1024)); - // 3. HTTP 与 RpcObject 的转换层 + // 再挂载 HTTP 报文与 RPC 对象之间的转换器 if (isServer) { // 服务端:解码 Request,编码 Response pipeline.addLast(new HttpRpcDecoder(serializer, RpcRequest.class)); @@ -62,4 +62,4 @@ public void sendRequest(io.netty.channel.Channel channel, RpcRequest request, } }); } -} \ No newline at end of file +} diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpRpcDecoder.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpRpcDecoder.java index a2cebf8..2efc5e4 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpRpcDecoder.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http/HttpRpcDecoder.java @@ -22,15 +22,15 @@ public HttpRpcDecoder(Serializer serializer, Class genericClass) { @Override protected void decode(ChannelHandlerContext ctx, FullHttpMessage msg, List out) { - // 读取 Body 数据 + // FullHttpMessage 已经由聚合器拼成完整报文,这里可以一次性读取 body ByteBuf content = msg.content(); byte[] bytes = new byte[content.readableBytes()]; content.readBytes(bytes); - // 反序列化 + // 直接按目标类型反序列化为 RpcRequest / RpcResponse Object obj = serializer.deserialize(bytes, genericClass); - // 如果是 Request,可以从 Header 中校验方法名(可选) + // 方法名放在 Header 里,主要用于排查和链路观察,不参与核心反序列化流程 if (msg instanceof FullHttpRequest) { String methodName = ((FullHttpRequest) msg).headers().get("Rpc-Method"); // log.info("当前解码获取到的的methodName:{}",methodName); @@ -38,4 +38,4 @@ protected void decode(ChannelHandlerContext ctx, FullHttpMessage msg, List out) { + // RPC 对象先序列化成二进制,再包成 HTTP 消息体 byte[] body = serializer.serialize(msg); FullHttpMessage httpMessage; @@ -34,7 +35,7 @@ protected void encode(ChannelHandlerContext ctx, Object msg, List out) { HttpMethod.POST, "/", Unpooled.wrappedBuffer(body)); - // 将方法名放入 Header + // 方法名放 Header,便于服务端排查请求来源 httpRequest.headers().set("Rpc-Method", request.getMethodName()); // log.info("使用 HTTP 协议发送请求,方法名: {},正在Encode", request.getMethodName()); httpRequest.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/x-rpc"); @@ -47,8 +48,8 @@ protected void encode(ChannelHandlerContext ctx, Object msg, List out) { Unpooled.wrappedBuffer(body)); } - // 设置必要的 HTTP 长度头 + // 显式设置长度,避免对端按分块模式误判读取边界 httpMessage.headers().set(HttpHeaderNames.CONTENT_LENGTH, body.length); out.add(httpMessage); } -} \ No newline at end of file +} diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http2/Http2Protocol.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http2/Http2Protocol.java index 5709daa..80f509a 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http2/Http2Protocol.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/http2/Http2Protocol.java @@ -67,7 +67,7 @@ protected void initChannel(Http2StreamChannel ch) { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { // 如果还有残留的设置帧传到这里,说明 FrameCodec 没拦截住 - // 打印一下以便调试,或者直接释放 + // 这里直接释放,避免引用计数对象泄漏 ReferenceCountUtil.release(msg); } @@ -86,7 +86,7 @@ public void sendRequest(io.netty.channel.Channel channel, RpcRequest request, RpcConfig rpcConfig = RpcConfig.getInstance(); Serializer serializer = SerializerCode.getSerializerByCode(rpcConfig.getSerializerCode()); - // 使用 bootstrap 异步创建新流 + // HTTP/2 每个请求走独立 Stream,底层 TCP 连接仍然复用同一个 Channel io.netty.handler.codec.http2.Http2StreamChannelBootstrap streamBootstrap = new io.netty.handler.codec.http2.Http2StreamChannelBootstrap( channel); @@ -97,7 +97,7 @@ public void sendRequest(io.netty.channel.Channel channel, RpcRequest request, } Http2StreamChannel streamChannel = (Http2StreamChannel) f.getNow(); - // 在流通道中构建完整的处理链 + // 每个 Stream 都有独立 pipeline,避免多请求之间相互干扰 streamChannel.pipeline().addLast(new Http2StreamFrameToHttpObjectCodec(false)); streamChannel.pipeline().addLast(new io.netty.handler.codec.http.HttpObjectAggregator(512 * 1024)); streamChannel.pipeline().addLast(new HttpRpcEncoder(serializer)); @@ -111,4 +111,4 @@ public void sendRequest(io.netty.channel.Channel channel, RpcRequest request, }); }); } -} \ No newline at end of file +} diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcDecoder.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcDecoder.java index dd1cf0b..020b396 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcDecoder.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcDecoder.java @@ -25,23 +25,23 @@ public MyRpcDecoder(Class genericClass) { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { - // 1. 校验魔数,4Bytes + // 先校验魔数(4 字节) int magic = in.readInt(); log.info("正在解码数据...魔数为:{}", magic); if (magic != MAGIC_NUMBER) { throw new RuntimeException("未知协议魔数: " + magic); } - // 2. 读取消息类型,1Byte + // 读取消息类型(1 字节) byte packageType = in.readByte(); log.info("正在解码数据...消息类型为:{}", packageType); - // 3. 读取序列化器标识并获取实例,1Bytes + // 读取序列化器标识并拿到对应实现(1 字节) byte serializerCode = in.readByte(); Serializer serializer = SerializerCode.getSerializerByCode(serializerCode); log.info("正在解码数据...序列化器标识为:{}", serializerCode); - // 4. 读取 Body 长度 + // 读取消息体长度 int length = in.readInt(); int maxFrameSize = com.xiaoyu.rpc.core.config.RpcConfig.getInstance().getMaxMessageSize(); if (length > maxFrameSize || length < 0) { @@ -50,13 +50,13 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { throw new RuntimeException("拒绝过大的报文: " + length); } - // 5. 读取 Body 数据 + // 按长度读取消息体数据 byte[] body = new byte[length]; in.readBytes(body); - // 6. 反序列化 + // 反序列化为请求或响应对象 Class clazz = (packageType == 0x01) ? RpcRequest.class : RpcResponse.class; Object obj = serializer.deserialize(body, clazz); out.add(obj); } -} \ No newline at end of file +} diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcEncoder.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcEncoder.java index ad727cc..d30148a 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcEncoder.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcEncoder.java @@ -16,26 +16,26 @@ public MyRpcEncoder(Serializer serializer) { @Override protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) { - // 1. 写入魔数 (4字节) + // 先写固定魔数(4 字节) out.writeInt(MAGIC_NUMBER); - // 2. 写入消息类型 (1字节) + // 写消息类型(1 字节) if (msg instanceof RpcRequest) { out.writeByte(0x01); // 请求 } else { out.writeByte(0x02); // 响应 } - // 3. 写入序列化器标识 (1字节) + // 写序列化器标识(1 字节) out.writeByte(serializer.getCode()); - // 4. 获取序列化后的字节数组 + // 序列化消息体 byte[] body = serializer.serialize(msg); - // 5. 写入 Body 长度 (4字节) + // 写消息体长度(4 字节) out.writeInt(body.length); - // 6. 写入 Body 数据 + // 写消息体内容 out.writeBytes(body); } -} \ No newline at end of file +} diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/NettyProtocol.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/NettyProtocol.java index 5087983..38d9dbe 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/NettyProtocol.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/netty/NettyProtocol.java @@ -17,21 +17,19 @@ public String getName() { @Override public void config(ChannelPipeline pipeline, boolean isServer, io.netty.channel.ChannelHandler serverHandler) { - // 1. 获取配置 + // 读取当前序列化配置 RpcConfig rpcConfig = RpcConfig.getInstance(); byte code = rpcConfig.getSerializerCode(); Serializer serializer = SerializerCode.getSerializerByCode(code); - // 2. 判断解码类型 - // 如果是服务端(isServer=true),我要读 Request - // 如果是客户端(isServer=false),我要读 Response + // 服务端解码 RpcRequest,客户端解码 RpcResponse if (isServer) { pipeline.addLast(new MyRpcDecoder(RpcRequest.class)); } else { pipeline.addLast(new MyRpcDecoder(RpcResponse.class)); } - // 3. 编码器 (收发都需要编码) + // 编码器在收发两侧都需要 pipeline.addLast(new MyRpcEncoder(serializer)); if (isServer && serverHandler != null) { @@ -53,4 +51,4 @@ public void sendRequest(io.netty.channel.Channel channel, RpcRequest request, } }); } -} \ No newline at end of file +} diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/server/NettyRpcHandler.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/server/NettyRpcHandler.java index 4438ef0..e349d47 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/server/NettyRpcHandler.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/server/NettyRpcHandler.java @@ -28,13 +28,13 @@ protected void channelRead0(ChannelHandlerContext ctx, RpcRequest request) throw responseBuilder.setRequestId(request.getRequestId()); try { - // 1. 获取实现类 (从 ServiceRepository 获取) + // 从 ServiceRepository 取到目标服务实现 Object serviceBean = ServiceRepository.getService(request.getInterfaceName()); if (serviceBean == null) { throw new RuntimeException("未找到服务实现: " + request.getInterfaceName()); } - // 2. 解析参数类型 (List -> Class[]) + // 将参数类型名还原为 Class[] // Proto 存的是类名字符串,我们需要反射还原成 Class 对象 List paramTypeNames = request.getParamTypesList(); Class[] parameterTypes = new Class[paramTypeNames.size()]; @@ -43,7 +43,7 @@ protected void channelRead0(ChannelHandlerContext ctx, RpcRequest request) throw parameterTypes[i] = Class.forName(paramTypeNames.get(i)); } - // 3. 解析参数值 (List -> Object[]) + // 将参数字节反序列化为方法入参 // Proto 存的是二进制,我们需要反序列化回 Java 对象 List paramByteList = request.getParametersList(); Object[] parameters = new Object[paramByteList.size()]; @@ -56,12 +56,12 @@ protected void channelRead0(ChannelHandlerContext ctx, RpcRequest request) throw parameters[i] = serializer.deserialize(bytes, parameterTypes[i]); } - // 4. 反射调用 + // 通过反射调用目标方法 Class serviceClass = serviceBean.getClass(); Method method = serviceClass.getMethod(request.getMethodName(), parameterTypes); Object result = method.invoke(serviceBean, parameters); - // 5. 封装成功结果 (Object -> byte[] -> ByteString) + // 把返回值序列化后写入响应 byte[] resultBytes; if (result == null) { resultBytes = new byte[0]; @@ -79,7 +79,7 @@ protected void channelRead0(ChannelHandlerContext ctx, RpcRequest request) throw responseBuilder.setData(ByteString.EMPTY); } - // 6. 发送响应 + // 返回响应 ctx.writeAndFlush(responseBuilder.build()); } -} \ No newline at end of file +} diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java index 36542a3..8aedb45 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportClient.java @@ -33,6 +33,7 @@ private static Bootstrap getBootstrap() { if (bootstrap == null) { synchronized (NettyTransportClient.class) { if (bootstrap == null) { + // Bootstrap 和 EventLoopGroup 进程内复用,避免每次请求都创建线程池 eventLoopGroup = new NioEventLoopGroup(); Bootstrap newBootstrap = new Bootstrap(); newBootstrap.group(eventLoopGroup) @@ -72,12 +73,14 @@ public CompletableFuture sendRequest(RpcRequest request, InetSocketAddre } // Generate ID and set to request + // requestId 是客户端关联响应的关键键值,必须在发送前写入 String requestId = java.util.UUID.randomUUID().toString(); RpcRequest.Builder builder = request.toBuilder(); builder.setRequestId(requestId); RpcRequest newRequest = builder.build(); CompletableFuture resultFuture = new CompletableFuture<>(); + // 先注册 future 再发送,避免极端情况下响应先到导致找不到回调 clientHandler.addFuture(requestId, resultFuture); Protocol protocol = ProtocolFactory.getProtocol(protocolName); diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportServer.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportServer.java index 29b5caa..f986517 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportServer.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/transport/netty/NettyTransportServer.java @@ -26,6 +26,7 @@ public NettyTransportServer(int port) { @Override public void start() throws InterruptedException { + // boss 负责接收连接,worker 负责连接上的读写事件 bossGroup = new NioEventLoopGroup(); workerGroup = new NioEventLoopGroup(); try { @@ -35,6 +36,7 @@ public void start() throws InterruptedException { .childHandler(new ChannelInitializer() { @Override protected void initChannel(SocketChannel ch) { + // 协议实现通过配置切换,服务端业务处理统一复用 NettyRpcHandler String protocolName = RpcConfig.getInstance().getProtocol(); Protocol protocol = ProtocolFactory.getProtocol(protocolName); protocol.config(ch.pipeline(), true, new NettyRpcHandler()); @@ -50,6 +52,7 @@ protected void initChannel(SocketChannel ch) { @Override public void stop() { + // shutdownGracefully 会等待队列任务处理后再退出,避免直接中断 I/O if (bossGroup != null) bossGroup.shutdownGracefully(); if (workerGroup != null) From d9d658f73442bf15f4939760b68786fab74c9c38 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Thu, 12 Feb 2026 18:14:53 +0800 Subject: [PATCH 25/38] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=B1=BB=E8=BF=9B=E8=A1=8C=E8=BE=B9=E7=95=8C=E6=9D=A1=E4=BB=B6?= =?UTF-8?q?=E7=9A=84=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/maven.yml | 62 +++++-- .../com/xiaoyu/rpc/core/client/RpcClient.java | 6 + .../registry/nacos/NacosServiceDiscovery.java | 11 +- .../xiaoyu/rpc/core/client/RpcClientTest.java | 114 ++++++++++++ .../xiaoyu/rpc/core/config/RpcConfigTest.java | 12 ++ .../rpc/core/registry/LocalRegistryTest.java | 19 ++ .../nacos/NacosServiceDiscoveryTest.java | 169 ++++++++++++++++++ .../core/server/ServiceRepositoryTest.java | 52 ++++++ .../client/NettyRpcClientHandlerTest.java | 80 +++++++++ .../rpc/core/protocol/ProtocolTest.java | 18 ++ .../protocol/grpc/GrpcServerHandlerTest.java | 87 +++++++++ .../core/protocol/http/HttpRpcCodecTest.java | 72 ++++++++ .../core/protocol/netty/MyRpcCodecTest.java | 74 ++++++++ 13 files changed, 761 insertions(+), 15 deletions(-) create mode 100644 rpc-core/src/test/java/com/xiaoyu/rpc/core/client/RpcClientTest.java create mode 100644 rpc-core/src/test/java/com/xiaoyu/rpc/core/registry/nacos/NacosServiceDiscoveryTest.java create mode 100644 rpc-core/src/test/java/com/xiaoyu/rpc/core/server/ServiceRepositoryTest.java create mode 100644 rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/client/NettyRpcClientHandlerTest.java create mode 100644 rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcServerHandlerTest.java create mode 100644 rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/protocol/http/HttpRpcCodecTest.java create mode 100644 rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcCodecTest.java diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 2537f57..ab86993 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -7,9 +7,39 @@ on: branches: [ "main" ] jobs: + unit-tests: + runs-on: ubuntu-latest + timeout-minutes: 20 + + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + + - name: Run Maven Tests + run: | + set -euo pipefail + mvn -B -ntp clean test + + - name: Upload Surefire Reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: surefire-reports + path: | + **/target/surefire-reports/*.txt + **/target/surefire-reports/*.xml + integration-test: runs-on: ubuntu-latest - + timeout-minutes: 30 + needs: unit-tests + services: nacos: image: nacos/nacos-server:v2.2.3 @@ -25,7 +55,6 @@ jobs: --health-interval 10s --health-timeout 5s --health-retries 10 - steps: - uses: actions/checkout@v4 @@ -37,28 +66,26 @@ jobs: cache: maven - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.x' - - name: Install Python Dependencies run: | + set -euo pipefail python -m pip install --upgrade pip pip install grpcio grpcio-tools protobuf - - name: Build with Maven - run: mvn clean install -DskipTests - - - name: Run Unit Tests + - name: Build Jars For Integration Test run: | - echo "Running unit tests..." - mvn test -pl rpc-core + set -euo pipefail + mvn -B -ntp -DskipTests clean package - name: Start RPC Provider (Java Server) run: | + set -euo pipefail # Build classpath including project jars and dependencies - CLASSPATH="rpc-provider/target/rpc-provider-1.0-SNAPSHOT.jar:rpc-core/target/rpc-core-1.0-SNAPSHOT.jar:rpc-common/target/rpc-common-1.0-SNAPSHOT.jar:rpc-api/target/rpc-api-1.0-SNAPSHOT.jar:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout -pl rpc-provider -am)" + CLASSPATH="rpc-provider/target/rpc-provider-1.0-SNAPSHOT.jar:rpc-transport-netty/target/rpc-transport-netty-1.0-SNAPSHOT.jar:rpc-core/target/rpc-core-1.0-SNAPSHOT.jar:rpc-common/target/rpc-common-1.0-SNAPSHOT.jar:rpc-api/target/rpc-api-1.0-SNAPSHOT.jar:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout -pl rpc-provider -am)" echo "Starting Java Server..." # Run in background with nohup @@ -75,5 +102,16 @@ jobs: - name: Run Python Client working-directory: python_client run: | + set -euo pipefail echo "Running Python Client..." - python client.py + python client.py | tee ../python-client.log + grep -q "Message: Success" ../python-client.log + + - name: Upload CI Logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: ci-logs + path: | + server.log + python-client.log diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/RpcClient.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/RpcClient.java index 69ecbc9..3dd96ab 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/RpcClient.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/RpcClient.java @@ -8,6 +8,7 @@ import com.xiaoyu.rpc.core.transport.TransportClient; import java.net.InetSocketAddress; +import java.util.Objects; public class RpcClient { @@ -25,6 +26,11 @@ public RpcClient() { this.transportClient = transport.createClient(); } + RpcClient(TransportClient transportClient, ServiceDiscovery serviceDiscovery) { + this.transportClient = Objects.requireNonNull(transportClient, "transportClient"); + this.serviceDiscovery = Objects.requireNonNull(serviceDiscovery, "serviceDiscovery"); + } + public java.util.concurrent.CompletableFuture sendRequest(RpcRequest request, Class returnType) { try { // 先做一次服务发现(同步查找,通常会命中本地缓存) diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/nacos/NacosServiceDiscovery.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/nacos/NacosServiceDiscovery.java index 67085ce..c53ebda 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/nacos/NacosServiceDiscovery.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/nacos/NacosServiceDiscovery.java @@ -32,9 +32,14 @@ public class NacosServiceDiscovery implements ServiceDiscovery { private static final java.util.Set subscribedServices = java.util.concurrent.ConcurrentHashMap.newKeySet(); public NacosServiceDiscovery() { - this.namingService = NacosUtils.getNacosNamingService(); - String loadBalancerCode = RpcConfig.getInstance().getLoadBalancer(); - this.loadBalancer = ExtensionLoader.getExtensionLoader(LoadBalancer.class).getExtension(loadBalancerCode); + this(NacosUtils.getNacosNamingService(), + ExtensionLoader.getExtensionLoader(LoadBalancer.class) + .getExtension(RpcConfig.getInstance().getLoadBalancer())); + } + + NacosServiceDiscovery(NamingService namingService, LoadBalancer loadBalancer) { + this.namingService = namingService; + this.loadBalancer = loadBalancer; } @Override diff --git a/rpc-core/src/test/java/com/xiaoyu/rpc/core/client/RpcClientTest.java b/rpc-core/src/test/java/com/xiaoyu/rpc/core/client/RpcClientTest.java new file mode 100644 index 0000000..4e4403b --- /dev/null +++ b/rpc-core/src/test/java/com/xiaoyu/rpc/core/client/RpcClientTest.java @@ -0,0 +1,114 @@ +package com.xiaoyu.rpc.core.client; + +import com.google.protobuf.ByteString; +import com.xiaoyu.rpc.common.extension.ExtensionLoader; +import com.xiaoyu.rpc.common.serialization.Serializer; +import com.xiaoyu.rpc.common.vo.RpcRequest; +import com.xiaoyu.rpc.common.vo.RpcResponse; +import com.xiaoyu.rpc.core.config.RpcConfig; +import com.xiaoyu.rpc.core.registry.ServiceDiscovery; +import com.xiaoyu.rpc.core.transport.TransportClient; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.net.InetSocketAddress; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.*; + +@DisplayName("RpcClient 异常与边界测试") +public class RpcClientTest { + + @BeforeEach + void setUp() throws Exception { + System.setProperty("rpc.serializer", "java"); + resetRpcConfigSingleton(); + } + + @AfterEach + void tearDown() throws Exception { + System.clearProperty("rpc.serializer"); + resetRpcConfigSingleton(); + } + + @Test + @DisplayName("服务发现为空时返回异常 Future") + void testServiceNotFound() { + TransportClient transportClient = (request, address) -> CompletableFuture.completedFuture(null); + ServiceDiscovery serviceDiscovery = serviceName -> null; + RpcClient rpcClient = new RpcClient(transportClient, serviceDiscovery); + + CompletableFuture future = rpcClient.sendRequest(minimalRequest(), String.class); + + assertTrue(future.isCompletedExceptionally(), "Future should be completed exceptionally"); + ExecutionException ex = assertThrows(ExecutionException.class, () -> future.get(1, TimeUnit.SECONDS)); + assertTrue(ex.getCause().getMessage().contains("未发现服务"), "Error should mention service not found"); + } + + @Test + @DisplayName("传输层抛异常时返回异常 Future") + void testTransportThrows() { + TransportClient transportClient = (request, address) -> { + throw new RuntimeException("transport down"); + }; + ServiceDiscovery serviceDiscovery = serviceName -> new InetSocketAddress("127.0.0.1", 8080); + RpcClient rpcClient = new RpcClient(transportClient, serviceDiscovery); + + CompletableFuture future = rpcClient.sendRequest(minimalRequest(), String.class); + + assertTrue(future.isCompletedExceptionally(), "Future should be completed exceptionally"); + ExecutionException ex = assertThrows(ExecutionException.class, () -> future.get(1, TimeUnit.SECONDS)); + assertTrue(ex.getCause().getMessage().contains("transport down"), "Error should keep transport failure"); + } + + @Test + @DisplayName("返回非 RpcResponse 类型时应失败") + void testUnexpectedResponseType() { + TransportClient transportClient = (request, address) -> CompletableFuture.completedFuture("not-rpc-response"); + ServiceDiscovery serviceDiscovery = serviceName -> new InetSocketAddress("127.0.0.1", 8080); + RpcClient rpcClient = new RpcClient(transportClient, serviceDiscovery); + + CompletableFuture future = rpcClient.sendRequest(minimalRequest(), String.class); + + assertTrue(future.isCompletedExceptionally(), "Future should be completed exceptionally"); + ExecutionException ex = assertThrows(ExecutionException.class, () -> future.get(1, TimeUnit.SECONDS)); + assertTrue(ex.getCause().getMessage().contains("Unexpected response type"), "Error should mention type mismatch"); + } + + @Test + @DisplayName("RpcResponse 正常反序列化返回目标类型") + void testSuccessfulDeserialize() throws Exception { + Serializer serializer = ExtensionLoader.getExtensionLoader(Serializer.class).getExtension("java"); + byte[] body = serializer.serialize("hello"); + RpcResponse response = RpcResponse.newBuilder() + .setRequestId("req-1") + .setMessage("Success") + .setData(ByteString.copyFrom(body)) + .build(); + + TransportClient transportClient = (request, address) -> CompletableFuture.completedFuture(response); + ServiceDiscovery serviceDiscovery = serviceName -> new InetSocketAddress("127.0.0.1", 8080); + RpcClient rpcClient = new RpcClient(transportClient, serviceDiscovery); + + Object result = rpcClient.sendRequest(minimalRequest(), String.class).get(1, TimeUnit.SECONDS); + assertEquals("hello", result, "Response payload should be deserialized to String"); + } + + private static RpcRequest minimalRequest() { + return RpcRequest.newBuilder() + .setInterfaceName("com.example.DemoService") + .setMethodName("ping") + .build(); + } + + private static void resetRpcConfigSingleton() throws Exception { + Field field = RpcConfig.class.getDeclaredField("instance"); + field.setAccessible(true); + field.set(null, null); + } +} diff --git a/rpc-core/src/test/java/com/xiaoyu/rpc/core/config/RpcConfigTest.java b/rpc-core/src/test/java/com/xiaoyu/rpc/core/config/RpcConfigTest.java index b12a7b5..cf57ca6 100644 --- a/rpc-core/src/test/java/com/xiaoyu/rpc/core/config/RpcConfigTest.java +++ b/rpc-core/src/test/java/com/xiaoyu/rpc/core/config/RpcConfigTest.java @@ -23,6 +23,7 @@ void setUp() throws Exception { System.clearProperty("rpc.registry"); System.clearProperty("rpc.serializer"); System.clearProperty("rpc.server-port"); + System.clearProperty("rpc.transport"); } @AfterEach @@ -31,6 +32,7 @@ void tearDown() throws Exception { System.clearProperty("rpc.registry"); System.clearProperty("rpc.serializer"); System.clearProperty("rpc.server-port"); + System.clearProperty("rpc.transport"); // 重置单例 resetSingleton(); } @@ -91,6 +93,16 @@ void testSystemPropertyOverridePort() throws Exception { assertEquals(9999, config.getServerPort(), "Server port should be overridden by system property"); } + @Test + @DisplayName("测试系统属性覆盖 - 传输层") + void testSystemPropertyOverrideTransport() throws Exception { + System.setProperty("rpc.transport", "netty"); + resetSingleton(); + + RpcConfig config = RpcConfig.getInstance(); + assertEquals("netty", config.getTransport(), "Transport should be overridden by system property"); + } + @Test @DisplayName("测试 getSerializerCode 方法") void testGetSerializerCode() { diff --git a/rpc-core/src/test/java/com/xiaoyu/rpc/core/registry/LocalRegistryTest.java b/rpc-core/src/test/java/com/xiaoyu/rpc/core/registry/LocalRegistryTest.java index b71f3d0..184586b 100644 --- a/rpc-core/src/test/java/com/xiaoyu/rpc/core/registry/LocalRegistryTest.java +++ b/rpc-core/src/test/java/com/xiaoyu/rpc/core/registry/LocalRegistryTest.java @@ -1,6 +1,7 @@ package com.xiaoyu.rpc.core.registry; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.DisplayName; @@ -21,6 +22,11 @@ void setUp() { registry = new LocalRegistry(); } + @AfterEach + void tearDown() { + registry.clearRegistry(); + } + @Test @DisplayName("测试注册和查找服务") void testRegisterAndLookupService() { @@ -80,4 +86,17 @@ void testOverwriteService() { assertNotNull(result, "Should find service"); assertEquals(9090, result.getPort(), "Port should be updated to new address"); } + + @Test + @DisplayName("测试清空注册中心") + void testClearRegistry() { + String serviceName = "com.example.ToBeCleared"; + registry.registerService(serviceName, new InetSocketAddress("127.0.0.1", 8088)); + + assertNotNull(registry.lookupService(serviceName), "Service should exist before clear"); + + registry.clearRegistry(); + + assertNull(registry.lookupService(serviceName), "Service should be removed after clear"); + } } diff --git a/rpc-core/src/test/java/com/xiaoyu/rpc/core/registry/nacos/NacosServiceDiscoveryTest.java b/rpc-core/src/test/java/com/xiaoyu/rpc/core/registry/nacos/NacosServiceDiscoveryTest.java new file mode 100644 index 0000000..7913d92 --- /dev/null +++ b/rpc-core/src/test/java/com/xiaoyu/rpc/core/registry/nacos/NacosServiceDiscoveryTest.java @@ -0,0 +1,169 @@ +package com.xiaoyu.rpc.core.registry.nacos; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.pojo.Instance; +import com.xiaoyu.rpc.core.loadbalancer.LoadBalancer; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.lang.reflect.Proxy; +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.*; + +@DisplayName("NacosServiceDiscovery 缓存与回退测试") +public class NacosServiceDiscoveryTest { + + @BeforeEach + @SuppressWarnings("unchecked") + void setUp() throws Exception { + Field cacheField = NacosServiceDiscovery.class.getDeclaredField("serviceCache"); + cacheField.setAccessible(true); + ((Map>) cacheField.get(null)).clear(); + + Field subscribedField = NacosServiceDiscovery.class.getDeclaredField("subscribedServices"); + subscribedField.setAccessible(true); + ((Set) subscribedField.get(null)).clear(); + } + + @Test + @DisplayName("正常发现服务并且同服务只订阅一次") + void testLookupAndSubscribeOnce() { + AtomicInteger subscribeCount = new AtomicInteger(); + List instances = List.of(instance("10.0.0.1", 8080), instance("10.0.0.2", 8081)); + + NamingService namingService = namingServiceProxy((method, args) -> { + if ("getAllInstances".equals(method) && args.length == 1) { + return instances; + } + if ("subscribe".equals(method) && args.length == 2 && args[1] instanceof EventListener) { + subscribeCount.incrementAndGet(); + return null; + } + return null; + }); + + LoadBalancer loadBalancer = addresses -> addresses.get(0); + NacosServiceDiscovery discovery = new NacosServiceDiscovery(namingService, loadBalancer); + + InetSocketAddress first = discovery.lookupService("svc-a"); + InetSocketAddress second = discovery.lookupService("svc-a"); + + assertEquals("10.0.0.1", first.getHostString()); + assertEquals(8080, first.getPort()); + assertEquals("10.0.0.1", second.getHostString()); + assertEquals(1, subscribeCount.get(), "Same service should only subscribe once"); + } + + @Test + @DisplayName("Nacos 异常时回退到本地缓存") + @SuppressWarnings("unchecked") + void testFallbackToCacheOnNacosError() throws Exception { + Field cacheField = NacosServiceDiscovery.class.getDeclaredField("serviceCache"); + cacheField.setAccessible(true); + Map> cache = (Map>) cacheField.get(null); + cache.put("svc-b", new ArrayList<>(List.of(instance("127.0.0.1", 9000)))); + + NamingService namingService = namingServiceProxy((method, args) -> { + if ("getAllInstances".equals(method) && args.length == 1) { + throw new NacosException(500, "network down"); + } + if ("subscribe".equals(method)) { + return null; + } + return null; + }); + + LoadBalancer loadBalancer = addresses -> addresses.get(0); + NacosServiceDiscovery discovery = new NacosServiceDiscovery(namingService, loadBalancer); + + InetSocketAddress address = discovery.lookupService("svc-b"); + assertEquals("127.0.0.1", address.getHostString()); + assertEquals(9000, address.getPort()); + } + + @Test + @DisplayName("Nacos 空列表且无缓存时抛异常") + void testNoInstanceAndNoCache() { + NamingService namingService = namingServiceProxy((method, args) -> { + if ("getAllInstances".equals(method) && args.length == 1) { + return List.of(); + } + if ("subscribe".equals(method)) { + return null; + } + return null; + }); + + LoadBalancer loadBalancer = addresses -> addresses.get(0); + NacosServiceDiscovery discovery = new NacosServiceDiscovery(namingService, loadBalancer); + + RuntimeException ex = assertThrows(RuntimeException.class, () -> discovery.lookupService("svc-empty")); + assertTrue(ex.getMessage().contains("未找到服务"), "Should throw not found error"); + } + + private static Instance instance(String ip, int port) { + Instance i = new Instance(); + i.setIp(ip); + i.setPort(port); + return i; + } + + private static NamingService namingServiceProxy(Invocation invocation) { + return (NamingService) Proxy.newProxyInstance( + NacosServiceDiscoveryTest.class.getClassLoader(), + new Class[] { NamingService.class }, + (proxy, method, args) -> { + if (method.getDeclaringClass() == Object.class) { + return switch (method.getName()) { + case "toString" -> "NamingServiceProxy"; + case "hashCode" -> System.identityHashCode(proxy); + case "equals" -> proxy == args[0]; + default -> null; + }; + } + Object result = invocation.invoke(method.getName(), args == null ? new Object[0] : args); + if (result == null && method.getReturnType().isPrimitive()) { + if (method.getReturnType() == boolean.class) { + return false; + } + if (method.getReturnType() == byte.class) { + return (byte) 0; + } + if (method.getReturnType() == short.class) { + return (short) 0; + } + if (method.getReturnType() == int.class) { + return 0; + } + if (method.getReturnType() == long.class) { + return 0L; + } + if (method.getReturnType() == float.class) { + return 0F; + } + if (method.getReturnType() == double.class) { + return 0D; + } + if (method.getReturnType() == char.class) { + return '\0'; + } + } + return result; + }); + } + + @FunctionalInterface + private interface Invocation { + Object invoke(String method, Object[] args) throws Throwable; + } +} diff --git a/rpc-core/src/test/java/com/xiaoyu/rpc/core/server/ServiceRepositoryTest.java b/rpc-core/src/test/java/com/xiaoyu/rpc/core/server/ServiceRepositoryTest.java new file mode 100644 index 0000000..6e93859 --- /dev/null +++ b/rpc-core/src/test/java/com/xiaoyu/rpc/core/server/ServiceRepositoryTest.java @@ -0,0 +1,52 @@ +package com.xiaoyu.rpc.core.server; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +@DisplayName("ServiceRepository 服务仓库测试") +public class ServiceRepositoryTest { + + @BeforeEach + @SuppressWarnings("unchecked") + void setUp() throws Exception { + Field mapField = ServiceRepository.class.getDeclaredField("SERVICE_MAP"); + mapField.setAccessible(true); + ((Map) mapField.get(null)).clear(); + } + + @Test + @DisplayName("测试注册并查询服务") + void testRegisterAndGetService() { + String serviceName = "com.example.TestService"; + Object serviceBean = new Object(); + + ServiceRepository.registerService(serviceName, serviceBean); + + assertSame(serviceBean, ServiceRepository.getService(serviceName), "Should return same registered bean"); + } + + @Test + @DisplayName("测试覆盖注册服务") + void testOverwriteService() { + String serviceName = "com.example.TestService"; + Object oldBean = new Object(); + Object newBean = new Object(); + + ServiceRepository.registerService(serviceName, oldBean); + ServiceRepository.registerService(serviceName, newBean); + + assertSame(newBean, ServiceRepository.getService(serviceName), "Latest registration should overwrite old one"); + } + + @Test + @DisplayName("测试查询不存在的服务") + void testGetNonExistentService() { + assertNull(ServiceRepository.getService("com.example.NotFound"), "Unknown service should return null"); + } +} diff --git a/rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/client/NettyRpcClientHandlerTest.java b/rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/client/NettyRpcClientHandlerTest.java new file mode 100644 index 0000000..7781c57 --- /dev/null +++ b/rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/client/NettyRpcClientHandlerTest.java @@ -0,0 +1,80 @@ +package com.xiaoyu.rpc.core.client; + +import com.google.protobuf.ByteString; +import com.xiaoyu.rpc.common.vo.RpcResponse; +import io.netty.channel.embedded.EmbeddedChannel; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.*; + +@DisplayName("NettyRpcClientHandler 并发回调测试") +public class NettyRpcClientHandlerTest { + + @Test + @DisplayName("按 requestId 路由并支持乱序响应") + void testRouteResponseByRequestId() throws Exception { + NettyRpcClientHandler handler = new NettyRpcClientHandler(); + EmbeddedChannel channel = new EmbeddedChannel(handler); + + CompletableFuture f1 = new CompletableFuture<>(); + CompletableFuture f2 = new CompletableFuture<>(); + handler.addFuture("req-1", f1); + handler.addFuture("req-2", f2); + + channel.writeInbound(response("req-2", "ok-2")); + channel.writeInbound(response("req-1", "ok-1")); + + RpcResponse r2 = (RpcResponse) f2.get(1, TimeUnit.SECONDS); + RpcResponse r1 = (RpcResponse) f1.get(1, TimeUnit.SECONDS); + assertEquals("ok-2", r2.getMessage()); + assertEquals("ok-1", r1.getMessage()); + } + + @Test + @DisplayName("未知 requestId 的响应不会污染已挂起请求") + void testUnknownRequestId() { + NettyRpcClientHandler handler = new NettyRpcClientHandler(); + EmbeddedChannel channel = new EmbeddedChannel(handler); + + CompletableFuture future = new CompletableFuture<>(); + handler.addFuture("known", future); + + channel.writeInbound(response("unknown", "ignored")); + assertFalse(future.isDone(), "Unknown response should not complete unrelated future"); + } + + @Test + @DisplayName("连接异常时所有挂起请求应失败并清空") + void testExceptionCaughtFailsAllPending() { + NettyRpcClientHandler handler = new NettyRpcClientHandler(); + EmbeddedChannel channel = new EmbeddedChannel(handler); + + CompletableFuture f1 = new CompletableFuture<>(); + CompletableFuture f2 = new CompletableFuture<>(); + handler.addFuture("a", f1); + handler.addFuture("b", f2); + + RuntimeException cause = new RuntimeException("boom"); + channel.pipeline().fireExceptionCaught(cause); + + assertTrue(f1.isCompletedExceptionally()); + assertTrue(f2.isCompletedExceptionally()); + assertFalse(channel.isActive(), "Channel should be closed on exception"); + + assertThrows(ExecutionException.class, f1::get); + assertThrows(ExecutionException.class, f2::get); + } + + private static RpcResponse response(String requestId, String message) { + return RpcResponse.newBuilder() + .setRequestId(requestId) + .setMessage(message) + .setData(ByteString.EMPTY) + .build(); + } +} diff --git a/rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/protocol/ProtocolTest.java b/rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/protocol/ProtocolTest.java index 80c1768..e075d35 100644 --- a/rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/protocol/ProtocolTest.java +++ b/rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/protocol/ProtocolTest.java @@ -61,6 +61,24 @@ void testUnknownProtocolFallback() { assertTrue(protocol instanceof NettyProtocol, "Should fallback to NettyProtocol"); } + @Test + @DisplayName("测试空协议名默认回退到 Netty") + void testNullProtocolFallback() { + Protocol protocol = ProtocolFactory.getProtocol(null); + + assertNotNull(protocol, "Null protocol should fallback"); + assertTrue(protocol instanceof NettyProtocol, "Null protocol should fallback to NettyProtocol"); + } + + @Test + @DisplayName("测试空白协议名默认回退到 Netty") + void testBlankProtocolFallback() { + Protocol protocol = ProtocolFactory.getProtocol(" "); + + assertNotNull(protocol, "Blank protocol should fallback"); + assertTrue(protocol instanceof NettyProtocol, "Blank protocol should fallback to NettyProtocol"); + } + @Test @DisplayName("测试 ProtocolFactory 多次获取相同协议") void testProtocolCaching() { diff --git a/rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcServerHandlerTest.java b/rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcServerHandlerTest.java new file mode 100644 index 0000000..10d1567 --- /dev/null +++ b/rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcServerHandlerTest.java @@ -0,0 +1,87 @@ +package com.xiaoyu.rpc.core.protocol.grpc; + +import com.google.protobuf.ByteString; +import com.xiaoyu.rpc.common.vo.RpcRequest; +import com.xiaoyu.rpc.common.vo.RpcResponse; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.codec.http2.DefaultHttp2DataFrame; +import io.netty.handler.codec.http2.Http2DataFrame; +import io.netty.handler.codec.http2.Http2Headers; +import io.netty.handler.codec.http2.Http2HeadersFrame; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +@DisplayName("gRPC 服务端处理器帧测试") +public class GrpcServerHandlerTest { + + @Test + @DisplayName("数据帧不足 5 字节前缀时忽略") + void testIgnoreShortDataFrame() { + EmbeddedChannel channel = new EmbeddedChannel(new GrpcServerHandler(new ChannelInboundHandlerAdapter())); + Http2DataFrame frame = new DefaultHttp2DataFrame(Unpooled.wrappedBuffer(new byte[] { 1, 2, 3, 4 }), true); + + channel.writeInbound(frame); + assertNull(channel.readInbound(), "Short frame should not produce RpcRequest"); + } + + @Test + @DisplayName("完整 gRPC 数据帧可还原 RpcRequest") + void testDecodeGrpcRequestFrame() { + EmbeddedChannel channel = new EmbeddedChannel(new GrpcServerHandler(new ChannelInboundHandlerAdapter())); + RpcRequest request = RpcRequest.newBuilder() + .setInterfaceName("com.example.DemoService") + .setMethodName("hello") + .build(); + byte[] payload = request.toByteArray(); + + ByteBuf buf = Unpooled.buffer(); + buf.writeByte(0); + buf.writeInt(payload.length); + buf.writeBytes(payload); + + channel.writeInbound(new DefaultHttp2DataFrame(buf, true)); + Object inbound = channel.readInbound(); + + assertInstanceOf(RpcRequest.class, inbound); + assertEquals("hello", ((RpcRequest) inbound).getMethodName()); + } + + @Test + @DisplayName("RpcResponse 写出时应包含 headers/data/trailers") + void testWriteRpcResponseAsGrpcFrames() { + EmbeddedChannel channel = new EmbeddedChannel(new GrpcServerHandler(new ChannelInboundHandlerAdapter())); + RpcResponse response = RpcResponse.newBuilder() + .setRequestId("req-1") + .setMessage("Success") + .setData(ByteString.copyFromUtf8("ok")) + .build(); + + assertTrue(channel.writeOutbound(response)); + + Object first = channel.readOutbound(); + Object second = channel.readOutbound(); + Object third = channel.readOutbound(); + + assertInstanceOf(Http2HeadersFrame.class, first); + assertInstanceOf(Http2DataFrame.class, second); + assertInstanceOf(Http2HeadersFrame.class, third); + + Http2Headers headers = ((Http2HeadersFrame) first).headers(); + assertEquals("200", headers.status().toString()); + assertEquals("application/grpc", headers.get("content-type").toString()); + + ByteBuf data = ((Http2DataFrame) second).content(); + assertEquals(0, data.readByte(), "gRPC compressed flag should be 0"); + int len = data.readInt(); + assertTrue(len > 0, "Payload length should be positive"); + + Http2Headers trailers = ((Http2HeadersFrame) third).headers(); + assertEquals("0", trailers.get("grpc-status").toString()); + assertTrue(((Http2HeadersFrame) third).isEndStream(), "Trailer frame should end stream"); + } +} diff --git a/rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/protocol/http/HttpRpcCodecTest.java b/rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/protocol/http/HttpRpcCodecTest.java new file mode 100644 index 0000000..9ed3266 --- /dev/null +++ b/rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/protocol/http/HttpRpcCodecTest.java @@ -0,0 +1,72 @@ +package com.xiaoyu.rpc.core.protocol.http; + +import com.google.protobuf.ByteString; +import com.xiaoyu.rpc.common.extension.ExtensionLoader; +import com.xiaoyu.rpc.common.serialization.Serializer; +import com.xiaoyu.rpc.common.vo.RpcRequest; +import io.netty.buffer.ByteBuf; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpVersion; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +@DisplayName("HTTP 编解码边界测试") +public class HttpRpcCodecTest { + + @Test + @DisplayName("RpcRequest 编码为 HTTP POST 报文") + void testEncodeRequest() { + Serializer serializer = ExtensionLoader.getExtensionLoader(Serializer.class).getExtension("java"); + EmbeddedChannel channel = new EmbeddedChannel(new HttpRpcEncoder(serializer)); + + RpcRequest request = RpcRequest.newBuilder() + .setInterfaceName("com.example.DemoService") + .setMethodName("hello") + .addParamTypes("java.lang.String") + .addParameters(ByteString.copyFrom(serializer.serialize("world"))) + .build(); + + assertTrue(channel.writeOutbound(request)); + Object outbound = channel.readOutbound(); + assertInstanceOf(FullHttpRequest.class, outbound); + + FullHttpRequest httpRequest = (FullHttpRequest) outbound; + assertEquals(HttpMethod.POST, httpRequest.method()); + assertEquals("/", httpRequest.uri()); + assertEquals("hello", httpRequest.headers().get("Rpc-Method")); + assertEquals("application/x-rpc", httpRequest.headers().get("Content-Type")); + + byte[] body = bytes(httpRequest.content()); + RpcRequest decoded = serializer.deserialize(body, RpcRequest.class); + assertEquals("hello", decoded.getMethodName()); + } + + @Test + @DisplayName("HTTP 响应解码为目标对象") + void testDecodeResponse() { + Serializer serializer = ExtensionLoader.getExtensionLoader(Serializer.class).getExtension("java"); + EmbeddedChannel channel = new EmbeddedChannel(new HttpRpcDecoder(serializer, String.class)); + + byte[] body = serializer.serialize("pong"); + FullHttpResponse response = new io.netty.handler.codec.http.DefaultFullHttpResponse( + HttpVersion.HTTP_1_1, + HttpResponseStatus.OK, + io.netty.buffer.Unpooled.wrappedBuffer(body)); + + assertTrue(channel.writeInbound(response)); + Object inbound = channel.readInbound(); + assertEquals("pong", inbound); + } + + private static byte[] bytes(ByteBuf buf) { + byte[] bytes = new byte[buf.readableBytes()]; + buf.getBytes(buf.readerIndex(), bytes); + return bytes; + } +} diff --git a/rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcCodecTest.java b/rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcCodecTest.java new file mode 100644 index 0000000..ab80176 --- /dev/null +++ b/rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/protocol/netty/MyRpcCodecTest.java @@ -0,0 +1,74 @@ +package com.xiaoyu.rpc.core.protocol.netty; + +import com.google.protobuf.ByteString; +import com.xiaoyu.rpc.common.extension.ExtensionLoader; +import com.xiaoyu.rpc.common.serialization.Serializer; +import com.xiaoyu.rpc.common.serialization.SerializerCode; +import com.xiaoyu.rpc.common.vo.RpcRequest; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.embedded.EmbeddedChannel; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +@DisplayName("自定义 Netty 协议编解码测试") +public class MyRpcCodecTest { + + @Test + @DisplayName("请求报文编码后可被解码还原") + void testRoundTripRequest() { + Serializer serializer = ExtensionLoader.getExtensionLoader(Serializer.class).getExtension("java"); + EmbeddedChannel encoderChannel = new EmbeddedChannel(new MyRpcEncoder(serializer)); + EmbeddedChannel decoderChannel = new EmbeddedChannel(new MyRpcDecoder(RpcRequest.class)); + + RpcRequest request = RpcRequest.newBuilder() + .setInterfaceName("com.example.DemoService") + .setMethodName("echo") + .addParamTypes("java.lang.String") + .addParameters(ByteString.copyFrom(serializer.serialize("abc"))) + .build(); + + assertTrue(encoderChannel.writeOutbound(request)); + ByteBuf encoded = encoderChannel.readOutbound(); + assertTrue(decoderChannel.writeInbound(encoded)); + + Object decoded = decoderChannel.readInbound(); + assertInstanceOf(RpcRequest.class, decoded); + assertEquals("echo", ((RpcRequest) decoded).getMethodName()); + } + + @Test + @DisplayName("非法魔数应抛异常") + void testInvalidMagicNumber() { + EmbeddedChannel channel = new EmbeddedChannel(new MyRpcDecoder(RpcRequest.class)); + ByteBuf invalid = Unpooled.buffer(); + invalid.writeInt(0x11223344); + invalid.writeByte(0x01); + invalid.writeByte(SerializerCode.JAVA_SERIALIZER); + invalid.writeInt(0); + + assertThrows(RuntimeException.class, () -> { + channel.writeInbound(invalid); + channel.checkException(); + }); + } + + @Test + @DisplayName("超长消息应被拒绝并关闭连接") + void testOversizedFrameRejected() { + EmbeddedChannel channel = new EmbeddedChannel(new MyRpcDecoder(RpcRequest.class)); + ByteBuf invalid = Unpooled.buffer(); + invalid.writeInt(0xAABBCCDD); + invalid.writeByte(0x01); + invalid.writeByte(SerializerCode.JAVA_SERIALIZER); + invalid.writeInt(100 * 1024 * 1024); + + assertThrows(RuntimeException.class, () -> { + channel.writeInbound(invalid); + channel.checkException(); + }); + assertFalse(channel.isOpen(), "Channel should be closed after oversized frame"); + } +} From 480d8e162befbfc9eda492d8e11a468614913146 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Thu, 12 Feb 2026 18:17:05 +0800 Subject: [PATCH 26/38] =?UTF-8?q?=E5=AF=B9Go=E5=AE=A2=E6=88=B7=E7=AB=AF?= =?UTF-8?q?=E4=B9=9F=E6=8F=90=E4=BE=9BCI/CD=E6=B5=8B=E8=AF=95=E6=B5=81?= =?UTF-8?q?=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/maven.yml | 38 ++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index ab86993..df8e5fe 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -35,10 +35,31 @@ jobs: **/target/surefire-reports/*.txt **/target/surefire-reports/*.xml + go-client-check: + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go_client/go.mod + cache-dependency-path: go_client/go.sum + + - name: Verify Go Client Build + working-directory: go_client + run: | + set -euo pipefail + go mod download + go test ./... + go build -o /tmp/go-rpc-client main.go + integration-test: runs-on: ubuntu-latest timeout-minutes: 30 - needs: unit-tests + needs: [unit-tests, go-client-check] services: nacos: @@ -70,6 +91,12 @@ jobs: with: python-version: '3.x' + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go_client/go.mod + cache-dependency-path: go_client/go.sum + - name: Install Python Dependencies run: | set -euo pipefail @@ -107,6 +134,14 @@ jobs: python client.py | tee ../python-client.log grep -q "Message: Success" ../python-client.log + - name: Run Go Client + working-directory: go_client + run: | + set -euo pipefail + echo "Running Go Client..." + go run main.go | tee ../go-client.log + grep -q "Message: Success" ../go-client.log + - name: Upload CI Logs if: always() uses: actions/upload-artifact@v4 @@ -115,3 +150,4 @@ jobs: path: | server.log python-client.log + go-client.log From 4dbb69059a42fa4f65ba49a7cbe5a742b863fcb2 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Thu, 12 Feb 2026 18:26:35 +0800 Subject: [PATCH 27/38] =?UTF-8?q?=E6=9B=B4=E6=96=B0readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 26 ++++++++++++++------- README_ZH.md | 24 +++++++++++++------ rpc-core/src/main/resources/rpc-config.yaml | 2 +- 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index ffdf88e..cee593d 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,10 @@ The project is organized into the following modules to ensure separation of conc | **`rpc-transport-netty`** | The default transport implementation based on **Netty**. | | **`rpc-provider`** | Example provider application that implements and exports services. | | **`rpc-consumer`** | Example consumer application that imports and invokes services. | +| **`rpc-spring-boot-starter`** | Spring Boot auto-configuration starter for provider/consumer integration. | | **`rpc-benchmark`** | Performance benchmarking module using JMH (Java Microbenchmark Harness). | | **`python_client`** | Python client implementation demonstrating cross-language gRPC interoperability. | +| **`go_client`** | Go client implementation demonstrating cross-language gRPC interoperability. | ## ✨ Key Features @@ -49,7 +51,7 @@ The project is organized into the following modules to ensure separation of conc --- -## Quick Start +## Quick Start ### 1. Prerequisites (Nacos) @@ -67,6 +69,9 @@ docker run --name nacos-standalone \ Execute the following commands to start the Java RPC Provider. This will build the project and register the `HelloService` to your local Nacos instance. +The default `rpc.protocol` is `netty` for Java-to-Java Provider/Consumer calls. +If you need Python/Go interoperability, switch to `grpc` in `rpc-core/src/main/resources/rpc-config.yaml`. + ```bash # 1. Build Project mvn clean package -DskipTests @@ -96,7 +101,7 @@ mvn test -pl rpc-core,rpc-transport-netty Run the full integration test suite: ```bash -mvn test -pl rpc-consumer -Dtest=FullIntegrationTest +mvn test -pl rpc-consumer -am -Dtest=FullIntegrationTest ``` ### 5. Performance & Benchmark Results @@ -138,17 +143,22 @@ Configure the framework via `rpc-core/src/main/resources/rpc-config.yaml`. ```yaml rpc: - transport: "netty" # Transport: netty (default) - protocol: "http2" # Protocol: netty, http, http2 - server-host: 127.0.0.1 + transport: "netty" # Transport: netty + protocol: "netty" # Protocol: netty, http, http2 + server-host: "127.0.0.1" server-port: 8080 registry: "nacos" # Registry: nacos, local registry-address: "127.0.0.1:8848" - serializer: KRYO # Serializer: PROTOBUF, KRYO, JAVA, JSON - proxy: bytebuddy # Proxy: jdk, bytebuddy + serializer: "protobuf" # Serializer: protobuf, kryo, java, json + proxy: "bytebuddy" # Proxy: jdk, bytebuddy load-balancer: roundrobin # Load Balancer: roundrobin, random + max-message-size: 8388608 # 8MB ``` +> [!IMPORTANT] +> Use `netty`/`http`/`http2` with `rpc-consumer` (`RpcClientProxy`). +> Switch to `grpc` only when interoperating with standard grpc clients (Python/Go). + ## 🔌 SPI Design & Ecosystem XiaoYu RPC adheres to the **Microkernel Architecture**, where the core (`rpc-core`) only provides the lifecycle management and SPI (Service Provider Interface) definitions, while all specific functionalities are implemented as plugins. This design ensures the framework is highly extensible, lightweight, and follows the **Open-Closed Principle**. @@ -289,7 +299,7 @@ This framework supports interoperability with standard gRPC clients (e.g., Pytho java -cp rpc-provider/target/rpc-provider-1.0-SNAPSHOT.jar:rpc-transport-netty/target/rpc-transport-netty-1.0-SNAPSHOT.jar:rpc-core/target/rpc-core-1.0-SNAPSHOT.jar:rpc-common/target/rpc-common-1.0-SNAPSHOT.jar:rpc-api/target/rpc-api-1.0-SNAPSHOT.jar:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout -pl rpc-provider -am) com.xiaoyu.rpc.provider.ProviderApp ``` 3. **Run the Python Client**: - Refer to [python_client/client.py](file:///Users/yaoyao/Dev/JAVA_Dev_Project/gRPC/python_client/client.py) for details. + Refer to [`python_client/client.py`](python_client/client.py) for details. ```bash cd python_client diff --git a/README_ZH.md b/README_ZH.md index f66d6f6..a81a9d2 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -30,8 +30,10 @@ | **`rpc-transport-netty`** | 基于 **Netty** 实现的默认传输层模块。 | | **`rpc-provider`** | 示例服务提供者应用,用于实现并暴露服务。 | | **`rpc-consumer`** | 示例服务消费者应用,用于引入并调用服务。 | +| **`rpc-spring-boot-starter`** | Spring Boot 自动配置 Starter,便于集成 Provider/Consumer。 | | **`rpc-benchmark`** | 基于 JMH (Java Microbenchmark Harness) 的性能基准测试模块。 | | **`python_client`** | Python 客户端实现,用于演示跨语言 gRPC 互操作性。 | +| **`go_client`** | Go 客户端实现,用于演示跨语言 gRPC 互操作性。 | ## ✨ 核心特性 @@ -105,6 +107,9 @@ docker run --name nacos-standalone \ 执行以下命令启动 Java RPC Provider。这将编译项目并将 `HelloService` 注册到本地 Nacos 实例。 +默认 `rpc.protocol` 已设置为 `netty`,用于 Java-to-Java 的 Provider/Consumer 调用。 +若需要 Python/Go 跨语言互通,请在 `rpc-core/src/main/resources/rpc-config.yaml` 中手动切换到 `grpc`。 + ```bash # 1. 构建项目 mvn clean package -DskipTests @@ -134,7 +139,7 @@ mvn test -pl rpc-core,rpc-transport-netty 运行完整的集成测试套件: ```bash -mvn test -pl rpc-consumer -Dtest=FullIntegrationTest +mvn test -pl rpc-consumer -am -Dtest=FullIntegrationTest ``` ### 5. 基准测试与性能结果 @@ -175,17 +180,22 @@ XiaoYu RPC 专注于极致性能。以下是使用 **JMH** 测得的真实数据 ```yaml rpc: - transport: "netty" # 传输层: netty (默认) - protocol: "http2" # 协议: netty, http, http2 - server-host: 127.0.0.1 + transport: "netty" # 传输层: netty + protocol: "netty" # 协议: netty, http, http2 + server-host: "127.0.0.1" server-port: 8080 registry: "nacos" # 注册中心: nacos, local registry-address: "127.0.0.1:8848" - serializer: KRYO # 序列化器: PROTOBUF, KRYO, JAVA, JSON - proxy: bytebuddy # 代理方式: jdk, bytebuddy + serializer: "protobuf" # 序列化器: protobuf, kryo, java, json + proxy: "bytebuddy" # 代理方式: jdk, bytebuddy load-balancer: roundrobin # 负载均衡: roundrobin, random + max-message-size: 8388608 # 8MB ``` +> [!IMPORTANT] +> 若通过 `rpc-consumer` 的 `RpcClientProxy` 进行调用,请使用 `netty`/`http`/`http2` 协议。 +> `grpc` 仅建议用于与标准 grpc 客户端(Python/Go)互操作场景。 + --- ## ❓ 常见问题 (FAQ) @@ -293,7 +303,7 @@ rpc: ``` 3. **运行 Python 客户端**: - 详情请参考 [python_client/client.py](file:///Users/yaoyao/Dev/JAVA_Dev_Project/gRPC/python_client/client.py)。 + 详情请参考 [`python_client/client.py`](python_client/client.py)。 ```bash cd python_client diff --git a/rpc-core/src/main/resources/rpc-config.yaml b/rpc-core/src/main/resources/rpc-config.yaml index 819268c..0cf374c 100644 --- a/rpc-core/src/main/resources/rpc-config.yaml +++ b/rpc-core/src/main/resources/rpc-config.yaml @@ -1,5 +1,5 @@ rpc: - protocol: "grpc" + protocol: "netty" server-host: "127.0.0.1" server-port: 8080 registry: "nacos" From 7b03e8f550d87991db7dda53c95328376aa35579 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Thu, 12 Feb 2026 18:30:24 +0800 Subject: [PATCH 28/38] =?UTF-8?q?=E6=9B=B4=E6=96=B0readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 +++++++++++--- README_ZH.md | 14 +++++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index cee593d..2e7a349 100644 --- a/README.md +++ b/README.md @@ -279,15 +279,23 @@ This framework supports interoperability with standard gRPC clients (e.g., Pytho ### Usage Guide -1. **Configure Java Server**: - Update `rpc-config.yaml` to enable `grpc` protocol and `protobuf` serialization: +1. **Configure Java Server (gRPC Mode)**: + Update `rpc-core/src/main/resources/rpc-config.yaml` as follows (copy-paste ready): ```yaml rpc: + transport: "netty" protocol: "grpc" - serializer: "protobuf" + server-host: "127.0.0.1" + server-port: 8080 registry: "nacos" + registry-address: "127.0.0.1:8848" + serializer: "protobuf" + proxy: "bytebuddy" + load-balancer: "roundrobin" + max-message-size: 8388608 ``` + After multi-language tests, switch `protocol` back to `netty` (or `http`/`http2`) for Java-to-Java calls. 2. **Start the Java Provider (gRPC Mode)**: Run the following commands to build the project and start the server: diff --git a/README_ZH.md b/README_ZH.md index a81a9d2..927c9ea 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -282,15 +282,23 @@ rpc: ### 使用指南 -1. **配置 Java 服务端**: - 更新 `rpc-config.yaml` 启用 `grpc` 协议和 `protobuf` 序列化: +1. **配置 Java 服务端 (gRPC 模式)**: + 将 `rpc-core/src/main/resources/rpc-config.yaml` 调整为以下配置(可直接覆盖): ```yaml rpc: + transport: "netty" protocol: "grpc" - serializer: "protobuf" + server-host: "127.0.0.1" + server-port: 8080 registry: "nacos" + registry-address: "127.0.0.1:8848" + serializer: "protobuf" + proxy: "bytebuddy" + load-balancer: "roundrobin" + max-message-size: 8388608 ``` + 完成多语言联调后,如需恢复 Java-to-Java 调用,请将 `protocol` 改回 `netty`(或 `http`/`http2`)。 2. **启动 Java Provider (gRPC 模式)**: From b78d8115b923d377883aadaf66f6cf849b3d326a Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Thu, 12 Feb 2026 19:05:24 +0800 Subject: [PATCH 29/38] =?UTF-8?q?=E5=BF=98=E8=AE=B0=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E5=8E=9F=E6=9D=A5=E7=9A=84=E6=98=AFgrpc=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/maven.yml | 3 ++- .../java/com/xiaoyu/rpc/core/config/RpcConfig.java | 6 ++++++ .../com/xiaoyu/rpc/core/config/RpcConfigTest.java | 12 ++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index df8e5fe..afda34a 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -115,8 +115,9 @@ jobs: CLASSPATH="rpc-provider/target/rpc-provider-1.0-SNAPSHOT.jar:rpc-transport-netty/target/rpc-transport-netty-1.0-SNAPSHOT.jar:rpc-core/target/rpc-core-1.0-SNAPSHOT.jar:rpc-common/target/rpc-common-1.0-SNAPSHOT.jar:rpc-api/target/rpc-api-1.0-SNAPSHOT.jar:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout -pl rpc-provider -am)" echo "Starting Java Server..." + echo "Provider protocol override: rpc.protocol=grpc" # Run in background with nohup - nohup java -cp "$CLASSPATH" com.xiaoyu.rpc.provider.ProviderApp > server.log 2>&1 & + nohup java -Drpc.protocol=grpc -cp "$CLASSPATH" com.xiaoyu.rpc.provider.ProviderApp > server.log 2>&1 & # Wait for server port 8080 to be available echo "Waiting for Java Server to start on port 8080..." diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/config/RpcConfig.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/config/RpcConfig.java index 5ea7c7c..a408e58 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/config/RpcConfig.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/config/RpcConfig.java @@ -118,6 +118,12 @@ private void loadConfig() { this.transport = transportStr; log.info("检测到 System Property 覆盖传输层: {}", this.transport); } + + String protocolStr = System.getProperty("rpc.protocol"); + if (protocolStr != null) { + this.protocol = protocolStr; + log.info("检测到 System Property 覆盖协议: {}", this.protocol); + } } /** diff --git a/rpc-core/src/test/java/com/xiaoyu/rpc/core/config/RpcConfigTest.java b/rpc-core/src/test/java/com/xiaoyu/rpc/core/config/RpcConfigTest.java index cf57ca6..7d53bef 100644 --- a/rpc-core/src/test/java/com/xiaoyu/rpc/core/config/RpcConfigTest.java +++ b/rpc-core/src/test/java/com/xiaoyu/rpc/core/config/RpcConfigTest.java @@ -24,6 +24,7 @@ void setUp() throws Exception { System.clearProperty("rpc.serializer"); System.clearProperty("rpc.server-port"); System.clearProperty("rpc.transport"); + System.clearProperty("rpc.protocol"); } @AfterEach @@ -33,6 +34,7 @@ void tearDown() throws Exception { System.clearProperty("rpc.serializer"); System.clearProperty("rpc.server-port"); System.clearProperty("rpc.transport"); + System.clearProperty("rpc.protocol"); // 重置单例 resetSingleton(); } @@ -103,6 +105,16 @@ void testSystemPropertyOverrideTransport() throws Exception { assertEquals("netty", config.getTransport(), "Transport should be overridden by system property"); } + @Test + @DisplayName("测试系统属性覆盖 - 协议") + void testSystemPropertyOverrideProtocol() throws Exception { + System.setProperty("rpc.protocol", "grpc"); + resetSingleton(); + + RpcConfig config = RpcConfig.getInstance(); + assertEquals("grpc", config.getProtocol(), "Protocol should be overridden by system property"); + } + @Test @DisplayName("测试 getSerializerCode 方法") void testGetSerializerCode() { From 0fc42e60bae71dd476e2ba9719f653242a2346ab Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Thu, 12 Feb 2026 19:14:56 +0800 Subject: [PATCH 30/38] =?UTF-8?q?=E7=BB=99Java=E7=9A=84=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=AB=AF=E4=B9=9F=E6=B7=BB=E5=8A=A0=E4=BA=86gRPC=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=EF=BC=8C=E5=88=A0=E9=99=A4=E4=BA=86=E4=B9=8B=E5=89=8D?= =?UTF-8?q?=E7=9A=84gRPC=E4=BB=85=E4=BB=85=E6=94=AF=E6=8C=81=E8=B7=A8?= =?UTF-8?q?=E8=AF=AD=E8=A8=80=E5=AE=A2=E6=88=B7=E7=AB=AF=E7=9A=84=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +- README_ZH.md | 4 +- .../rpc/consumer/FullIntegrationTest.java | 2 +- .../grpc/GrpcClientResponseHandler.java | 77 +++++++++++++++++++ .../rpc/core/protocol/grpc/GrpcProtocol.java | 63 ++++++++++++++- .../grpc/GrpcClientResponseHandlerTest.java | 77 +++++++++++++++++++ 6 files changed, 220 insertions(+), 7 deletions(-) create mode 100644 rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcClientResponseHandler.java create mode 100644 rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcClientResponseHandlerTest.java diff --git a/README.md b/README.md index 2e7a349..516620e 100644 --- a/README.md +++ b/README.md @@ -156,8 +156,8 @@ rpc: ``` > [!IMPORTANT] -> Use `netty`/`http`/`http2` with `rpc-consumer` (`RpcClientProxy`). -> Switch to `grpc` only when interoperating with standard grpc clients (Python/Go). +> Default is `netty` for a faster local Java-to-Java path. +> `grpc` now also works with `rpc-consumer` (`RpcClientProxy`) and can be used for both Java and Python/Go interoperability. ## 🔌 SPI Design & Ecosystem diff --git a/README_ZH.md b/README_ZH.md index 927c9ea..3b9f1e0 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -193,8 +193,8 @@ rpc: ``` > [!IMPORTANT] -> 若通过 `rpc-consumer` 的 `RpcClientProxy` 进行调用,请使用 `netty`/`http`/`http2` 协议。 -> `grpc` 仅建议用于与标准 grpc 客户端(Python/Go)互操作场景。 +> 默认使用 `netty`,适合本地 Java-to-Java 的高性能链路。 +> 现在 `grpc` 也支持 `rpc-consumer`(`RpcClientProxy`)调用,可同时用于 Java 侧和 Python/Go 互操作。 --- diff --git a/rpc-consumer/src/test/java/com/xiaoyu/rpc/consumer/FullIntegrationTest.java b/rpc-consumer/src/test/java/com/xiaoyu/rpc/consumer/FullIntegrationTest.java index ea74566..cd38854 100644 --- a/rpc-consumer/src/test/java/com/xiaoyu/rpc/consumer/FullIntegrationTest.java +++ b/rpc-consumer/src/test/java/com/xiaoyu/rpc/consumer/FullIntegrationTest.java @@ -41,7 +41,7 @@ public void testFullIntegration() throws InterruptedException { // 沙箱或受限环境无法监听端口时,跳过此集成测试,避免把环境问题算成代码失败 Assumptions.assumeTrue(canBindLocalPort(9090), "No permission to bind local test port 9090"); - // 当前默认配置可能是 grpc,但客户端泛化 grpc 尚未支持,测试里强制切到 netty + // 集成测试固定走 netty,减少跨协议变量,确保该用例只验证端到端调用主链路 forceConfig("protocol", "netty"); // Start Server in a thread diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcClientResponseHandler.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcClientResponseHandler.java new file mode 100644 index 0000000..6fd54e0 --- /dev/null +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcClientResponseHandler.java @@ -0,0 +1,77 @@ +package com.xiaoyu.rpc.core.protocol.grpc; + +import com.xiaoyu.rpc.common.vo.RpcResponse; +import com.xiaoyu.rpc.core.client.NettyRpcClientHandler; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http2.Http2DataFrame; +import io.netty.handler.codec.http2.Http2Frame; +import io.netty.handler.codec.http2.Http2Headers; +import io.netty.handler.codec.http2.Http2HeadersFrame; + +/** + * 将 gRPC/HTTP2 帧转换为内部 RpcResponse,并交给通用客户端处理器完成 requestId 关联。 + */ +class GrpcClientResponseHandler extends SimpleChannelInboundHandler { + + private final NettyRpcClientHandler clientHandler; + private final String requestId; + + GrpcClientResponseHandler(NettyRpcClientHandler clientHandler, String requestId) { + this.clientHandler = clientHandler; + this.requestId = requestId; + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, Http2Frame frame) throws Exception { + if (frame instanceof Http2DataFrame) { + Http2DataFrame dataFrame = (Http2DataFrame) frame; + ByteBuf content = dataFrame.content(); + if (content.readableBytes() < 5) { + clientHandler.failRequest(requestId, new IllegalStateException("Invalid gRPC frame: missing 5-byte prefix")); + return; + } + + byte compressedFlag = content.readByte(); + if (compressedFlag != 0) { + clientHandler.failRequest(requestId, new UnsupportedOperationException("Compressed gRPC payload is not supported")); + return; + } + + int length = content.readInt(); + if (content.readableBytes() < length) { + clientHandler.failRequest(requestId, new IllegalStateException("Invalid gRPC frame: payload length mismatch")); + return; + } + + ByteBuf slice = content.readSlice(length); + RpcResponse response; + if (slice.nioBufferCount() > 0) { + response = RpcResponse.parseFrom(slice.nioBuffer()); + } else { + byte[] bytes = new byte[length]; + slice.readBytes(bytes); + response = RpcResponse.parseFrom(bytes); + } + ctx.fireChannelRead(response); + return; + } + + if (frame instanceof Http2HeadersFrame) { + Http2Headers headers = ((Http2HeadersFrame) frame).headers(); + CharSequence grpcStatus = headers.get("grpc-status"); + if (grpcStatus != null && !"0".contentEquals(grpcStatus)) { + CharSequence grpcMessage = headers.get("grpc-message"); + String message = grpcMessage == null ? "unknown grpc error" : grpcMessage.toString(); + clientHandler.failRequest(requestId, new RuntimeException("gRPC error status=" + grpcStatus + ", message=" + message)); + } + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + clientHandler.failRequest(requestId, cause); + ctx.close(); + } +} diff --git a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcProtocol.java b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcProtocol.java index 611411d..3271df3 100644 --- a/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcProtocol.java +++ b/rpc-transport-netty/src/main/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcProtocol.java @@ -5,10 +5,23 @@ import com.xiaoyu.rpc.core.protocol.Protocol; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http2.DefaultHttp2DataFrame; +import io.netty.handler.codec.http2.DefaultHttp2Headers; +import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; import io.netty.handler.codec.http2.Http2FrameCodecBuilder; +import io.netty.handler.codec.http2.Http2Headers; +import io.netty.handler.codec.http2.Http2Settings; +import io.netty.handler.codec.http2.Http2StreamChannel; +import io.netty.handler.codec.http2.Http2StreamChannelBootstrap; import io.netty.handler.codec.http2.Http2MultiplexHandler; +import io.netty.util.ReferenceCountUtil; + +import java.net.InetSocketAddress; public class GrpcProtocol implements Protocol { @@ -35,12 +48,58 @@ protected void initChannel(Channel ch) throws Exception { } })); } else { - throw new UnsupportedOperationException("Client side grpc not supported yet"); + pipeline.addLast(Http2FrameCodecBuilder.forClient() + .autoAckSettingsFrame(true) + .autoAckPingFrame(true) + .initialSettings(Http2Settings.defaultSettings().maxHeaderListSize(8192)) + .build()); + pipeline.addLast(new Http2MultiplexHandler(new ChannelInboundHandlerAdapter() { + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + // 连接级残留帧统一释放,避免引用计数对象泄漏 + ReferenceCountUtil.release(msg); + } + })); } } @Override public void sendRequest(Channel channel, RpcRequest request, NettyRpcClientHandler clientHandler) throws Exception { - throw new UnsupportedOperationException("Client side generic grpc not supported yet"); + Http2StreamChannelBootstrap streamBootstrap = new Http2StreamChannelBootstrap(channel); + streamBootstrap.open().addListener(openFuture -> { + if (!openFuture.isSuccess()) { + clientHandler.failRequest(request.getRequestId(), openFuture.cause()); + return; + } + + Http2StreamChannel streamChannel = (Http2StreamChannel) openFuture.getNow(); + streamChannel.pipeline().addLast(new GrpcClientResponseHandler(clientHandler, request.getRequestId())); + streamChannel.pipeline().addLast(clientHandler); + + byte[] payload = request.toByteArray(); + io.netty.buffer.ByteBuf body = streamChannel.alloc().buffer(payload.length + 5); + body.writeByte(0); // compressed-flag + body.writeInt(payload.length); + body.writeBytes(payload); + + Http2Headers headers = new DefaultHttp2Headers() + .method("POST") + .path("/GrpcService/handle") + .scheme("http") + .set(HttpHeaderNames.CONTENT_TYPE, "application/grpc") + .set(HttpHeaderNames.TE, "trailers"); + + if (channel.remoteAddress() instanceof InetSocketAddress) { + InetSocketAddress remote = (InetSocketAddress) channel.remoteAddress(); + headers.authority(remote.getHostString() + ":" + remote.getPort()); + } + + streamChannel.write(new DefaultHttp2HeadersFrame(headers, false)); + streamChannel.writeAndFlush(new DefaultHttp2DataFrame(body, true)).addListener(writeFuture -> { + if (!writeFuture.isSuccess()) { + clientHandler.failRequest(request.getRequestId(), writeFuture.cause()); + } + }); + }); } } diff --git a/rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcClientResponseHandlerTest.java b/rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcClientResponseHandlerTest.java new file mode 100644 index 0000000..ca0808f --- /dev/null +++ b/rpc-transport-netty/src/test/java/com/xiaoyu/rpc/core/protocol/grpc/GrpcClientResponseHandlerTest.java @@ -0,0 +1,77 @@ +package com.xiaoyu.rpc.core.protocol.grpc; + +import com.google.protobuf.ByteString; +import com.xiaoyu.rpc.common.vo.RpcResponse; +import com.xiaoyu.rpc.core.client.NettyRpcClientHandler; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.codec.http2.DefaultHttp2DataFrame; +import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; +import io.netty.handler.codec.http2.Http2Headers; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import static org.junit.jupiter.api.Assertions.*; + +@DisplayName("gRPC 客户端响应处理器测试") +class GrpcClientResponseHandlerTest { + + @Test + @DisplayName("应把 DataFrame 解码为 RpcResponse 并完成 future") + void testDecodeDataFrameToRpcResponse() throws Exception { + NettyRpcClientHandler clientHandler = new NettyRpcClientHandler(); + EmbeddedChannel channel = new EmbeddedChannel( + new GrpcClientResponseHandler(clientHandler, "req-1"), + clientHandler); + + CompletableFuture future = new CompletableFuture<>(); + clientHandler.addFuture("req-1", future); + + RpcResponse response = RpcResponse.newBuilder() + .setRequestId("req-1") + .setMessage("Success") + .setData(ByteString.copyFromUtf8("ok")) + .build(); + + byte[] payload = response.toByteArray(); + ByteBuf buf = Unpooled.buffer(); + buf.writeByte(0); + buf.writeInt(payload.length); + buf.writeBytes(payload); + + channel.writeInbound(new DefaultHttp2DataFrame(buf, true)); + + assertTrue(future.isDone(), "Future should be completed"); + Object result = future.get(); + assertInstanceOf(RpcResponse.class, result); + assertEquals("req-1", ((RpcResponse) result).getRequestId()); + assertEquals("Success", ((RpcResponse) result).getMessage()); + } + + @Test + @DisplayName("收到 grpc-status 非 0 时应异常完成 future") + void testFailFutureOnGrpcErrorStatus() { + NettyRpcClientHandler clientHandler = new NettyRpcClientHandler(); + EmbeddedChannel channel = new EmbeddedChannel( + new GrpcClientResponseHandler(clientHandler, "req-2"), + clientHandler); + + CompletableFuture future = new CompletableFuture<>(); + clientHandler.addFuture("req-2", future); + + Http2Headers trailers = new io.netty.handler.codec.http2.DefaultHttp2Headers() + .set("grpc-status", "13") + .set("grpc-message", "internal"); + channel.writeInbound(new DefaultHttp2HeadersFrame(trailers, true)); + + assertTrue(future.isCompletedExceptionally(), "Future should be completed exceptionally"); + ExecutionException ex = assertThrows(ExecutionException.class, future::get); + String message = ex.getCause().getMessage(); + assertNotNull(message, "Error message should not be null"); + assertTrue(message.toLowerCase().contains("grpc"), "Error message should include grpc details"); + } +} From 713da75a31e212338748d80e8f4a02b2e86ffa19 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Thu, 12 Feb 2026 19:17:27 +0800 Subject: [PATCH 31/38] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=AF=B9=E5=BA=94CI/CD?= =?UTF-8?q?=E7=9A=84=E9=80=BB=E8=BE=91=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/maven.yml | 58 +++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index afda34a..b4e14d2 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -127,21 +127,65 @@ jobs: # Optional: Print initial logs head -n 20 server.log - - name: Run Python Client + - name: Run Java Client Chain + continue-on-error: true + run: | + set -euo pipefail + mkdir -p .ci-status + CLASSPATH="rpc-consumer/target/rpc-consumer-1.0-SNAPSHOT.jar:rpc-transport-netty/target/rpc-transport-netty-1.0-SNAPSHOT.jar:rpc-core/target/rpc-core-1.0-SNAPSHOT.jar:rpc-common/target/rpc-common-1.0-SNAPSHOT.jar:rpc-api/target/rpc-api-1.0-SNAPSHOT.jar:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout -pl rpc-consumer -am)" + + echo "Running Java Client (grpc)..." + if java -Drpc.protocol=grpc -Drpc.serializer=protobuf -cp "$CLASSPATH" com.xiaoyu.rpc.consumer.ConsumerApp | tee java-client.log \ + && grep -q "Result1:" java-client.log \ + && grep -q "Result2:" java-client.log; then + echo "PASS" > .ci-status/java.status + else + echo "FAIL" > .ci-status/java.status + fi + + - name: Run Python Client Chain working-directory: python_client + continue-on-error: true run: | set -euo pipefail + mkdir -p ../.ci-status echo "Running Python Client..." - python client.py | tee ../python-client.log - grep -q "Message: Success" ../python-client.log + if python client.py | tee ../python-client.log && grep -q "Message: Success" ../python-client.log; then + echo "PASS" > ../.ci-status/python.status + else + echo "FAIL" > ../.ci-status/python.status + fi - - name: Run Go Client + - name: Run Go Client Chain working-directory: go_client + continue-on-error: true run: | set -euo pipefail + mkdir -p ../.ci-status echo "Running Go Client..." - go run main.go | tee ../go-client.log - grep -q "Message: Success" ../go-client.log + if go run main.go | tee ../go-client.log && grep -q "Message: Success" ../go-client.log; then + echo "PASS" > ../.ci-status/go.status + else + echo "FAIL" > ../.ci-status/go.status + fi + + - name: Aggregate Multi-Client Results + run: | + set -euo pipefail + JAVA_RESULT=$(cat .ci-status/java.status 2>/dev/null || echo "FAIL") + PY_RESULT=$(cat .ci-status/python.status 2>/dev/null || echo "FAIL") + GO_RESULT=$(cat .ci-status/go.status 2>/dev/null || echo "FAIL") + + echo "Multi-client chain summary:" + echo " Java : ${JAVA_RESULT}" + echo " Python : ${PY_RESULT}" + echo " Go : ${GO_RESULT}" + + if [[ "${JAVA_RESULT}" != "PASS" || "${PY_RESULT}" != "PASS" || "${GO_RESULT}" != "PASS" ]]; then + echo "At least one client chain failed." + exit 1 + fi + echo "All three client chains passed." - name: Upload CI Logs if: always() @@ -150,5 +194,7 @@ jobs: name: ci-logs path: | server.log + java-client.log python-client.log go-client.log + .ci-status/*.status From 20e78b2dac901196a341fc2ab949696529cda490 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Thu, 12 Feb 2026 19:19:34 +0800 Subject: [PATCH 32/38] =?UTF-8?q?=E5=B0=86CI/CD=E6=94=B9=E6=88=90=E5=B9=B6?= =?UTF-8?q?=E8=A1=8C=E8=BF=90=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/maven.yml | 84 +++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 35 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index b4e14d2..2b42aff 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -127,47 +127,61 @@ jobs: # Optional: Print initial logs head -n 20 server.log - - name: Run Java Client Chain - continue-on-error: true + - name: Wait For Service Registration In Nacos run: | set -euo pipefail - mkdir -p .ci-status - CLASSPATH="rpc-consumer/target/rpc-consumer-1.0-SNAPSHOT.jar:rpc-transport-netty/target/rpc-transport-netty-1.0-SNAPSHOT.jar:rpc-core/target/rpc-core-1.0-SNAPSHOT.jar:rpc-common/target/rpc-common-1.0-SNAPSHOT.jar:rpc-api/target/rpc-api-1.0-SNAPSHOT.jar:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout -pl rpc-consumer -am)" - - echo "Running Java Client (grpc)..." - if java -Drpc.protocol=grpc -Drpc.serializer=protobuf -cp "$CLASSPATH" com.xiaoyu.rpc.consumer.ConsumerApp | tee java-client.log \ - && grep -q "Result1:" java-client.log \ - && grep -q "Result2:" java-client.log; then - echo "PASS" > .ci-status/java.status - else - echo "FAIL" > .ci-status/java.status - fi + echo "Waiting for HelloService registration in Nacos..." + timeout 60s bash -c 'until curl -sf "http://localhost:8848/nacos/v1/ns/instance/list?serviceName=com.xiaoyu.rpc.api.HelloService" | grep -q "\"hosts\":\\[{"; do sleep 1; done' + echo "HelloService is discoverable from Nacos." - - name: Run Python Client Chain - working-directory: python_client - continue-on-error: true + - name: Run Multi-Client Chains In Parallel run: | set -euo pipefail - mkdir -p ../.ci-status - echo "Running Python Client..." - if python client.py | tee ../python-client.log && grep -q "Message: Success" ../python-client.log; then - echo "PASS" > ../.ci-status/python.status - else - echo "FAIL" > ../.ci-status/python.status - fi + mkdir -p .ci-status - - name: Run Go Client Chain - working-directory: go_client - continue-on-error: true - run: | - set -euo pipefail - mkdir -p ../.ci-status - echo "Running Go Client..." - if go run main.go | tee ../go-client.log && grep -q "Message: Success" ../go-client.log; then - echo "PASS" > ../.ci-status/go.status - else - echo "FAIL" > ../.ci-status/go.status - fi + CLASSPATH="rpc-consumer/target/rpc-consumer-1.0-SNAPSHOT.jar:rpc-transport-netty/target/rpc-transport-netty-1.0-SNAPSHOT.jar:rpc-core/target/rpc-core-1.0-SNAPSHOT.jar:rpc-common/target/rpc-common-1.0-SNAPSHOT.jar:rpc-api/target/rpc-api-1.0-SNAPSHOT.jar:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout -pl rpc-consumer -am)" + + run_java() { + echo "Running Java Client (grpc)..." + if java -Drpc.protocol=grpc -Drpc.serializer=protobuf -cp "$CLASSPATH" com.xiaoyu.rpc.consumer.ConsumerApp | tee java-client.log \ + && grep -q "Result1:" java-client.log \ + && grep -q "Result2:" java-client.log; then + echo "PASS" > .ci-status/java.status + else + echo "FAIL" > .ci-status/java.status + fi + } + + run_python() { + echo "Running Python Client..." + if (cd python_client && python client.py) | tee python-client.log \ + && grep -q "Message: Success" python-client.log; then + echo "PASS" > .ci-status/python.status + else + echo "FAIL" > .ci-status/python.status + fi + } + + run_go() { + echo "Running Go Client..." + if (cd go_client && go run main.go) | tee go-client.log \ + && grep -q "Message: Success" go-client.log; then + echo "PASS" > .ci-status/go.status + else + echo "FAIL" > .ci-status/go.status + fi + } + + run_java & + PID_JAVA=$! + run_python & + PID_PY=$! + run_go & + PID_GO=$! + + wait "${PID_JAVA}" || true + wait "${PID_PY}" || true + wait "${PID_GO}" || true - name: Aggregate Multi-Client Results run: | From 635e0c0654e0174d6d52c3027100ee671c8a2825 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Thu, 12 Feb 2026 19:26:55 +0800 Subject: [PATCH 33/38] =?UTF-8?q?=E4=BF=AEbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/maven.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 2b42aff..8400f07 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -130,9 +130,14 @@ jobs: - name: Wait For Service Registration In Nacos run: | set -euo pipefail - echo "Waiting for HelloService registration in Nacos..." - timeout 60s bash -c 'until curl -sf "http://localhost:8848/nacos/v1/ns/instance/list?serviceName=com.xiaoyu.rpc.api.HelloService" | grep -q "\"hosts\":\\[{"; do sleep 1; done' - echo "HelloService is discoverable from Nacos." + echo "Waiting for HelloService registration..." + if timeout 60s bash -c 'until grep -q "Service registered: com.xiaoyu.rpc.api.HelloService" server.log; do sleep 1; done'; then + echo "HelloService registration confirmed." + else + echo "Service registration wait timed out. Recent server logs:" + tail -n 120 server.log || true + exit 1 + fi - name: Run Multi-Client Chains In Parallel run: | From 4808fe5bc1224577c064268c77301b34b13cbf19 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Thu, 12 Feb 2026 19:33:42 +0800 Subject: [PATCH 34/38] =?UTF-8?q?=E4=BF=AEbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/maven.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 8400f07..2e1b7dc 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -69,6 +69,7 @@ jobs: AUTH_ENABLE: false ports: - 8848:8848 + - 9848:9848 # Wait for Nacos to be fully ready options: >- --name nacos-server @@ -108,6 +109,15 @@ jobs: set -euo pipefail mvn -B -ntp -DskipTests clean package + - name: Wait For Nacos Ports + run: | + set -euo pipefail + echo "Waiting for Nacos HTTP API on 8848..." + timeout 60s bash -c 'until curl -sf http://localhost:8848/nacos/v1/console/health/readiness > /dev/null; do sleep 1; done' + echo "Waiting for Nacos gRPC port on 9848..." + timeout 60s bash -c 'until echo > /dev/tcp/localhost/9848; do sleep 1; done' + echo "Nacos 8848/9848 are both reachable." + - name: Start RPC Provider (Java Server) run: | set -euo pipefail From 3e985610838c224f31faf771f4aff9d660c0bf56 Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Thu, 12 Feb 2026 19:42:26 +0800 Subject: [PATCH 35/38] =?UTF-8?q?=E4=BF=AEbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/maven.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 2e1b7dc..b90040e 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -127,7 +127,7 @@ jobs: echo "Starting Java Server..." echo "Provider protocol override: rpc.protocol=grpc" # Run in background with nohup - nohup java -Drpc.protocol=grpc -cp "$CLASSPATH" com.xiaoyu.rpc.provider.ProviderApp > server.log 2>&1 & + nohup java -Dlogback.configurationFile=rpc-core/src/main/resources/logback.xml -Drpc.protocol=grpc -cp "$CLASSPATH" com.xiaoyu.rpc.provider.ProviderApp > server.log 2>&1 & # Wait for server port 8080 to be available echo "Waiting for Java Server to start on port 8080..." @@ -158,31 +158,34 @@ jobs: run_java() { echo "Running Java Client (grpc)..." - if java -Drpc.protocol=grpc -Drpc.serializer=protobuf -cp "$CLASSPATH" com.xiaoyu.rpc.consumer.ConsumerApp | tee java-client.log \ + if timeout 120s java -Dlogback.configurationFile=rpc-core/src/main/resources/logback.xml -Drpc.protocol=grpc -Drpc.serializer=protobuf -cp "$CLASSPATH" com.xiaoyu.rpc.consumer.ConsumerApp | tee java-client.log \ && grep -q "Result1:" java-client.log \ && grep -q "Result2:" java-client.log; then echo "PASS" > .ci-status/java.status else + echo "Java client chain failed or timed out." echo "FAIL" > .ci-status/java.status fi } run_python() { echo "Running Python Client..." - if (cd python_client && python client.py) | tee python-client.log \ + if timeout 120s bash -c 'cd python_client && python client.py' | tee python-client.log \ && grep -q "Message: Success" python-client.log; then echo "PASS" > .ci-status/python.status else + echo "Python client chain failed or timed out." echo "FAIL" > .ci-status/python.status fi } run_go() { echo "Running Go Client..." - if (cd go_client && go run main.go) | tee go-client.log \ + if timeout 120s bash -c 'cd go_client && go run main.go' | tee go-client.log \ && grep -q "Message: Success" go-client.log; then echo "PASS" > .ci-status/go.status else + echo "Go client chain failed or timed out." echo "FAIL" > .ci-status/go.status fi } From 248a97f6327b19792940cf968baa971880ee913e Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Thu, 12 Feb 2026 19:43:38 +0800 Subject: [PATCH 36/38] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E7=BA=A7=E5=88=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rpc-core/src/main/resources/logback.xml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 rpc-core/src/main/resources/logback.xml diff --git a/rpc-core/src/main/resources/logback.xml b/rpc-core/src/main/resources/logback.xml new file mode 100644 index 0000000..1572866 --- /dev/null +++ b/rpc-core/src/main/resources/logback.xml @@ -0,0 +1,20 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger -- %msg%n + + + + + + + + + + + + + + + From 554dc6e5c7c688e70d62afda57d1f5b48bad5d8d Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Thu, 12 Feb 2026 19:55:14 +0800 Subject: [PATCH 37/38] =?UTF-8?q?=E4=BF=AE=E5=A4=8DCD=E6=B5=81=E6=B0=B4?= =?UTF-8?q?=E7=BA=BFbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/xiaoyu/rpc/consumer/ConsumerApp.java | 2 ++ .../xiaoyu/rpc/core/client/ByteBuddyProxyFactory.java | 10 +++++++--- .../com/xiaoyu/rpc/core/client/JdkProxyFactory.java | 8 +++++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/rpc-consumer/src/main/java/com/xiaoyu/rpc/consumer/ConsumerApp.java b/rpc-consumer/src/main/java/com/xiaoyu/rpc/consumer/ConsumerApp.java index 04b4323..1da9767 100644 --- a/rpc-consumer/src/main/java/com/xiaoyu/rpc/consumer/ConsumerApp.java +++ b/rpc-consumer/src/main/java/com/xiaoyu/rpc/consumer/ConsumerApp.java @@ -22,6 +22,8 @@ public static void main(String[] args) { } catch (Exception e) { e.printStackTrace(); + System.exit(1); } + System.exit(0); } } diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/ByteBuddyProxyFactory.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/ByteBuddyProxyFactory.java index d7b2617..cacbafa 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/ByteBuddyProxyFactory.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/ByteBuddyProxyFactory.java @@ -11,6 +11,7 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; +import java.util.concurrent.CompletableFuture; public class ByteBuddyProxyFactory implements ProxyFactory { @@ -51,9 +52,12 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl } RpcRequest request = builder.build(); - // 这里不阻塞等待结果,直接把 CompletableFuture 返回给上层调用方。 - // 如果业务接口不是异步返回类型,运行时会出现类型不匹配。 - return rpcClient.sendRequest(request, method.getReturnType()); + CompletableFuture future = rpcClient.sendRequest(request, method.getReturnType()); + // 如果业务接口声明的返回类型是异步的,直接返回 Future;否则阻塞等待结果 + if (CompletableFuture.class.isAssignableFrom(method.getReturnType())) { + return future; + } + return future.get(); } })).make().load(clazz.getClassLoader()).getLoaded().getConstructor().newInstance(); } catch (Exception e) { diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/JdkProxyFactory.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/JdkProxyFactory.java index 788e7c4..1f30ac4 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/JdkProxyFactory.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/JdkProxyFactory.java @@ -9,6 +9,7 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.util.concurrent.CompletableFuture; public class JdkProxyFactory implements ProxyFactory { @@ -43,7 +44,12 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl } RpcRequest request = builder.build(); - return new RpcClient().sendRequest(request, method.getReturnType()); + CompletableFuture future = new RpcClient().sendRequest(request, method.getReturnType()); + // 如果业务接口声明的返回类型是异步的,直接返回 Future;否则阻塞等待结果 + if (CompletableFuture.class.isAssignableFrom(method.getReturnType())) { + return future; + } + return future.get(); } }); } From 31e55d59dcc3494ba282b02ffe010154c323befe Mon Sep 17 00:00:00 2001 From: yaojingxi <23722032@bjtu.edu.cn> Date: Fri, 13 Feb 2026 11:20:53 +0800 Subject: [PATCH 38/38] =?UTF-8?q?=E6=9B=B4=E6=96=B0readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++ README_ZH.md | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/README.md b/README.md index 516620e..27c797a 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,68 @@ The project is organized into the following modules to ensure separation of conc | **`python_client`** | Python client implementation demonstrating cross-language gRPC interoperability. | | **`go_client`** | Go client implementation demonstrating cross-language gRPC interoperability. | +### System Architecture Diagram + +```mermaid +flowchart LR + subgraph External["External Systems"] + E1["python_client (grpc-python)"] + E2["go_client (grpc-go)"] + E3["Nacos Registry"] + end + + subgraph Internal["Internal Components"] + subgraph Clients["Java Clients"] + C1["rpc-consumer"] + C2["Spring Boot App"] + end + + subgraph Core["rpc-core (Microkernel)"] + P1["ProxyFactory (JDK/ByteBuddy)"] + P2["RpcClient / RpcServer"] + P3["ExtensionLoader (SPI)"] + end + + subgraph Plugins["SPI Plugins"] + S1["Protocol: Netty / HTTP / HTTP2 / gRPC"] + S2["Serializer: Protobuf / Kryo / JSON / Java"] + S3["LoadBalancer: RoundRobin / Random"] + S4["Registry: Nacos / Local"] + S5["Transport: rpc-transport-netty"] + end + + subgraph Provider["Service Provider"] + M1["rpc-provider"] + M2["HelloServiceImpl"] + end + end + + A1["rpc-api (service interfaces)"] + A2["rpc-common (RpcRequest/RpcResponse + proto)"] + + C1 --> P1 + C2 --> P1 + E1 -->|gRPC / HTTP2 + Protobuf| S1 + E2 -->|gRPC / HTTP2 + Protobuf| S1 + P1 --> P2 + P2 --> S3 + P2 --> S4 + P2 --> S5 + P2 --> S2 + S5 --> S1 + S1 --> M1 + M1 --> M2 + S4 <-->|service register/discover| E3 + A1 -.shared API.-> C1 + A1 -.shared API.-> M1 + A2 -.shared model.-> P2 + P3 -.loads.-> S1 + P3 -.loads.-> S2 + P3 -.loads.-> S3 + P3 -.loads.-> S4 + P3 -.loads.-> S5 +``` + ## ✨ Key Features - **🔌 Plugin-based Architecture**: Leverages a custom SPI mechanism for maximum flexibility. diff --git a/README_ZH.md b/README_ZH.md index 3b9f1e0..6f7beaa 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -35,6 +35,68 @@ | **`python_client`** | Python 客户端实现,用于演示跨语言 gRPC 互操作性。 | | **`go_client`** | Go 客户端实现,用于演示跨语言 gRPC 互操作性。 | +### 系统架构图 + +```mermaid +flowchart LR + subgraph External["外部系统"] + E1["python_client (grpc-python)"] + E2["go_client (grpc-go)"] + E3["Nacos 注册中心"] + end + + subgraph Internal["内部组件"] + subgraph Clients["Java 客户端"] + C1["rpc-consumer"] + C2["Spring Boot 应用"] + end + + subgraph Core["rpc-core (微内核)"] + P1["ProxyFactory (JDK/ByteBuddy)"] + P2["RpcClient / RpcServer"] + P3["ExtensionLoader (SPI)"] + end + + subgraph Plugins["SPI 插件层"] + S1["Protocol: Netty / HTTP / HTTP2 / gRPC"] + S2["Serializer: Protobuf / Kryo / JSON / Java"] + S3["LoadBalancer: RoundRobin / Random"] + S4["Registry: Nacos / Local"] + S5["Transport: rpc-transport-netty"] + end + + subgraph Provider["服务提供端"] + M1["rpc-provider"] + M2["HelloServiceImpl"] + end + end + + A1["rpc-api (服务接口)"] + A2["rpc-common (请求响应模型 + proto)"] + + C1 --> P1 + C2 --> P1 + E1 -->|gRPC / HTTP2 + Protobuf| S1 + E2 -->|gRPC / HTTP2 + Protobuf| S1 + P1 --> P2 + P2 --> S3 + P2 --> S4 + P2 --> S5 + P2 --> S2 + S5 --> S1 + S1 --> M1 + M1 --> M2 + S4 <-->|服务注册/发现| E3 + A1 -.共享接口.-> C1 + A1 -.共享接口.-> M1 + A2 -.共享模型.-> P2 + P3 -.加载.-> S1 + P3 -.加载.-> S2 + P3 -.加载.-> S3 + P3 -.加载.-> S4 + P3 -.加载.-> S5 +``` + ## ✨ 核心特性 - **🔌 插件化架构**: 采用自定义 SPI 机制,实现最大程度的灵活性。