第28章 RPC 框架异常:Dubbo / gRPC

第28章 RPC 框架异常:Dubbo / gRPC

28.1 Dubbo 常见异常

异常 触发场景
RpcException Dubbo RPC 调用过程中的通用异常,code 字段区分具体类型(超时、网络异常、序列化异常等)
TimeoutExceptionRpcException 的一种,isTimeout() 返回 true) 调用超过配置的 timeout 时间未返回
RemotingException 网络层通信异常(连接失败、连接断开等)
RpcException(No provider available) 服务发现失败,注册中心中找不到对应的 Provider 实例(服务未启动、或者服务分组/版本号配置不匹配导致过滤后无可用实例)

超时问题的典型排查路径:

java 复制代码
@Reference(timeout = 3000, retries = 2) // 单次调用超时3秒,失败后重试2次
private UserService userService;

关键提醒:retries 配置的重试机制只对幂等的读操作安全 ,对于写操作(如扣款、下单),超时后自动重试可能导致同一个操作被执行多次(第一次调用可能实际已经在 Provider 端成功执行,只是响应因为网络原因没有及时返回给 Consumer,被误判为超时后自动重试,就会造成重复执行)。因此写类接口通常应该显式设置 retries = 0,把重试的决策权交给业务层结合幂等设计来处理,而不是依赖框架的无脑重试。

"No provider available"排查清单:

  1. Provider 服务是否已经成功启动并注册到注册中心(查看注册中心控制台,如 Nacos/ZooKeeper 的服务列表)。
  2. Consumer 和 Provider 配置的 group/version 是否一致(Dubbo 支持按分组和版本号做服务隔离,配置不一致会导致 Consumer 找不到匹配的 Provider)。
  3. 网络策略(安全组/防火墙)是否放通了 Consumer 到 Provider 之间的端口。

28.2 gRPC 常见异常与状态码

gRPC 使用 StatusRuntimeException(非受检)统一表达调用失败,核心是 Status.Code 枚举,常见状态码及含义:

Status Code 含义 常见触发场景
DEADLINE_EXCEEDED 调用超过设置的截止时间 类似 HTTP 超时,网络延迟或服务端处理慢
UNAVAILABLE 服务不可达 目标地址连接失败、服务端过载主动拒绝连接
UNIMPLEMENTED 调用的方法在服务端没有实现 客户端/服务端 proto 定义版本不一致,客户端调用了服务端尚未实现的新接口
INVALID_ARGUMENT 请求参数不合法 服务端对入参做了校验并主动拒绝
INTERNAL 服务端内部错误 服务端代码抛出了未被妥善处理的异常
RESOURCE_EXHAUSTED 资源耗尽 触发了服务端的限流/配额限制
java 复制代码
try {
    response = stub.withDeadlineAfter(3, TimeUnit.SECONDS).someRpcCall(request);
} catch (StatusRuntimeException e) {
    Status.Code code = e.getStatus().getCode();
    if (code == Status.Code.DEADLINE_EXCEEDED) {
        log.warn("调用超时: {}", e.getMessage());
    } else if (code == Status.Code.UNAVAILABLE) {
        log.error("服务不可达: {}", e.getMessage());
    }
    // 根据不同状态码做差异化处理,而不是笼统 catch Exception 一把梭
}

gRPC 相比传统 HTTP+JSON 接口的一个显著差异 :所有错误信息都被规范化到统一的 Status/Code 体系里(而不是像 HTTP 那样用一堆不同的状态码 + 五花八门的 body 格式表达错误),配合 Status.withDescription()/withCause(),可以在保持跨语言兼容的同时携带更结构化的错误上下文,这是 gRPC 在微服务错误处理规范化方面相对传统 REST 接口的一个设计优势。

28.3 序列化/协议不兼容问题

Dubbo 和 gRPC 都对"跨版本兼容"有明确设计考量,但错误发生的层面不同:

  • Dubbo (默认使用 Hessian2 序列化):接口方法签名变化(比如增加了一个参数)通常需要 Provider 和 Consumer 同时更新,否则会在调用时抛出序列化/反序列化相关的 RpcException
  • gRPC/Protobuf:Protobuf 的设计从一开始就重点考虑了向前向后兼容(新增字段使用新的字段编号,旧客户端会忽略不认识的字段而不是报错),这是 gRPC 在大规模微服务场景下比很多其他 RPC 框架更受青睐的原因之一------协议演进对旧版本客户端通常是"优雅降级"而不是直接报错。

相关推荐
搬砖小匠1 小时前
SpringBoot 通过自定义注解 + AOP 实现数据字典自动翻译(通用方案)
java·后端
生信星球1 小时前
空间转录组常规分析(大白话教程)
后端
izhaorui2 小时前
【无标题】
后端
长栎2 小时前
AI 写的接口能用,但永远差点意思——这不是 prompt 的问题,是接口设计的品控规则它没装
后端
小p2 小时前
nextjs学习9: Next.js 渲染与缓存
前端·后端
宋哥转AI2 小时前
深入理解 AI Agent 03|RAG评估体系:量化检索增强效果,精准定位系统短板
人工智能·后端·agent
Leo2822 小时前
数据库Executor-Selector排查实践
后端
Zane19942 小时前
线程池入门:7 大核心参数与 4 种拒绝策略
java·后端
用户298698530142 小时前
无需安装 Excel,也能轻松将 CSV 转为 XLS 或 XLSX
后端·c#·excel