核心概念
RPC 比 HTTP 快,主要体现在传输效率、序列化开销、连接管理和路由寻址四个方面。这些优化使得 RPC 在微服务内部调用场景下性能提升 30%-50% 以上。
需要注意:这里的 HTTP 主要指传统的 HTTP/1.1 + JSON 方案。HTTP/2 + Protobuf 的性能已经接近专有 RPC 协议。
性能优势分析
1. 传输协议开销
HTTP 协议的开销:
POST /api/user/getUser HTTP/1.1
Host: user-service.com
Content-Type: application/json
Accept: application/json
User-Agent: Apache-HttpClient/4.5.13
Connection: keep-alive
Content-Length: 25
{"userId": 123456789}
Dubbo 协议的开销:
+---------------+---------------+---------------+
| Magic(2字节) | Flag(1字节) | Status(1字节) |
+---------------+---------------+---------------+
| Request ID(8字节) |
+---------------+---------------+---------------+
| Data Length(4字节) | Data |
+---------------+---------------+---------------+
对比分析:
- HTTP 头部:通常 200-500 字节
- Dubbo 头部:16 字节
- 头部节省:95% 以上
// HTTP 请求示例
POST /api/user HTTP/1.1 // 约 23 字节
Host: example.com // 约 20 字节
Content-Type: application/json // 约 30 字节
Content-Length: 50 // 约 18 字节
// 其他头部... 总计: 200+ 字节
// Dubbo 协议示例
0xdabb // 魔数: 2 字节
0x42 // 标志: 1 字节
0x00 // 状态: 1 字节
0x0000000000000001 // 请求ID: 8 字节
0x00000032 // 数据长度: 4 字节
// 总计: 16 字节
2. 序列化效率
JSON 序列化(HTTP 常用):
// 传输数据
{
"userId": 123456789,
"userName": "张三",
"age": 25,
"email": "zhangsan@example.com"
}
// 大小:约 85 字节
Hessian2 序列化(Dubbo 默认):
// 同样的数据,Hessian2 序列化后约 40 字节
// 压缩率:约 50%
性能对比表:
| 序列化方式 | 数据大小 | 序列化耗时 | 反序列化耗时 |
|---|---|---|---|
| JSON | 100% | 100% | 100% |
| Hessian2 | 50% | 30% | 40% |
| Protobuf | 40% | 20% | 25% |
| Kryo | 45% | 15% | 20% |
// Dubbo 序列化配置
@Service(serialization = "hessian2") // 默认,性能与兼容性平衡
@Service(serialization = "kryo") // 更快,但需要额外依赖
@Service(serialization = "protobuf") // Google 方案,跨语言友好
public class UserServiceImpl implements UserService {
// ...
}
3. 连接管理
HTTP 短连接模式:
// 每次请求的流程
1. TCP 三次握手(1.5 RTT)
2. 发送 HTTP 请求
3. 接收 HTTP 响应
4. TCP 四次挥手(2 RTT)
// 总开销:3.5 RTT + 数据传输时间
HTTP Keep-Alive 模式:
// 连接复用,但有限制
Connection: keep-alive
Keep-Alive: timeout=5, max=100
// 问题:
// 1. 超时后仍需重新建连
// 2. 单个连接串行处理请求
// 3. 服务端需要维护大量连接
Dubbo 长连接 + 多路复用:
// Dubbo 连接特点
@Reference(
connections = 1, // 单个长连接
lazy = false // 启动时建立连接
)
private UserService userService;
// 优势:
// 1. 长连接一直保持,无握手开销
// 2. 一个连接支持并发多个请求(多路复用)
// 3. 异步非阻塞通信
性能对比:
// HTTP 短连接:每次调用 3.5 RTT
1000 次调用 = 3500 RTT
// HTTP Keep-Alive:首次 1.5 RTT,后续 0 RTT(直到超时)
1000 次调用 = 1.5 RTT(理想情况)
// Dubbo 长连接:启动时 1.5 RTT,后续 0 RTT
1000 次调用 = 0 RTT(运行时)
4. 负载均衡与服务发现
HTTP 方案:
客户端 -> Nginx -> 服务A
-> 服务B
-> 服务C
// 多一跳,增加延迟:
// 延迟 = 客户端->Nginx + Nginx->服务端
Dubbo 方案:
客户端 -> 服务A
-> 服务B (客户端直连,框架内负载均衡)
-> 服务C
// 客户端直连,减少一跳:
// 延迟 = 客户端->服务端
// Dubbo 客户端负载均衡
@Reference(
loadbalance = "random", // 随机
retries = 2 // 失败重试
)
private UserService userService;
// 客户端直接选择服务提供者,无需中间代理
性能优化实践
1. Dubbo 性能调优
// 服务提供者优化
@Service(
protocol = "dubbo",
serialization = "kryo", // 使用更快的序列化
threads = 200, // 业务线程池大小
accepts = 1000, // 最大连接数
payload = 8 * 1024 * 1024 // 最大传输数据 8MB
)
public class UserServiceImpl implements UserService {
// ...
}
// 服务消费者优化
@Reference(
timeout = 3000, // 调用超时 3 秒
retries = 1, // 失败重试 1 次
loadbalance = "leastactive", // 最少活跃数负载均衡
connections = 10, // 每个提供者 10 个连接
actives = 20 // 每个服务最大并发 20
)
private UserService userService;
2. 协议选择
// Dubbo 协议:最常用,性能最优
<dubbo:protocol name="dubbo" port="20880" />
// Triple 协议:基于 HTTP/2,兼容 gRPC
<dubbo:protocol name="triple" port="50051" />
// Rest 协议:对外提供 HTTP 接口
<dubbo:protocol name="rest" port="8080" />
// 多协议支持
@Service(protocol = {"dubbo", "rest"})
public class UserServiceImpl implements UserService {
// 同时支持 RPC 和 HTTP 调用
}
3. 异步调用
// 同步调用
User user = userService.getUser(123L); // 阻塞等待
// 异步调用(性能更好)
CompletableFuture<User> future =
RpcContext.getContext().asyncCall(() -> userService.getUser(123L));
future.thenAccept(user -> {
// 回调处理
System.out.println(user);
});
实际性能数据
基于典型的微服务调用场景(局域网环境):
| 维度 | HTTP + JSON | Dubbo + Hessian2 | 提升幅度 |
|---|---|---|---|
| 单次调用延迟 | 10ms | 3ms | 70% |
| QPS(单连接) | 1000 | 3000 | 200% |
| 网络带宽占用 | 100KB/s | 40KB/s | 60% |
| CPU 占用 | 20% | 12% | 40% |
// 基准测试代码
@BenchmarkMode(Mode.Throughput)
@Warmup(iterations = 3)
@Measurement(iterations = 5)
public class RpcBenchmark {
@Benchmark
public void httpCall() {
// HTTP + JSON 调用
restTemplate.getForObject(url, User.class);
}
@Benchmark
public void dubboCall() {
// Dubbo + Hessian2 调用
userService.getUser(123L);
}
}
// 结果:
// httpCall thrpt 5 1000.123 ops/s
// dubboCall thrpt 5 3000.456 ops/s
答题总结
RPC 比 HTTP 快的四大核心原因:
- 协议开销小:
- HTTP 头部 200+ 字节,Dubbo 仅 16 字节
- 节省 95% 的头部开销
- 序列化高效:
- JSON 可读性好但体积大、速度慢
- Hessian2/Protobuf 体积小 50%,速度快 3-5 倍
- 连接管理优:
- 长连接无握手开销
- 多路复用提升并发能力
- 异步非阻塞通信
- 路由更直接:
- 客户端直连服务端,减少一跳
- 框架内置负载均衡,无需额外代理
性能提升:在典型场景下,RPC 比 HTTP 快 2-3 倍,适合微服务内部高频调用。
面试技巧:强调性能优势的同时,也要提到 HTTP 的优势(通用性、可调试性),说明两者是互补而非替代关系。HTTP/2 + Protobuf 已经接近 RPC 性能。