Jaws:从零构建一个“五脏俱全“的 Java RPC 框架
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 │ └──────────┴──────────┴──────────┴──────────────┴──────────┴──────────┘支持fastjson2和hessian2两种序列化方式,可通过配置一键切换。
🏗️ 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|grepSampleProvider# 找到 PIDkill-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")privateGenericServicedemoService;// 调用Objectresult=demoService.$invoke("hello",newString[]{"java.lang.String"},newObject[]{"jaws"});Provider 端自动完成 Map↔POJO 参数转换,调用方完全无感知。
📊 7. 可观测性
引入jaws-observability-spring-boot-starter即可获得完整的可观测性能力:
| 能力 | 实现 | 说明 |
|---|---|---|
| 链路追踪 | Micrometer Tracing + OpenTelemetry | W3C TraceContext 格式自动传播,全链路 traceId 一致 |
| 指标采集 | Micrometer | RPC 调用次数、成功率、耗时分布、活跃请求数,sidetag 区分 consumer/provider |
| 日志关联 | OTel 日志桥接 | traceId/spanId 自动注入 MDC,无需手动处理 |
无需额外代码,Filter SPI 自动生效。
💡 8. 方法级别配置
全局配置不够精细?Jaws 支持在消费端为单个方法设置独立的超时和重试策略:
@JawsReference(methods={@Method(name="hello",timeout=5000,retries=3),// 这个方法单独配置@Method(name="getUser",timeout=500)// 这个方法快速失败})privateDemoServicedemoService;🔌 9. injvm 协议
JVM 内部直调,零网络开销,适合本地开发和单元测试:
./run-sample.sh injvm无需任何中间件,一行命令即可运行。
🧰 10. RpcContext
消费端可获取实际调用的服务端地址,提供端可获取调用方 IP:
// Consumer 端URLserverUrl=RpcContext.getContext().getServerUrl();// Provider 端StringcallerIp=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 # 性能基准测试核心设计理念:
- SPI 全可插拔:Protocol、Cluster、LoadBalance、Filter、Serialization 等所有核心组件均通过
@SPI+@Activation机制可插拔,替换任何一层不影响其他模块 - 分层解耦:core 保持轻量,extensions 承载附加功能,starter 负责自动装配
- 配置模型统一: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. 编译项目
gitclone https://github.com/javahongxi/jaws.gitcdjaws ./mvnwinstall-DskipTests2. 引入依赖
<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@SpringBootApplicationpublicclassProviderApplication{publicstaticvoidmain(String[]args){SpringApplication.run(ProviderApplication.class,args);}}@JawsServicepublicclassDemoServiceImplimplementsDemoService{@OverridepublicStringhello(Stringname){return"hello "+name;}}Consumer:
@ComponentpublicclassMyRunnerimplementsCommandLineRunner{@JawsReferenceprivateDemoServicedemoService;@Overridepublicvoidrun(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=16DURATION=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 |
🔗 相关链接
- 📦项目地址: https://github.com/javahongxi/jaws
- 🌐作者主页: hongxi.org
- 📚Java 全栈研究: whatsmars
- ☁️微服务最佳实践: spring-cloud-samples
- 📖Spring Boot: https://spring.io/projects/spring-boot
- 📡Netty: https://netty.io
- 🌐Nacos: https://nacos.io
🤝 贡献指南
欢迎提交 Issue 和 PR!如果你:
- 发现 Bug 或有更好的实现方案
- 想添加新的容错策略或负载均衡算法
- 希望支持更多序列化方式或注册中心
- 对性能优化有经验或建议
请随时参与贡献,让 Jaws 成为学习 RPC 原理的最佳参考项目!
© hongxi.org| 以生产级标准,打造最清晰的 Java RPC 框架实现
📝 结语
Jaws 不是要取代 Dubbo 或 gRPC,而是回答一个问题:一个生产级 RPC 框架,最少需要多少代码?
答案是:2万行 Java。
从协议设计到服务发现,从负载均衡到优雅停机,从泛化调用到链路追踪——每一个模块都经过精心设计,每一行代码都有实际意义。没有几十万行的历史包袱,没有看不懂的"魔法"。
如果你正在:
- 🎯 想深入理解 RPC 框架的核心原理
- 🚀 需要一个轻量级但功能完整的 RPC 方案
- 🏗️ 学习 SPI 扩展点设计和自定义协议实现
- 📚 寻找一个代码量可控、可以通读的框架源码
Star ⭐ Jaws,开启你的 RPC 原理探索之旅!
gitclone https://github.com/javahongxi/jaws.gitcdjaws ./run-sample.sh injvm让我们一起探索 RPC 框架的本质!🦈