Java 17 | Netty 4.1 | Spring Boot 3.5 | SPI 全可插拔 | 开箱即用

在 Java RPC 的世界里,Dubbo 和 gRPC 早已是家喻户晓的名字。但当你真正去读它们的源码时,往往会陷入几十万行代码的汪洋——核心逻辑被淹没在大量的兼容性代码、扩展点和历史包袱中。

如果你渴望有一个 RPC 框架,代码量可控、架构清晰、每一行都有意义,同时又不失生产级该有的核心能力——那么 Jaws 正是为此而生。

🎯 为什么需要 Jaws?

痛点一:RPC 框架"会用但不懂"

大多数开发者对 RPC 框架的使用停留在配置层面——加个注解、写个 YAML、服务就跑起来了。但背后的协议编解码、服务注册发现、负载均衡、容错机制,几乎是黑盒。

面试被问到"RPC 框架原理"时,你能讲清楚几层?

痛点二:生产级能力不是"可选项"

很多教学型 RPC Demo 只实现了最基本的调用链路,缺少:

  • 优雅停机(发布时丢请求)
  • 泛化调用(网关无法依赖每个服务的接口 JAR)
  • 可观测性(链路追踪和指标采集)
  • 方法级别配置(不同方法不同超时/重试策略)
  • 主流注册中心适配(Nacos / ZooKeeper 双支持,一键切换)

这些在生产环境中都是刚需。

痛点三:Spring Boot 集成体验参差不齐

有些 RPC 框架的 Spring Boot 集成还停留在 XML 配置或复杂的 Bean 定义阶段。开发者需要的是:两个注解搞定一切

🚀 项目亮点

✨ 1. 自定义二进制协议

基于 Netty 实现的 jaws 二进制协议,结构清晰,编解码过程完全透明:

┌──────────┬──────────┬──────────┬──────────────┬──────────┬──────────┐
│  magic   │   type   │   id     │ serialization│  status  │  body    │
│  2 bytes │ 1 byte   │ 8 bytes │   1 byte     │ 1 byte   │ variable │
└──────────┴──────────┴──────────┴──────────────┴──────────┴──────────┘

支持 fastjson2hessian2 两种序列化方式,可通过配置一键切换。

🏗️ 2. 双注册中心支持

注册中心 依赖 说明
ZooKeeper jaws-registry-zookeeper 基于 Curator 5.9,临时节点 + 心跳续约
Nacos jaws-registry-nacos 基于 Nacos 3.2.2,支持心跳续约与失败重连

切换注册中心只需更换一个 Maven 依赖和一行配置,业务代码零改动。

🔍 3. 五种负载均衡策略

策略 适用场景
Random 默认,简单高效
RoundRobin 均匀分配
LeastActive 热点感知,慢节点自动降权
ShortestResponse 响应时间感知,选择预估最快的节点
ConsistentHash 相同参数路由到同一节点,适合缓存场景

所有策略均通过 SPI 可插拔,你可以轻松实现自定义策略。

🛡️ 4. 高可用容错

  • Failover — 调用失败自动切换到其他节点,可配置重试次数
  • Failfast — 快速失败,适用于非幂等写操作

⚙️ 5. 四阶段优雅停机

这是很多 RPC 教程忽略但生产环境至关重要的能力。Jaws 实现了完整的停机流程,确保零请求丢失、零损伤发布

Phase 1: stopAccept()              关闭 ServerChannel,不再 accept 新连接
Phase 2: awaitInactiveRequests()   轮询 activeRequests 计数器,等待在途请求完成(默认 10s)
Phase 3: unregister()              从注册中心注销所有服务 URL
Phase 4: unexport() → destroy()    移除 Provider、关闭连接、释放 EventLoopGroup 和线程池

验证方式

# 启动 Provider
./run-sample.sh provider

# 另开终端运行 Consumer
./run-sample.sh consumer

# 发送 SIGTERM 信号,观察四阶段日志
jps | grep SampleProvider   # 找到 PID
kill -TERM <PID>

# Provider 日志依次输出:
# [GracefulShutdown] Phase 1: Stop accepting new requests
# [GracefulShutdown] Phase 2: Waiting for in-flight requests to complete
# [GracefulShutdown] Phase 3: Unregister from registry
# [GracefulShutdown] Phase 4: Close connections and release resources

🎨 6. 泛化调用

无需依赖 Provider 的接口 JAR 包即可发起 RPC 调用。API 网关、测试平台、Mock 服务的必备能力:

// Spring Boot 注解方式
@JawsReference(generic = true, serviceInterface = "com.example.DemoService")
private GenericService demoService;

// 调用
Object result = demoService.$invoke("hello",
    new String[]{"java.lang.String"},
    new Object[]{"jaws"});

Provider 端自动完成 Map↔POJO 参数转换,调用方完全无感知。

📊 7. 可观测性

引入 jaws-observability-spring-boot-starter 即可获得完整的可观测性能力:

能力 实现 说明
链路追踪 Micrometer Tracing + OpenTelemetry W3C TraceContext 格式自动传播,全链路 traceId 一致
指标采集 Micrometer RPC 调用次数、成功率、耗时分布、活跃请求数,side tag 区分 consumer/provider
日志关联 OTel 日志桥接 traceId/spanId 自动注入 MDC,无需手动处理

无需额外代码,Filter SPI 自动生效。

💡 8. 方法级别配置

全局配置不够精细?Jaws 支持在消费端为单个方法设置独立的超时和重试策略:

@JawsReference(methods = {
    @Method(name = "hello", timeout = 5000, retries = 3),   // 这个方法单独配置
    @Method(name = "getUser", timeout = 500)                 // 这个方法快速失败
})
private DemoService demoService;

🔌 9. injvm 协议

JVM 内部直调,零网络开销,适合本地开发和单元测试:

./run-sample.sh injvm

无需任何中间件,一行命令即可运行。

🧰 10. RpcContext

消费端可获取实际调用的服务端地址,提供端可获取调用方 IP:

// Consumer 端
URL serverUrl = RpcContext.getContext().getServerUrl();

// Provider 端
String callerIp = RpcContext.getContext().getCallerIp();

🏛️ 架构设计

jaws-parent
├── jaws-core                  # 核心:协议抽象、SPI、序列化、集群、路由、Filter、配置
├── jaws-transport-netty       # Netty 4.1 传输层实现
├── jaws-registry-zookeeper    # ZooKeeper 注册中心
├── jaws-registry-nacos        # Nacos 3.x 注册中心
├── jaws-extensions            # 可观测性:Micrometer 指标 + OpenTelemetry 链路追踪
├── jaws-spring-boot
│   ├── jaws-spring-boot-starter                # Spring Boot 自动配置
│   └── jaws-observability-spring-boot-starter  # 可观测性自动装配
└── jaws-samples
    ├── jaws-sample-api        # 接口定义
    ├── jaws-sample-injvm      # injvm 协议示例(无需 ZK)
    ├── jaws-sample-provider   # 服务提供者(ZooKeeper)
    ├── jaws-sample-consumer   # 服务消费者 + 泛化调用
    ├── jaws-sample-provider-boot  # Spring Boot Provider(Nacos)
    ├── jaws-sample-consumer-boot  # Spring Boot Consumer
    └── jaws-sample-benchmark  # 性能基准测试

核心设计理念

  1. SPI 全可插拔:Protocol、Cluster、LoadBalance、Filter、Serialization 等所有核心组件均通过 @SPI + @Activation 机制可插拔,替换任何一层不影响其他模块
  2. 分层解耦:core 保持轻量,extensions 承载附加功能,starter 负责自动装配
  3. 配置模型统一:URL 参数 → ServiceConfig/ReferenceConfig → Spring YAML,三层配置自动映射

🎓 学习价值

对于想理解 RPC 原理的开发者

  • ✅ 从零实现协议编解码,理解"网络上传的是什么"
  • ✅ 手写服务注册发现,理解"服务怎么找到对方"
  • ✅ 五种负载均衡策略实现,理解"请求怎么分配"
  • ✅ 四阶段优雅停机,理解"服务下线怎么不丢请求"

对于需要轻量 RPC 方案的团队

  • ✅ Spring Boot 两个注解开箱即用
  • ✅ 双注册中心(ZK/Nacos)自由选择
  • ✅ 泛化调用支持网关和测试平台场景
  • ✅ 可观测性 starter 一键引入

对于架构师

  • ✅ SPI 扩展点设计模式参考
  • ✅ 自定义二进制协议设计
  • ✅ 优雅停机与在途请求管理
  • ✅ 依赖版本统一管理(核心 jar 足够精简)

🚦 快速开始

前置要求

  • Java 17+
  • Maven 3.8+(或使用内置 ./mvnw
  • ZooKeeper 3.9+ 或 Nacos 3.x

三步启动

1. 编译项目

git clone https://github.com/javahongxi/jaws.git
cd jaws
./mvnw install -DskipTests

2. 引入依赖

<dependency>
    <groupId>org.hongxi</groupId>
    <artifactId>jaws-spring-boot-starter</artifactId>
    <version>${jaws.version}</version>
</dependency>
<dependency>
    <groupId>org.hongxi</groupId>
    <artifactId>jaws-registry-nacos</artifactId>
    <version>${jaws.version}</version>
</dependency>

3. 发布与引用服务

Provider:

@EnableJaws
@SpringBootApplication
public class ProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class, args);
    }
}

@JawsService
public class DemoServiceImpl implements DemoService {
    @Override
    public String hello(String name) {
        return "hello " + name;
    }
}

Consumer:

@Component
public class MyRunner implements CommandLineRunner {

    @JawsReference
    private DemoService demoService;

    @Override
    public void run(String... args) {
        System.out.println(demoService.hello("jaws"));
    }
}

一键运行示例

# injvm 协议(无需中间件)
./run-sample.sh injvm

# 完整 RPC 调用(需要 ZK)
./run-sample.sh run

# 性能基准测试
./run-sample.sh bench-jaws

# 自定义参数
THREADS=16 DURATION=20 ./run-sample.sh bench-jaws

🏆 技术栈

组件 技术 版本
语言 Java 17
网络 Netty 4.1.132
注册中心 ZooKeeper + Curator 3.9 / 5.9
注册中心 Nacos 3.2.2
序列化 fastjson2 / hessian-lite 2.0.62 / 4.0.5
框架集成 Spring Boot 3.5
可观测性 Micrometer + OpenTelemetry 1.14 / 1.48
工具 Guava 33.6

🔗 相关链接

🤝 贡献指南

欢迎提交 Issue 和 PR!如果你:

  • 发现 Bug 或有更好的实现方案
  • 想添加新的容错策略或负载均衡算法
  • 希望支持更多序列化方式或注册中心
  • 对性能优化有经验或建议

请随时参与贡献,让 Jaws 成为学习 RPC 原理的最佳参考项目!


© hongxi.org | 以生产级标准,打造最清晰的 Java RPC 框架实现


📝 结语

Jaws 不是要取代 Dubbo 或 gRPC,而是回答一个问题:一个生产级 RPC 框架,最少需要多少代码?

答案是:2万行 Java。

从协议设计到服务发现,从负载均衡到优雅停机,从泛化调用到链路追踪——每一个模块都经过精心设计,每一行代码都有实际意义。没有几十万行的历史包袱,没有看不懂的"魔法"。

如果你正在:

  • 🎯 想深入理解 RPC 框架的核心原理
  • 🚀 需要一个轻量级但功能完整的 RPC 方案
  • 🏗️ 学习 SPI 扩展点设计和自定义协议实现
  • 📚 寻找一个代码量可控、可以通读的框架源码

Star ⭐ Jaws,开启你的 RPC 原理探索之旅!

git clone https://github.com/javahongxi/jaws.git
cd jaws
./run-sample.sh injvm

让我们一起探索 RPC 框架的本质!🦈

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐