Skip to content

Repository files navigation

🚀 XiaoYu RPC Framework

A lightweight, high-performance, and extensible RPC framework based on Netty, Nacos, and ByteBuddy. Supports multiple protocols including HTTP/2, HTTP/1.1, and custom Netty protocols.

Java Netty Nacos License


📖 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 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.

🏗️ 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 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.
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.

System Architecture Diagram

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
Loading

✨ Key Features

  • 🔌 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.
  • 📦 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.

Quick Start

1. Prerequisites (Nacos)

Start Nacos using Docker:

docker run --name nacos-standalone \
    -e MODE=standalone \
    -p 8848:8848 \
    -p 9848:9848 \
    -d nacos/nacos-server:v2.3.1-slim

2. Run the Provider

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.

# 1. Build Project
mvn clean package -DskipTests

# 2. Start 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. Run the Consumer

Execute the ConsumerApp in the rpc-consumer module to make calls to the provider.

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 protocols:

mvn test -pl rpc-core,rpc-transport-netty

Integration Tests: Run the full integration test suite:

mvn test -pl rpc-consumer -am -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.

5.3 End-to-End Load Test (Business Simulation)

Use LoadTestApp to simulate microservice-style calls with configurable concurrency, duration, payload size, and output file. It reports QPS, P50/P95/P99 latency, error rate, and basic GC/heap stats.

1) Build

mvn -pl rpc-consumer -am -DskipTests package

2) Start Provider

./run_server.sh

3) Run Load Test Client

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.LoadTestApp \
--threads=200 --warmup=5 --duration=30 --payload=128 --output=loadtest-results.txt

Parameters

  • --threads=NUM Worker threads (default 200)
  • --warmup=SEC Warmup seconds (default 5)
  • --duration=SEC Measurement seconds (default 30)
  • --payload=BYTES Payload size in bytes (default 128)
  • --sample-size=NUM Latency sample size (default 1,000,000)
  • --output=PATH Output file path (default loadtest-results.txt)
  • --append Append to output file

Output format (one line per run)

time=2026-04-29T12:34:56Z threads=200 warmupSec=5 durationSec=30 payloadBytes=128 total=123456 success=123000 error=456 qps=4115.20 successQps=4100.00 errorRatePct=0.37 avgLatencyMs=0.410 minMs=0.120 p50Ms=0.300 p95Ms=0.900 p99Ms=1.500 maxMs=5.000 samples=1000000 heapUsedBytes=12345678 heapTotalBytes=268435456 gcCount=2 gcTimeMs=15

🛠️ Configuration

Configure the framework via rpc-core/src/main/resources/rpc-config.yaml.

rpc:
  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: "protobuf"     # Serializer: protobuf, kryo, java, json
  proxy: "bytebuddy"         # Proxy: jdk, bytebuddy
  load-balancer: roundrobin  # Load Balancer: roundrobin, random
  max-message-size: 8388608  # 8MB

Important

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

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.

🧩 Core Extension Points

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

  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:
    my-serializer=com.example.MyCustomSerializer
  4. Use It: update rpc-config.yaml:
    rpc:
      serializer: my-serializer

❓ FAQ

Q: Why ByteBuddy? A: CGLIB is problematic on Java 17+ due to deep reflection restrictions. ByteBuddy is the modern industry standard for bytecode manipulation.

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: 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

<dependency>
    <groupId>com.xiaoyu.rpc</groupId>
    <artifactId>rpc-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

Provider Example

@RpcService
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        return "Hello, " + name;
    }
}

Consumer Example

@RestController
public class HelloController {
    @RpcReference
    private HelloService helloService;

    @GetMapping("/hello")
    public String hello(@RequestParam String name) {
        return helloService.sayHello(name);
    }
}

Configuration (application.yml)

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

🌐 Multi-Language gRPC Support (Python & Go)

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

  • 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 Integration: Services registered in Nacos can be discovered and invoked.

Usage Guide

  1. Configure Java Server (gRPC Mode): Update rpc-core/src/main/resources/rpc-config.yaml as follows (copy-paste ready):

    rpc:
      transport: "netty"
      protocol: "grpc"
      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:

    # Build the project (skip tests to speed up)
    mvn clean package -DskipTests
    
    # Run the 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. Run the Python Client: Refer to python_client/client.py for details.

    cd python_client
    # ... (existing steps)
    python3 client.py
  4. Run the Go Client: Navigate to the go_client directory and run:

    cd go_client
    go run main.go

    Expected Output:

    Sending RpcRequest: interface=com.xiaoyu.rpc.api.HelloService, method=sayHello, param=World
    RpcResponse received:
    Data: Hello, World!
    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


🔬 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

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

Contributions are welcome! Feel free to open issues or submit pull requests to improve the framework.

About

模仿grpc搭建的一个远程调用框架(支持多种协议的切换和SPI自己注入协议内容),手动实现了gRPC的协议,实现了和原官方gRPC框架的python客户端的无缝对接

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages