5月27日 23:23

如何实现 RPC 的异步调用?

答案

RPC 异步调用的核心思路是:客户端发起调用后不阻塞等待响应,而是通过 Future、回调或响应式流等机制在结果就绪时获取。常见有三种模式:

  1. Future/Promise — 调用立即返回 Future 对象,调用方自行决定何时获取结果。Dubbo 原生支持 async=true,通过 RpcContext.getFuture() 拿到返回值。
  2. Callback 回调 — 传入回调接口,服务端响应到达时自动触发。适合事件驱动场景,但多层嵌套易产生回调地狱。
  3. 响应式流(Reactive) — 基于 Reactor/RxJava,以 Mono/Observable 表示异步结果,支持背压和链式组合,适合流式处理。

此外,Java 8 的 CompletableFuture 兼具 Future 和回调的优点,支持 thenCombine 组合多个异步结果,是目前最常用的异步编排工具。gRPC 则通过 StreamObserver 实现异步,并原生支持双向流通信。

异步调用的优势

  • 不阻塞调用线程,单线程可同时处理多个请求,提高并发能力和吞吐量
  • 并行调用多个服务,用 CompletableFuture.allOf 或响应式 zip 组合结果,显著降低总延迟
  • 避免资源浪费,线程不必在 I/O 等待上空转

关键挑战

  • 上下文传递:异步线程切换时 TraceId、用户信息等上下文易丢失,需用 TransmittableThreadLocal 显式传递
  • 超时控制:必须设超时并取消,否则请求可能无限挂起。future.get(timeout) + future.cancel(true) 是基本模式
  • 线程池管理:异步任务不能无限制创建线程,需配置有界线程池并监控队列积压

追问

Q: 同步调用和异步调用怎么选?

调用方需要立即拿到结果才能继续(如下单扣库存),用同步;调用方不依赖结果或可以后续处理(如发通知、写日志),用异步。实际中大部分 RPC 调用用同步,并行调用多个服务时用异步组合。

Q: CompletableFuture 和 Reactor 的区别是什么?

CompletableFuture 处理单值异步结果,API 简单,适合多数业务场景;Reactor 基于响应式流规范,支持多值序列和背压,适合流式数据处理,但学习成本更高。如果只是组合几个 RPC 调用,CompletableFuture 足够。

Q: 异步调用失败怎么重试?

不要简单循环重试,应采用指数退避策略(如初始 100ms,每次翻倍,最多 3 次),配合熔断器在连续失败时快速失败。CompletableFuture 可用 exceptionallyhandle 捕获异常后触发重试逻辑。

标签:RPC