RPC 与异步编程
RPC(Remote Procedure Call)和异步编程是后端开发中两个独立又相互交织的领域。RPC 解决的是”如何跨网络调用远程服务”,异步编程解决的是”如何在等待时不让线程空闲”。当两者结合——异步 RPC——就构成了现代高性能微服务通信的基础。
一、RPC:跨网络的函数调用
RPC 的核心理念是让远程调用看起来像本地调用。你调 userService.getUserById(100),而不关心这个服务在哪个 IP 的哪个端口上。
1.1 调用流程
1 | 客户端 服务端 |
1.2 gRPC 与 Protocol Buffers
gRPC 是 Google 开源的高性能 RPC 框架,基于 HTTP/2 协议,使用 Protocol Buffers 作为接口定义语言和序列化协议。
1 | // user.proto |
gRPC 支持四种调用模式——一元调用(传统请求-响应)、服务端流、客户端流、双向流。详细内容参见 gRPC 与 Protocol Buffers。
二、异步编程:不让线程闲着
2.1 为什么需要异步
在传统的同步阻塞模型中,一个请求对应一个线程:
1 | Request → [Thread-1] → 查询数据库(阻塞 100ms)→ 等待 Redis(阻塞 20ms) → 返回 |
整个等待期间线程完全空闲,但占用了内存(每个线程约 1MB 栈)。1000 个并发请求 = 1000 个线程 = 1GB 内存。如果请求延迟增加,线程池打满,系统崩溃。
异步编程的核心思想:在等待 I/O 时不阻塞线程,去处理其他请求。
2.2 CompletableFuture
Java 8 引入的 CompletableFuture 是异步编程的基础工具:
1 | // 异步查询用户 + 异步查询订单 → 合并结果 |
CompletableFuture 的核心方法:
| 方法 | 作用 |
|---|---|
thenApply() |
转换结果(同步) |
thenCompose() |
串联异步操作 |
thenCombine() |
合并两个异步结果 |
allOf() |
等待所有完成 |
exceptionally() |
异常处理 |
详细内容参见 CompletableFuture 详解。
2.3 响应式编程:从 Future 到 Stream
CompletableFuture 处理单次异步结果,但遇到流式数据(如 WebSocket 实时消息、Kafka 消息流)就不够了。响应式编程将”数据流 + 操作符”的组合抽象提升到一等公民。
Reactive Streams 规范定义了四个核心接口:
1 | public interface Publisher<T> { |
Project Reactor(Spring WebFlux 的基础)提供了 Flux(0-N 个元素)和 Mono(0-1 个元素):
1 | Flux<User> users = userRepository.findAll() |
详细内容参见 响应式编程 — RxJava / Reactor。
三、异步 RPC:两者结合的实践
gRPC 原生支持异步调用和流式调用——这正是 RPC 和异步编程的结合点:
1 | // 异步 RPC 调用 |
四、小结
RPC 解决的是分布式通信,异步编程解决的是并发效率。两者结合——异步 RPC——是现代微服务通信的主流范式。gRPC + CompletableFuture/Reactor 构成了 Spring 生态中从 RPC 到异步编程的完整技术栈。