Small-RPC 是一款专为深入理解分布式 RPC 内核原理而设计的高性能、轻量级、高度可扩展的 Java RPC 框架。
3.0 延续「透视 RPC 内核」的路线,在 2.x 微内核基座之上补齐了消费端容错治理的最后一块拼图:
- 消费端治理闭环(3.0 新增):熔断(错误率/慢调用双条件三态状态机)、客户端限流(令牌桶+并发信号量)、降级 Fallback、在途上限快速失败——全部 opt-in,默认关闭;
- 微内核架构(2.x 基座):核心抽象与网络传输彻底解耦,核心层
rpc-core达成对网络协议与序列化的零依赖; - 自研工业级 SPI(2.x 基座):支持默认实现绑定、依赖注入(内置环检测)、运行时自适应动态分发;
- 事件驱动注册中心(2.x 基座):彻底退役 1.x 同步拉取模型,重构为
Registry订阅-推送模型,配合CachingServiceDirectory本地快照缓存实现无锁极速读取与注册中心故障无损降级; - 全套生态扩展(2.x 基座):开箱即用支持本地直连、Apache ZooKeeper(Curator 5.6 临时节点 + 真实推送)及 Redis(Jedis 4.4 Set 注册表 + 轮询差量比对推送);
- 异步与容错基座(2.x 基座):Failover 集群重试、超时预算、
CompletableFuture异步调用、权重负载均衡与实例预热、全链路 Trace 上下文、Spring Boot 优雅下线与自动注销。
⚠️ 学习与研究定位:本项目专注于核心机制的极致清晰呈现与工程规范设计,适合深入学习分布式网络编程、RPC 通信协议与服务治理机制。
Small-RPC 诞生于 2019 年 10 月(首个 Commit 于 2019-10-09)。
立项初心:为了深入学习与透视 RPC 底层内核原理。 在分布式微服务时代,Dubbo、gRPC、Spring Cloud 等工业级框架功能强大但体量庞大,底层核心细节(网络 I/O 线程模型、自定义二进制协议编解码、动态代理桩、服务注册与发现寻址等)被深藏在重重抽象与繁复框架代码之中。很多初学者往往停留在「会配配置、会加注解」的表层,难以透彻理解一次跨网络 RPC 调用在底层究竟经历了什么。
为了彻底打破黑盒,Small-RPC 选择从零手写:基于最经典的 Java + Netty + Hessian,纯手工实现网络通信、动态代理、服务暴露与远程调用的完整闭环,构建出了一套极简、高可读性、可单步 Debug 学习的 RPC 最小原型系统(Small-RPC 1.0)。
在 1.0 阶段,框架以「极简可用、清晰呈现通信骨架」为核心设计目标:
- 单体工程架构:所有核心逻辑集中于单体模块
small-rpc-core中,同时包含 Netty 通信、Hessian 编解码、简单的注册发现与 Spring 集成; - 通信与序列化:底层采用 Netty 4 NIO 传输通道,上层直接通过 Hessian 进行 Java 对象与二进制字节流的序列化/反序列化;
- Spring 手动工厂装配:通过显式配置
RpcSpringProviderFactory(管理端口、工作线程池与 Netty Server 启动)和RpcSpringInvokerFactory(管理服务发现与客户端 JDK 动态代理生成),通过@RpcService与@RpcReference注解实现服务暴露与引用; - 基础注册发现:早期基于
LocalServiceRegistry(单机静态直连/内存注册),并在后续 1.1/1.2 版本中探索了 Redis 与 ZooKeeper 的简单拉取注册。
随着学习研究的深入与分布式系统复杂度的提升,1.0 原始架构逐渐暴露出一系列结构性瓶颈,这也直接驱动了 2.0 版本的微内核重构:
- 核心逻辑与网络协议强耦合:1.0 核心代码与 Netty、Hessian 深度绑定,无法在不改动核心代码的前提下平滑替换通信引擎或序列化协议;
- 缺乏工业级扩展机制:1.0 缺乏统一的 SPI 扩展规范,切换实现依赖 Class 硬编码或繁琐反射,扩展性不足;
- 注册中心脆弱且缺乏容灾能力:1.0 采用简单的同步拉取或直连模式,一旦注册中心发生网络抖动或全量宕机,服务调用立即中断,缺乏客户端本地快照与故障降级自愈能力;
- 协议帧缺乏工业级健壮性:1.0 缺少自定义魔数(Magic Number)、协议版本以及细粒度状态码(Status Code)隔离,排查网络粘包、拆包或协议错误难度较大;
- Spring 集成侵入性高:1.0 需要用户手动在配置类中定义 Factory Bean,无法满足现代微服务开箱即用的自动装配(Spring Boot Starter)体验。
| 对比维度 | 1.0 原型阶段 (2019) | 2.x 微内核架构 |
|---|---|---|
| 定位与目标 | 极简学习原型,打通 RPC 通信闭环 | 高性能微内核架构,兼具学习透视与生产级治理规范 |
| 工程模块化 | 单体 small-rpc-core(依赖混杂) |
8 大职责单一子模块,rpc-core 微内核仅依赖 slf4j-api(零第三方协议与网络依赖) |
| 扩展机制 | 无 SPI,配置传 Class 类名或反射 | 自研工业级 SPI 框架:支持 @Spi 默认名、@SpiInject 依赖注入(含环依赖检测)、@Adaptive 动态代理分发 |
| 网络协议帧 | 裸 Hessian 字节流传输,无自定义帧头 | 自研二进制协议帧:Magic 0x5352、MsgType 消息分类、全局请求 ID、协议状态码 Passthrough |
| 注册发现机制 | 简单同步拉取 / 本地静态直连 | 事件驱动订阅-推送模型(subscribe / onChange),全量不可变实例快照推送 |
| 高可用与容灾 | 注册中心宕机即调用中断 | 注册中心故障无损降级:CachingServiceDirectory 本地只读快照冻结,支持 Redis 心跳租约与网络自愈 |
| 集群与容错 | 仅单点调用或简单随机 | Failover 集群容错重试、SPI 动态负载均衡(Random / RoundRobin)、全链路 Trace 追踪 |
| Spring 生态 | 手动定义 Factory @Bean 与参数 |
small-rpc-spring-boot-starter 全自动装配,强类型 YAML 配置提示与校验 |
2.0 重构收尾时,设计文档为容错治理留下了明确伏笔:FailoverClusterInvoker 的状态码模型按「熔断决策依据」设计,源码注释写着「熔断留给 3.0——状态码模型已为其预留决策依据」。3.0 的主题就是兑现这个承诺——把 2.x 已铺好的容错基座(Failover 重试、超时预算、权重预热、服务端 CallerRuns 背压)补全为完整的消费端治理闭环:
| 对比维度 | 2.x 微内核基座 | 3.0 治理闭环(当前版本) |
|---|---|---|
| 故障隔离 | Failover 换实例重试,但持续故障下重试放大流量 | 熔断器:滑动窗口统计错误率/慢调用率,超阈值 OPEN 快速失败,半开探测自愈(Hystrix/Sentinel 状态机 + Resilience4j 双条件) |
| 流量防护 | 服务端 CallerRuns 背压(入压侧) | 客户端限流:令牌桶限速 + 并发信号量(出压侧);在途上限 CLIENT_BUSY 快速失败,构成全链路背压闭环 |
| 失败善后 | 失败以 RpcException 直抛业务方 | 降级 Fallback:@RpcReference(fallback=...) 兜底(Dubbo Mock 思想) |
| 可观测性 | 调用计数与 RT(service#method) |
新增按原因归因的拒绝计数(circuit-open / rate-limit / concurrent-limit / client-busy)与降级计数 |
治理三件全部以 Filter 形式挂载于消费端链(Trace → Metrics → Fallback → RateLimit → CircuitBreaker → FailoverCluster),与新 SPI 扩展点(CircuitBreaker / RateLimiter)一同延续「每个机制可单独阅读、单独 Debug、单独关掉」的教学立场;拒绝状态(CIRCUIT_OPEN / RATE_LIMITED / CLIENT_BUSY)延续 2.x「本地状态码」原则——只在消费端结算,绝不上网络线。
为了完整体验从「1.0 极简原型」到「2.0 工业级微内核」再到「3.0 治理闭环」的架构演进历程,推荐读者通过历史 Git Tag 检出代码进行对照学习:
# 检出 1.0 初始学习原型(感受手写 RPC 的第一行代码)
git checkout 1.0.0
# 检出 1.x 演进版(包含 Redis / ZooKeeper 早期探索)
git checkout 1.2.0
# 检出 2.x 微内核完成形态(治理闭环之前:权重预热/异步/泛化/指标齐备)
git checkout 2.2.0
# 切回 3.0 治理闭环架构主分支
git checkout master
整个工程由父 POM 统一管理依赖与插件版本(基于 Spring Boot 2.7.18 依赖管理生态),划分为 8 个职责单一的子模块:
| 模块名 | 核心职责 | 核心依赖 |
|---|---|---|
rpc-core |
微内核抽象:SPI 机制、Invocation、Invoker、Filter 链路、LoadBalancer、Registry 抽象、CachingServiceDirectory 缓存降级、治理抽象(CircuitBreaker/RateLimiter SPI、滑动窗口、Fallback/限流/熔断 Filter) | 仅 slf4j-api(零第三方协议与传输依赖) |
rpc-transport-netty |
自研二进制协议帧(Magic 0x5352)编解码、Netty 4 通信引擎、SerializerRegistry、Hessian 序列化、RpcServer、客户端在途上限(PendingRequests) |
Netty 4.1, Hessian 4.0 |
rpc-registry-local |
本地直连注册中心:支持静态配置与单进程内存服务注册,用于本地开发与单机测试 | rpc-core |
rpc-registry-zookeeper |
ZooKeeper 注册中心:EPHEMERAL 临时实例节点、PathChildrenCache 实时事件推送、自动断网重连 | Apache Curator 5.6, ZooKeeper 3.7+ |
rpc-registry-redis |
Redis 注册中心:Set 集合注册表、定时差量比对推送、线程安全调度管理 | Jedis 4.4 |
small-rpc-spring |
Spring 框架集成:@RpcService 服务导出、@RpcReference 引用注入、ReferenceBeanPostProcessor 代理创建 |
Spring Context 5.3+ |
small-rpc-spring-boot-starter |
Spring Boot 自动装配:自动装配 Provider/Consumer、配置驱动、优雅停机注销生命周期 | Spring Boot Autoconfigure 2.7+ |
rpc-examples |
端到端示例工程:rpc-example-api(接口定义)、rpc-example-server(提供方)、rpc-example-client(消费端与 Web 控制器) |
Spring Boot Starter Web |
框架采用统一的 SPI 扩展规范,扩展点定义文件统一存放于 META-INF/small-rpc/<接口全限定名>,格式为 扩展名=实现类全限定名。
@Spi("defaultName"): 标注在 SPI 接口上声明默认扩展名。例如@Spi("random") public interface LoadBalancer。@SpiInject字段依赖注入: 在扩展实现类的字段上标注该注解,SPI 加载器会自动解析并注入对应依赖类型的默认单例;内置同线程环依赖检测,发现依赖成环时立即抛出IllegalStateException大声失败。@Adaptive("key")动态自适应代理: 根据方法入参Invocation的attachments[key]键值动态路由到具体的扩展实现,未传时平滑回退到默认扩展。- 多资源防重载 (name, FQCN): 在多 ClassLoader 或 Classpath 覆盖环境下,精确匹配扩展名与类名,幂等去重;若扩展名相同但类名不同则严格报错。
| 接口 | SPI 文件名 | 内置实现名 | 默认值 |
|---|---|---|---|
Registry |
io.github.upowerman.core.registry.Registry |
local, zookeeper, redis |
local |
LoadBalancer |
io.github.upowerman.core.loadbalance.LoadBalancer |
random, roundrobin, weightedrandom |
random |
Serializer |
io.github.upowerman.core.serialize.Serializer |
hessian(typeId=1)、json(typeId=2,Jackson) |
hessian |
Filter |
io.github.upowerman.core.filter.Filter |
trace、metrics(内置进消费端链) |
— |
CircuitBreaker |
io.github.upowerman.core.governance.CircuitBreaker |
default(滑动窗口 + 三态状态机) |
default |
RateLimiter |
io.github.upowerman.core.governance.RateLimiter |
tokenbucket(令牌桶,容量=qps) |
tokenbucket |
实例元数据(weight / startTime / warmup)随注册数据传播:ZooKeeper 写入临时节点 data、Redis 存于并行 meta Hash、Local 内存直存。weightedrandom 按有效权重 = weight × 预热因子 分流——启动后预热窗口内流量从 0 线性爬坡到满权重;startTime 未知(旧格式数据)或未配预热时恒为满权重。全部实例有效权重为 0 时回退均匀随机。旧版本注册数据按等权、已预热处理,前后兼容。
接口方法签名返回 CompletableFuture<T> 即异步调用,无需注解:代理直接透传内部调用链的 future(不在代理层阻塞),服务端自动解包实现方法返回的 future,响应帧携带最终业务值。同一接口可混合同步与异步方法。
不引入业务接口 jar,直接以服务名 + 方法名 + 参数类型名发起调用(网关/控制台/脚本场景)。消费端注入 GenericService bean:
@Autowired
private GenericService genericService;
Object result = genericService.$invoke(
"com.example.api.HelloService", // 服务名(接口全限定名)
"hello", // 方法名
new String[]{"java.lang.String"}, // 参数类型名(支持 int/long 等缩写)
new Object[]{"world"}); // 参数值
当前边界:参数类型在消费端 classpath 解析(
Class.forName);真·零依赖泛化(参数类型按描述符反射构造)仍属协议层演进方向,未包含在 3.0 治理主题内。
消费端链内置 MetricsFilter,按 service#method 累计 总数 / 成功 / 失败 / 累计 RT / 最大 RT,进程级单例 RpcMetrics.getInstance().snapshot() 读取单调累计快照(差值/时间窗即得 QPS)。3.0 治理扩展:快照新增 rejections()(按 circuit-open / rate-limit / concurrent-limit / client-busy 分原因归因)与 degraded()(Fallback 救回次数)。对接 Micrometer 等外部体系由使用方基于快照自行桥接。
- 彻底摒弃传统 1.x 每次 RPC 调用都向注册中心发起同步 Pull 的低效架构;
Registry接口采用subscribe(service, ServiceListener),注册中心状态发生变化时异步回调ServiceListener.onChange(snapshot)推送全量不可变实例快照;- Provider 实例通过 Spring 容器生命周期管理,在启动绑定端口后自动向注册中心注册,在停机阶段(Spring 上下文销毁或 SIGTERM 信号触发 ShutdownHook)自动调用
unregister,实现干净摘除。
- 业务调用永不阻塞:RPC 消费端发起请求时,仅从本地
ConcurrentHashMap缓存读取实例列表,不与任何远程注册中心产生网络同步等待; - 注册中心故障无损降级:当注册中心发生网络分区、抖动、甚至全量停机时,本地缓存被完全冻结保留,已有服务实例的调用完全不受影响(降级继续可用);
- 故障自愈:当注册中心节点恢复并重连成功后,实时推送管道自动恢复,缓存即时刷新为最新状态。
治理三件以 Filter 形式挂在消费端链上,链序 Trace → Metrics → Fallback → RateLimit → CircuitBreaker → FailoverCluster;在途背压落在传输层。全部默认关闭(opt-in),升级零行为变化。与 2.x 一致的「本地状态码」原则:熔断/限流/在途上限的拒绝只在消费端本地结算(CIRCUIT_OPEN / RATE_LIMITED / CLIENT_BUSY),绝不上网络线。
| 能力 | 机制 | 思想出处 |
|---|---|---|
| 熔断器 | 滑动窗口(秒级桶)统计错误率与慢调用率,任一超阈值即 OPEN 快速失败;open-ms 后进入 HALF_OPEN 放行探测,成功恢复 CLOSED |
Hystrix/Sentinel 状态机 + Resilience4j 双条件 |
| 客户端限流 | 每资源(service#method)令牌桶限速 + 并发信号量闸门,拒绝立即返回 |
Sentinel 单机流控 |
| 降级 Fallback | @RpcReference(fallback = X.class),下游失败或治理拒绝时调用兜底实现(容器 bean 优先,缺省反射实例化) |
Dubbo Mock(fail 语义) |
| 在途背压 | 消费端全局 in-flight 上限,超限 CLIENT_BUSY 快速失败,与服务端 CallerRuns 构成全链路背压 |
TCP 拥塞控制思想 |
public static class HelloFallback implements HelloService {
@Override
public HelloDTO hello(String name) {
return new HelloDTO(name, "degraded: provider unavailable");
}
}
@RpcReference(fallback = HelloFallback.class)
private HelloService protectedHelloService;
small-rpc:
consumer:
max-in-flight: 100
governance:
circuit-breaker:
enabled: true
min-calls: 5
open-ms: 5000
rate-limit:
enabled: true
qps: 50
指标归因:
RpcMetrics快照的rejections()按 circuit-open / rate-limit / concurrent-limit / client-busy 四种原因分列,degraded()为 Fallback 救回次数(见「调用指标」节)。
Small-RPC 提供了三种灵活的服务消费模型,覆盖从本地极速调试到生产集群治理的全部场景:
适合本地单机联调,客户端直接配置目标 Provider 地址(支持 IDE 属性智能补全):
small-rpc:
consumer:
enabled: true
provider:
enabled: false
registry:
type: local
local:
direct-address: localhost:7081
Note
为保证向下兼容,历史配置格式 small-rpc.registry.param.DIRECT_ADDRESS 依然完全支持并作为备选兜底。
在代码层面显式指定特定实例地址,完全跳过注册中心寻址与负载均衡:
@RestController
public class OrderController {
// 强制点对点调用指定 IP 与端口,适合调试与金丝雀灰度验证
@RpcReference(address = "127.0.0.1:7081")
private HelloService helloService;
}
面向微服务分布式部署,由注册中心进行实例管理,并结合 LoadBalancer 动态调度与容错:
small-rpc:
consumer:
enabled: true
provider:
enabled: false
registry:
type: zookeeper # 或 redis / local
zookeeper:
connect: localhost:2181
namespace: small-rpc
loadbalance: random # random 或 roundrobin
若使用 Redis 注册中心:
small-rpc:
registry:
type: redis
redis:
host: localhost
port: 6379
在 Spring Boot 应用的 pom.xml 中引入 starter(已默认内置单机 Local 直连注册中心,零外部依赖开箱即用):
<dependency>
<groupId>io.github.upowerman</groupId>
<artifactId>small-rpc-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<!-- 生产环境集群部署:按需引入分布式注册中心实现(二选一) -->
<!-- 1. ZooKeeper 注册中心 -->
<dependency>
<groupId>io.github.upowerman</groupId>
<artifactId>rpc-registry-zookeeper</artifactId>
<version>3.0.0</version>
</dependency>
<!-- 2. Redis 注册中心 -->
<dependency>
<groupId>io.github.upowerman</groupId>
<artifactId>rpc-registry-redis</artifactId>
<version>3.0.0</version>
</dependency>
在 application.yml 中配置服务提供端端口:
server:
port: 8090
small-rpc:
provider:
rpc2-port: 7081
consumer:
enabled: false # 纯提供方建议关闭消费端装配(防无用代理注入)
registry:
type: local # 本地直连;集群环境可换为 zookeeper 或 redis
定义业务接口与实现类,使用 @RpcService 暴露服务:
@Service
@RpcService
public class HelloServiceImpl implements HelloService {
@Override
public HelloDTO hello(String name) {
return new HelloDTO(name, "hello world");
}
}
在 Controller 或 Service 中通过 @RpcReference 注入远程代理对象:
@RestController
public class HelloController {
@RpcReference
private HelloService helloService;
@GetMapping("/rpc2/hello")
public HelloDTO hello(@RequestParam("name") String name) {
return helloService.hello(name);
}
}
工程提供了完整的样例(rpc-example-server 与 rpc-example-client),支持多实例负载均衡、故障节点自动摘除与注册中心停机降级验证。
-
启动本地 ZooKeeper(Docker):
docker run -d --name zookeeper -p 2181:2181 zookeeper:3.9 -
启动提供方实例 1(Web: 8090, RPC: 7081):
java -jar rpc-examples/rpc-example-server/target/rpc-example-server-3.0.0.jar \ --spring.profiles.active=zookeeper --server.port=8090 --small-rpc.provider.rpc2-port=7081 -
启动提供方实例 2(Web: 8092, RPC: 7082):
java -jar rpc-examples/rpc-example-server/target/rpc-example-server-3.0.0.jar \ --spring.profiles.active=zookeeper --server.port=8092 --small-rpc.provider.rpc2-port=7082 -
启动消费方(Web: 8091):
java -jar rpc-examples/rpc-example-client/target/rpc-example-client-3.0.0.jar \ --spring.profiles.active=zookeeper --server.port=8091 -
验证多实例负载均衡: 连续发起 20 次请求:
for i in {1..20}; do curl -s "http://127.0.0.1:8091/rpc2/hello?name=zk-$i"; done查看服务端日志,调用均匀分摊在 7081 与 7082 两台实例上。
-
验证宕机摘除与故障转移: 直接 kill 实例 1(
kill -9 <实例1的PID>)。 等待 ZooKeeper 会话超时(≤10s),再次连续调用 20 次: 所有调用保持 100% 成功,且全部平滑转移至存活的 7082 实例!
- 在上述 ZooKeeper 集群运行中,消费端已经建立订阅;
- 完全停止 ZooKeeper 服务:
docker stop zookeeper - 发起调用验证降级:
调用依然正常返回 200!此时消费端依靠本地内存冻结快照继续服务,丝毫不受注册中心宕机影响。
curl -s "http://127.0.0.1:8091/rpc2/hello?name=degraded" - 恢复 ZooKeeper 服务:
消费端与服务端自动完成重连,实时推送与目录同步自动恢复。
docker start zookeeper
- 启动本地 Redis(Docker):
docker run -d --name redis -p 6379:6379 redis:7 - 启动两台 Provider 与一台 Consumer,附带参数
--spring.profiles.active=redis; - 发起调用观察负载均衡;
- 优雅关闭实例 1(
kill <实例1PID>),Provider 自动触发注销(SREM),Redis 轮询任务比对后在 3~5 秒内自动更新客户端缓存,后续调用平滑切换至 7082 存活实例。 - 被动退出也能摘除:Redis 用「存活租约键」(
{prefix}:registry-heartbeat:{service}:{address})判定实例存活——Provider 周期性续期(heartbeat-ms),Consumer 轮询时剔除租约已过期(超过heartbeat-stale-ms未续期)的实例。因此kill -9/ OOM / 容器驱逐 这类无法执行优雅注销的被动退出,也会在租约过期后的下一个轮询周期被自动摘除,不会永久占坑。升级注意:存活判定依赖租约键,只有 2.x 及以上的 Provider 会写租约。若注册表混入旧版本 Provider 或运维手工
SADD的成员(无租约键),Consumer 会将其视为 stale 立即剔除(自 2.0 首发引入,仅影响混合部署/手工造数场景)。
示例客户端已内置治理演示配置(circuit-breaker.enabled=true, min-calls=5, open-ms=5000)与带 fallback 的受保护引用(/rpc2/protected-hello)。
- 启动提供方与消费方(同场景 A 步骤);
- 注入连续失败(服务端对
name=boom抛异常),连发 10 次:for i in {1..10}; do curl -s "http://127.0.0.1:8091/rpc2/protected-hello?name=boom"; done - 验证熔断快速失败:立即换成正常调用——熔断已打开,请求不再到达服务端(服务端日志无新
hello called),调用以 CIRCUIT_OPEN 快速失败并由 Fallback 兜底返回降级文案,RT 接近 0:curl -s "http://127.0.0.1:8091/rpc2/protected-hello?name=alice" # 返回 {"name":"alice","message":"degraded: provider unavailable"} - 验证半开自愈:等待
open-ms(5s)后熔断放行半开探测,服务已恢复 → 探测成功回到 CLOSED,恢复正常响应:sleep 6 && curl -s "http://127.0.0.1:8091/rpc2/protected-hello?name=alice" # 返回 {"name":"alice","message":"hello world"}
示例客户端已配置 rate-limit.qps=50 与 max-in-flight=100。
- 限流验证:突发压测触发令牌桶拒绝,部分请求立即失败(RATE_LIMITED,被 fallback 接住):
响应中混合
for i in {1..100}; do curl -s "http://127.0.0.1:8091/rpc2/protected-hello?name=rl-$i" & done; waithello world与degraded: provider unavailable(后者即限流拒绝被兜底的调用)。 - 在途上限验证:用
name=slow-...让服务端阻塞 4s 占住 in-flight 名额,并发超过max-in-flight的请求立即得到 CLIENT_BUSY(不会堆积在客户端内存):for i in {1..120}; do curl -s "http://127.0.0.1:8091/rpc2/protected-hello?name=slow-$i" & done; wait - 指标归因:消费端日志或运维端点读取
RpcMetrics.snapshot(),rejections()按原因(circuit-open / rate-limit / concurrent-limit / client-busy)分列,degraded()为兜底触发次数。
| 配置项 | 默认值 | 类型 | 详细说明 |
|---|---|---|---|
small-rpc.provider.enabled |
true |
Boolean | 是否启用服务提供端(纯消费端应用请务必设为 false,防端口占用) |
small-rpc.provider.rpc2-port |
7081 |
Integer | RpcServer Netty 监听端口 |
small-rpc.provider.address |
自动探测本机 IP | String | 提供端对外注册的地址(IP:Port),多网卡或容器端口映射环境下建议显式配置 |
small-rpc.provider.weight |
1 |
Integer | 实例静态权重:weightedrandom 负载均衡按权重比例分流量;1 = 等权 |
small-rpc.provider.warmup-ms |
0 |
Long | 预热窗口毫秒:>0 时启动后窗口内流量从 0 线性爬坡到满权重;0 = 关闭预热 |
small-rpc.provider.business-threads |
8 |
Integer | 服务端业务线程池线程数(请求反序列化与反射执行,不占 Netty IO 线程) |
small-rpc.provider.business-queue-capacity |
0 |
Integer | 业务线程池有界队列容量;0 = 无界。>0 时打满后由提交线程执行(CallerRuns 背压),不拒绝请求 |
small-rpc.provider.shutdown-drain-ms |
10000 |
Long | 优雅停机排空上限(毫秒):停机时先摘注册再等在途请求完成;0 = 不等待直接关闭 |
small-rpc.consumer.enabled |
true |
Boolean | 是否启用服务消费端 |
small-rpc.consumer.timeout-ms |
3000 |
Long | 全局默认单次调用超时(deadline 预算,含重试);@RpcReference.timeout 未设置时生效 |
small-rpc.consumer.retries |
1 |
Integer | 全局默认失败重试次数(0 = 不重试);@RpcReference.retries 未设置时生效 |
small-rpc.serializer |
hessian |
String | 消费端序列化 SPI 名:hessian / json;服务端按帧 codec 自动识别,两端无需对称配置 |
small-rpc.registry.type |
local |
String | 注册中心 SPI 扩展名:local / zookeeper / redis |
small-rpc.registry.local.direct-address |
"" |
String | Local 直连模式下的目标 Provider 地址(支持 IDE 属性智能补全) |
small-rpc.registry.zookeeper.connect |
localhost:2181 |
String | ZooKeeper 连接串(支持多地址逗号分隔) |
small-rpc.registry.zookeeper.namespace |
small-rpc |
String | ZooKeeper 命名空间根路径 |
small-rpc.registry.zookeeper.session-timeout-ms |
10000 |
Integer | ZooKeeper 会话超时时间(毫秒) |
small-rpc.registry.zookeeper.connection-timeout-ms |
3000 |
Integer | ZooKeeper 连接建立超时时间(毫秒) |
small-rpc.registry.redis.host |
localhost |
String | Redis 服务器主机名/IP 地址 |
small-rpc.registry.redis.port |
6379 |
Integer | Redis 服务器端口(1~65535) |
small-rpc.registry.redis.database |
0 |
Integer | Redis 数据库索引(默认为 0) |
small-rpc.registry.redis.timeout-ms |
2000 |
Integer | Redis 连接与读取超时时间(毫秒) |
small-rpc.registry.redis.password |
无 | String | Redis 认证密码(无密码留空) |
small-rpc.registry.redis.key-prefix |
small-rpc:registry: |
String | Redis 注册表集合 Key 前缀 |
small-rpc.registry.redis.poll-interval-ms |
3000 |
Long | Redis 注册表差量比对拉取周期(毫秒) |
small-rpc.registry.redis.heartbeat-ms |
1000 |
Long | Provider 心跳续期周期(毫秒);续写实例存活租约键 |
small-rpc.registry.redis.heartbeat-stale-ms |
5000 |
Long | 存活租约键 TTL(毫秒);Provider 被动退出超过此值未续期即被 Consumer 剔除,须 ≥ heartbeat-ms |
small-rpc.registry.param.* |
- | Map<String, String> | 底层注册中心原始通用参数字典(向下兼容,同名键优先级高于强类型默认值) |
small-rpc.loadbalance |
random |
String | 全局负载均衡算法 SPI 名:random(随机)、roundrobin(轮询)或 weightedrandom(加权随机,配合 provider.weight / provider.warmup-ms) |
small-rpc.consumer.max-in-flight |
0 |
Integer | 客户端在途请求上限;0 = 不限。超限以 CLIENT_BUSY 快速失败 |
small-rpc.consumer.governance.circuit-breaker.enabled |
false |
Boolean | 熔断开关(opt-in) |
small-rpc.consumer.governance.circuit-breaker.error-rate-threshold |
0.5 |
Double | 错误率跳闸阈值(窗口内样本 ≥ min-calls 才生效) |
small-rpc.consumer.governance.circuit-breaker.slow-call-rate-threshold |
0.5 |
Double | 慢调用率跳闸阈值 |
small-rpc.consumer.governance.circuit-breaker.slow-rt-ms |
0 |
Long | 慢调用阈值毫秒;0 = 按调用超时预算 80% 推导 |
small-rpc.consumer.governance.circuit-breaker.min-calls |
20 |
Integer | 窗口内最少样本数(防冷启动误跳) |
small-rpc.consumer.governance.circuit-breaker.window-ms |
10000 |
Long | 统计滑动窗口毫秒 |
small-rpc.consumer.governance.circuit-breaker.open-ms |
10000 |
Long | 熔断打开持续时间毫秒 |
small-rpc.consumer.governance.circuit-breaker.half-open-probes |
1 |
Integer | 半开探测名额 |
small-rpc.consumer.governance.rate-limit.enabled |
false |
Boolean | 限流开关(opt-in) |
small-rpc.consumer.governance.rate-limit.qps |
0 |
Integer | 每资源令牌桶速率(每秒);0 = 不限速 |
small-rpc.consumer.governance.rate-limit.max-concurrent |
0 |
Integer | 每资源并发上限(信号量);0 = 不限并发。注意:作用域为单个 @RpcReference 引用(每个代理独立计数),同一接口注入多个字段时上限按字段数放大 |
sequenceDiagram
autonumber
participant App as 业务调用方
participant Proxy as JDK 动态代理
participant Trace as TraceFilter
participant Metrics as MetricsFilter
participant FB as FallbackFilter
participant RL as RateLimitFilter
participant CB as CircuitBreakerFilter
participant Cluster as FailoverClusterInvoker
participant Dir as CachingServiceDirectory
participant LB as LoadBalancer (SPI)
participant Client as NettyClientTransport
participant Server as RpcServer
participant Invoker as ReflectiveInvoker
participant Svc as @RpcService Bean
App->>Proxy: helloService.hello("p3")
Proxy->>Trace: invoke(Invocation)
Trace->>Metrics: invoke(Invocation)
Metrics->>FB: invoke(Invocation)
FB->>RL: invoke(Invocation)
RL->>CB: invoke(Invocation)
CB->>CB: tryPass?OPEN 时立即以 CIRCUIT_OPEN 结算
CB->>Cluster: invoke(Invocation)
Cluster->>Dir: list(Invocation) [从本地只读快照取实例]
Dir-->>Cluster: List<ServiceInstance>
Cluster->>LB: select(invokers, invocation)
LB-->>Cluster: 选出目标 RemoteInvoker
Cluster->>Client: invoke(Invocation)
Client->>Server: 发送自研二进制协议 Frame (Magic 0x5352)
Server->>Invoker: 解码校验并派发业务线程
Invoker->>Svc: 反射调用业务方法
Svc-->>Invoker: 返回业务 DTO
Invoker-->>Server: Result (Status.SUCCESS)
Server-->>Client: 回写响应 Frame
Client-->>Cluster: CompletableFuture.completedFuture(Result)
Cluster-->>CB: Result(回填熔断统计,含重试终态)
CB-->>RL: Result(释放并发许可)
RL-->>FB: Result(非 SUCCESS 时调用 fallback 兜底)
FB-->>Metrics: Result(降级时记 degraded)
Metrics-->>Trace: Result(计数/计时)
Trace-->>Proxy: Result.value()
Proxy-->>App: 返回 HelloDTO
本项目基于 Apache License 2.0 开源协议。