臭名昭著aTrust隔离计划1.臭名昭著aTrust隔离计划2.VPN原理与aTrust隔离网络实践3.docker-easyconnect到底做了什么4.TUN(tunnel-隧道-虚拟网卡)模式
个人项目
personal(个人展示)GitHub Actions 自动部署个人展示项目
基于Quartz搭建的个人博客1.Quartz个人博客使用教程2.使用 rsync 增量部署 Quartz 博客3.使用 GitHub Actions 自动部署 Quartz 博客5.域名绑定6.CDN加速7.Google 搜索收录排查:为什么关键词搜得到别人,却搜不到我的网站?Obsidian、双向链接与知识图谱
架构设计
分布式分布式事务RPC原理
旅行
行程攻略青岛三日游行程攻略
前端
NginxNginx配置与反向代理入门
Nodenpm与npx的区别
数据库
mysql01.MySQL基础语法与约束02.MySQL数据库类型03.MySQL多表查询04.MySQL 索引05.MySQL 事务06.MySQL 隔离级别07.MySQL MVCC08.MySQL 锁09.EXPLAIN10.慢查询与 SQL 优化
mysql面试题01.索引问题02.事务与隔离级别03.锁机制04.存储引擎与性能优化05.用户权限、字符集与备份恢复
mysql实战01.EXPLAIN实战02.测试环境菜单误删排查与恢复03.MySQL IN 详解
网络
网络安全1.无线监听网卡苹果M系列虚拟机安装Kali Linux教程3.kali怎么使用RTL8812AU4.kali基础命令
网络基础进程之间怎样通过网络传递数据
网址常用网址
中间键
esElasticsearch 入门
mq
RabbitMQRabbitMQ高级RabbitMQ基础
1. MQ 基础2. MQ 核心角色与代码对应3. MQ Consumer Group 与消费模式4. SOFAMQ 账户变更代码实践5. MQ 可靠性问题6. 消息幂等
redis缓存击穿、穿透与雪崩Redis 分布式锁Redis 缓存问题Redis 数据结构
AI
使用说明
第三方插件&技能简介Archify使用与安装指南geekgeekRun使用OfferFlow使用指南Ponytail使用指南Skills Manager使用指南
CodexCodex CLI与IDE区别及使用指南Codex Hook单独配置与提交通知Codex MCP安装与使用指南Codex第三方插件安装与使用指南
Agent开发0-学习路线1.LLM、Token、上下文窗口与模型参数2.大模型 API、请求参数与响应结构3.Spring AI ChatClient4.Prompt、System Prompt、Prompt 模板6.结构化输出、JSON Schema7.SSE 流式响应8.会话 ID、聊天记录、Redis9.超时、重试、限流、降级10.*完成可运行聊天接口11.Function Calling、Tool Calling 原理12.用 Java 方法定义 Tool13.工具参数设计和校验14.实现订单查询工具15.实现物流查询工具16.实现库存查询工具17.多个工具自动选择18.工具异常、超时、重试
diy主机
主机零件学习1.CPU2.主板3.内存4.显卡5.硬盘6.散热7.风扇8.机箱热门型号价格表
捡垃圾diy-13600kf+5060diy-14600kf+5070
English高频词汇英文基础语法简单句子音标
GitGit常用命令Sublime Merge 使用指南
Java
工具库Caffeine本地缓存Reactor教程
工作实践如何使用@Async线程池使用与排查线上服务 CPU 使用率过高排查压测下RPC接口大量失败排查记录
基础1.HashMap2.ConcurrentHashMap3.HashSet4.final5.static6.volatile7.AtomicInteger8.String动态代理反射锁与线程同步线程注解
面试题1.HashMap 存储在方法内是线程安全的吗2.ArrayList参数传递与引用重新赋值3.== 和 equals 的区别、hashcode4.重载和重写的区别5.String、StringBuffer、StringBuilder区别及使用场景6.接口和抽象类7.ArrayList 与 LinkedList 的区别和扩容机制8.HashMap 和 Hashtable 的区别及底层实现9.Object 和 Objects 的区别10.ThreadLocal的原理和使用场景11.字节码是什么及作用12.Java类加载器有哪些13.双亲委派模型14.Java异常体系15.GC是什么16.GC如何判断对象可以被回收
面试总结0.HR面试模板1.面试一2.面试二3.面试三笔试
网络与 IO(黑马程序员-纯理论)01-网络编程基础02-IO模型与零拷贝03-BIO04-NIO基础与缓冲区05-NIO通道与选择器06-AIO
juc00-目录01-进程与线程02-线程同步与通信03-并发设计模式04-Java内存模型05-CAS与原子类06-ThreadLocal07-阻塞队列08-线程池使用与原理09-任务调度10-ForkJoin与享元模式11-AQS12-ReentrantLock13-读写锁14-并发协作工具15-ConcurrentHashMap16-CopyOnWrite与同步容器17-SkipListMap18-非阻塞队列
juc(工作常见)Java并发编程三个重点拆解
jvm1. JVM运行时数据区
Spring1.Spring IoC、DI与构造方法注入CGLIB、Byte Buddy 与 Spring AOPSpring 常用注解详解Spring Bean 生命周期
Spring MVC异常处理机制
linux常用命令
mac快捷键终端常用操作
python
基础01.Python是什么,为什么要安装Python02.Python项目中.venv是什么03.Python基础语法
基础实战sql替换
HTTPXHRMS员工详情接口调用(Python HTTPX)
windowswindows激活

一、💡 一句话理解

核心结论

RPC(Remote Procedure Call,远程过程调用)让一个程序通过网络调用另一个程序提供的方法,业务代码写起来接近本地方法调用。
记住这条线:找谁调用 → 选哪个实例 → 编码发送 → 远端执行 → 返回结果;等不到结果时,还要处理不确定性。

知识归属:架构设计 → 分布式 → 服务间通信 → RPC。SOFA RPC、Dubbo、gRPC 是具体实现;序列化、超时、服务发现和负载均衡是理解这些实现的基础。

本文按“服务约定 → 调用流程 → 网络失败 → 多实例寻址”的依赖顺序讲解。以订单服务访问库存服务为主线,围绕一次调用,逐步理解各个机制的职责与边界。官方资料查阅日期:2026-11-03。

二、🧭 理论:它是什么

2.1 从订单查询库存说起

一个应用中,订单模块和库存模块一起运行,订单代码可以直接调用库存对象的方法。系统拆成两个独立进程后,订单进程里的对象引用无法直接指向库存进程中的对象。

即使两个进程在同一台机器上,它们也有独立的内存空间。订单服务需要通过约定好的通信方式,把“调用哪个方法、传什么参数”告诉库存服务。

场景角色技术对象职责
订单服务调用方 / Consumer / Client发起库存查询或扣减请求
库存服务提供方 / Provider / Server执行库存业务并返回结果
调用方持有的库存代理对象客户端代理 / Stub把方法调用转换成网络请求
服务接口或 IDL服务契约约定方法、参数、返回值等
服务地址目录注册中心或名称解析系统帮调用方找到提供方

这是一个业务示例。真实框架中“代理”“注册中心”的具体实现不同,也可以直接配置地址,不要求每次调用都经过注册中心。

2.2 先约定双方能说什么

跨进程调用前,双方需要约定:服务名称、方法名称、参数与返回值结构,以及错误怎样表达。Java RPC 框架常使用共享接口和 DTO;gRPC 通常使用 .proto 描述服务和消息,再生成代码。

例如双方约定一个“按商品编号查询库存”的操作:输入商品编号 1001,输出商品编号和可用库存。只有方法名相同还不够,参数类型和字段含义也必须一致。

2.3 远程调用与本地调用有什么不同

对比项本地方法调用远程方法调用
执行位置当前进程内另一个进程,可能跨机器
数据传递参数值或本进程对象引用编码成可传输的数据
时间开销不包含网络往返包含编码、排队、网络和远端执行
失败情况主要是程序执行异常还包括断网、超时、远端不可用
失败后的结果普通同步调用中通常较容易定位可能无法判断对方是否执行成功

“像本地调用”的边界

RPC 简化的是调用方式。网络延迟、进程故障、数据复制和结果未知仍然存在,不能把远程调用当成一次便宜且必然成功的普通方法调用。

三、⚙️ 理论:它是怎么工作的

3.1 一次调用走过哪些步骤

订单业务代码
    ↓ 调用代理对象的查询方法
客户端代理
    ↓ 获取可用地址,选择一个库存实例
    ↓ 编码方法信息和参数,通过网络发送
库存服务框架
    ↓ 解码请求,找到对应业务方法
库存业务方法
    ↓ 查询库存,生成返回值
库存服务框架
    ↓ 编码结果,通过网络返回
客户端代理
    ↓ 解码结果,交还给订单业务代码

代理对象不是远程业务对象本身。它在本地接住调用,负责组织请求、通信和处理响应。不同框架可能使用动态代理、生成代码或显式客户端实现这一层。

请求中通常需要能识别目标服务和操作的信息、参数,以及把响应关联回调用的机制。具体信息放在消息体、请求路径还是协议头里,由所用协议决定,并非都发送同一种“Java 方法名字符串”。

3.2 序列化:对象怎样穿过网络

订单进程中的对象只存在于自己的内存里。传一个内存地址给库存进程没有意义,对方无法用这个地址访问订单进程的对象。

序列化把结构化数据编码成可保存、可传输的表示;反序列化把收到的数据恢复为接收方可使用的结构。

调用方参数 → 序列化 → 字节数据 → 网络 → 反序列化 → 提供方参数
提供方结果 → 序列化 → 字节数据 → 网络 → 反序列化 → 调用方结果
常见方案直观理解学习重点
JSON用文本表达数据结构易观察,关注数字、日期等类型约定
XML用标签表达数据结构XML-RPC 等协议会使用它
Protocol Buffers按预先定义的消息结构编码关注字段编号、类型和演进兼容性

序列化格式不是整个 RPC 协议。完整通信还需要约定消息如何识别、请求怎样对应响应、错误怎样表达等。序列化也不是加密,编码成字节不代表数据已经保密。

双方无需使用同一种编程语言,但必须遵守兼容的数据和通信约定。升级接口时,不要随意改变既有字段的含义或类型;具体兼容规则取决于序列化方案。

3.3 超时:调用方最多愿意等多久

库存服务可能正在排队、查询较慢,或者响应在网络中延迟。如果订单服务一直等待,线程、连接等资源可能长期被占用。

超时用于限制等待时长。截止时间(deadline)则表示最晚愿意等到哪个时间点。两者表达方式不同,但都用于约束时间预算。

超时不等于没执行

客户端超时只能说明它没有在规定时间内拿到可接受的结果。请求可能没到服务端,也可能已经执行成功,只是响应回来得太晚。

例如订单服务要求 500 毫秒内完成扣库存,库存服务在 400 毫秒时已提交修改,但响应在 600 毫秒才到。订单侧会超时,库存却已经扣了。

取消调用也不意味着已经提交的业务自动回滚。框架发出取消信号后,业务任务是否停止,还取决于实现和任务是否配合取消。

时间预算要覆盖整个调用链。假设一次请求总预算为 1 秒,前面已经消耗 700 毫秒,下游和可能的重试应共享剩余预算,而不是各自再等待完整的 1 秒。这些数字仅用于理解,实际配置应结合延迟分布、业务目标和压测。

3.4 重试:再调用一次之前先考虑什么

网络瞬时故障有时可以通过重试恢复。但超时后的结果未知,直接再发一次扣库存请求,可能把库存扣两次。

幂等表示同一个业务操作重复执行,业务效果与执行一次一致。典型做法是给同一操作一个稳定的业务唯一标识,服务端识别重复请求,并返回已处理结果或当前处理状态。

订单 ORD-001 发起一次扣库存操作 → 服务端处理并记录结果
响应丢失 → 调用方使用同一操作标识重试
服务端识别为同一操作 → 返回已有结果,不重复扣减

真正落地时,去重记录和库存变更需要有可靠的一致性约束。只在内存里放一个标记,或先查“有没有处理”再无保护地修改,都不足以解决并发重复请求。

重试时至少判断:

  • 错误是否可能恢复;参数非法、库存不足等业务拒绝通常不该原样重试。
  • 操作是否幂等,尤其是扣款、扣库存、创建订单。
  • 剩余时间和最大尝试次数是否允许。
  • 是否需要退避和随机抖动,避免大量请求同时重试。

还要避免每一层都重试。如果三层调用各自最多尝试三次,最坏情况下最下游可能收到 27 次调用,放大故障压力。配置里的“重试次数”是否包含首次调用,要看具体框架定义。

3.5 服务注册与发现:地址从哪里来

起初只有一个库存实例,可以直接配置它的地址。实例扩容、重启或迁移后,固定地址越来越难维护。

常见注册发现模型如下:

库存实例 A、B → 向注册中心登记服务与地址
订单服务     → 查询或订阅库存服务地址列表
注册中心     → 返回地址,后续按机制通知变化
订单服务     → 选择地址,直接请求库存实例

注册解决“我提供什么服务、在哪里”;发现解决“我要的服务有哪些地址”。地址变化可能通过推送、轮询等方式传播,具体机制由系统决定。

注册中心通常不转发每次业务调用。客户端往往缓存地址列表,因此注册中心短时不可用时,已有调用不一定立即失败;但新实例发现、地址更新和新应用启动可能受影响,行为取决于框架及配置。

注册中心里的地址也不保证当前一定可用:故障检测和变更传播存在时间差,还需要连接状态、健康检查和失败处理配合。RPC 可以采用直连或 DNS 等寻址方式,独立注册中心不是 RPC 的必需组成。

3.6 负载均衡:找到三个地址后选哪个

服务发现得到库存实例 A、B、C 的地址,负载均衡负责从符合路由条件且可用的候选实例里选一个。

策略怎样选择注意什么
轮询依次选 A、B、C请求数均匀不代表资源消耗均匀
随机随机选择候选实例少量请求可能不均匀
加权选择权重高的实例分到更多请求权重需要符合实例能力与运行状态
最少活跃请求等根据客户端可见的负载指标选择本地观测不一定代表实例全局负载
一致性哈希根据业务键等映射到实例适用于需要一定访问亲和性的场景

客户端负载均衡是在调用方选实例;也可以通过代理或网关转发,让中间层参与选择。不同 RPC 框架支持的策略不同。

记忆方式:服务发现给候选名单,路由缩小范围,负载均衡做本次选择。

3.7 把这些概念放回同一次下单

  1. 订单服务通过服务发现得到库存服务的实例地址。
  2. 根据路由和负载均衡策略,选择一个可用实例。
  3. 代理把服务操作、参数和必要元信息编码成请求。
  4. 库存服务解码请求,执行查询或扣减,返回编码后的结果。
  5. 调用方在预算内收到响应,解析结果;超时或失败则按业务规则处理。
  6. 如果要重试扣减,使用稳定操作标识,并依赖服务端幂等机制防止重复扣减。

RPC 负责完成通信,不自动保证多个服务的数据一起成功。 订单已创建但扣库存失败时,还需要业务协调机制,见 分布式事务。

3.8 RPC、HTTP、REST、MQ 和 SOFA RPC 怎样区分

名称所在层面与当前场景的关系
RPC远程调用模型订单服务调用库存服务的操作
HTTP应用层通信协议可以承载 RPC 请求,例如 XML-RPC 和 gRPC 使用的 HTTP 协议族
REST架构风格通常围绕资源及统一接口组织交互
MQ消息通信机制可以发布事件,让消费方异步处理
SOFA RPC / Dubbo / gRPC具体框架提供远程调用及相关能力,功能范围各不相同

RPC 与 HTTP 不处在同一层面,不能简单说“用了 RPC 就不用 HTTP”。RPC 也支持异步和流式调用,不能简单说“RPC 都同步,MQ 都异步”。选择时要看业务是否需要立即拿结果、怎样处理失败和如何解耦。

SOFA RPC 是 Java RPC 框架,具备协议、注册发现和负载均衡等相关能力。它使用的具体配置和 API 适合后续单独记录;本篇先建立通用原理。

四、🎤 面试时怎么说

4.1 回答路线

按“是什么 → 一次调用怎么走 → 多实例怎么找和选 → 失败怎样处理”回答,最后点出网络调用与本地调用的边界。

4.2 30 秒回答

RPC 是远程过程调用,让应用通过网络调用另一个应用的方法。调用方通常通过代理组织请求,对参数进行序列化,服务端收到后反序列化并执行业务,再返回结果。部署多个实例时,可以通过服务发现获取地址,通过负载均衡选择实例。它虽然写起来像本地调用,但会遇到网络超时和结果未知,所以要设置合理的时间预算,并结合幂等设计决定是否重试。

4.3 常见追问

超时后能不能直接重试?

不能一概而论。对方可能已经执行成功;扣款、扣库存等操作需要可靠的幂等机制,还要限制重试次数、时间预算,并判断错误是否可恢复。

注册中心挂了,RPC 就完全不能调用了吗?

不一定。已有客户端可能使用缓存地址继续调用,但地址变化、新实例发现和新应用启动可能受影响,要结合框架和配置判断。

RPC 能保证订单和库存一起提交吗?

不能。通信成功和跨资源事务一致性是不同问题,需要结合 分布式事务 中的协调机制处理。

五、📌 快速回顾

概念先记住这个问题
远程调用怎样让另一个进程执行约定的方法?
序列化参数和结果怎样变成能传输的数据?
超时我最多愿意等待多久?
重试与幂等再试一次,会不会把业务做两遍?
服务发现服务有哪些候选地址?
负载均衡本次请求选择哪个候选实例?

学习时先沿着一次调用串起这些概念,再去看 SOFA RPC 或 Dubbo 的配置。内容积累多了,再把序列化、注册发现或负载均衡拆成独立笔记。

六、📚 官方资料

本篇将多份官方资料按初学所需的前置关系组织,gRPC 的特定行为不代表所有 RPC 框架都相同。业务示例用于帮助理解。