RPC调用异常必须在客户端封装层拦截并转换为继承BaseAppException的业务异常,禁止裸抛grpc.RpcError等框架异常;需通过exc.code()和exc.details()精准映射错误语义,统一错误码、脱敏消息,并记录完整traceback。

RPC调用异常必须转成业务异常类,不能裸抛框架异常
微服务间 RPC 调用(如 gRPC、Nameko、Feign)一旦出错,原始异常(grpc.RpcError、NamekoServiceError、FeignException)直接透传到上层,会导致日志混乱、前端无法识别、监控告警失焦。必须在 RPC 客户端封装层就做拦截和转换。
常见错误现象:日志里满屏 StatusCode.UNAVAILABLE 或 ConnectionRefusedError,但业务方根本不知道这是“用户服务不可用”还是“参数校验失败”;前端收到 {"code": 500, "msg": "Internal Server Error"},无法触发重试或降级逻辑。
- 所有 RPC 客户端包装类(如
UserClient)应在get_user()等方法内部捕获底层异常,立即转为继承自BaseAppException的业务异常,例如UserServiceUnavailableError - 不要在 service 层或 controller 层再 catch
grpc.RpcError—— 那已经晚了,调用链路已断裂,上下文丢失 - 转换时必须携带原始异常的
traceback(用logging.exception记录),但响应体中只暴露脱敏后的code和静态msg
gRPC 调用需主动 decode status_code + details,不能只看 exception type
gRPC 的 grpc.RpcError 是个壳,真正语义在 .code() 和 .details() 里。只用 except grpc.RpcError: 捕获,等于放弃所有错误分类能力。
使用场景:你调用下游 UserStub.GetUser,对方返回 StatusCode.INVALID_ARGUMENT,这对应的是 ErrorCode.PARAM_MISSING;若返回 StatusCode.NOT_FOUND,应映射为 ErrorCode.USER_NOT_FOUND。两者处理方式完全不同(前者要前端修正输入,后者可静默兜底)。
- 必须显式调用
exc.code()判断状态码,而非依赖异常类名 -
exc.details()可能含敏感信息(如数据库字段名),须清洗后才进日志;严禁原样返回给前端 - 建议封装一个
grpc_status_to_app_exception(exc)工具函数,集中管理映射规则,避免各客户端重复写 if/elif
Nameko RPC 超时异常要区分网络超时和服务无响应
Nameko 默认同步调用的超时是 AMQP 层级的(60 秒),它不等于网络连接超时,而是“消息发出去后,在队列里等结果等太久”。这个 timeout 一旦触发,抛出的是 TimeoutError,但业务上需要知道:是 RabbitMQ 挂了?还是 user_service 根本没注册?还是它卡死在 DB 查询?
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
容易踩的坑:TimeoutError 被统一当成“服务不可用”,结果真实原因是下游服务启动失败但注册中心未剔除,导致流量持续打空。
- 在
ClusterRpcProxy初始化时,设置timeout参数(如timeout=3.0),让超时更贴近业务预期 - 捕获
TimeoutError后,检查proxy.container.service_name是否在注册中心存活(可通过nameko.inspect或直接查 RabbitMQ Management API) - 若确认服务未注册,抛
ServiceNotRegisteredError(code=5001);若注册但无响应,抛ServiceUnresponsiveError(code=5002)
跨语言 RPC(如 Python 调 Java Feign)必须约定错误体 JSON 结构
当 Python 服务通过 HTTP 调用 Java 微服务(Feign),对方返回的错误响应体格式决定了你能解析出什么。如果 Java 端返回的是 {"timestamp":"...", "status":400, "error":"Bad Request", "message":"xxx"},Python 客户端就必须按此结构 decode,否则会拿到空 message 或解析失败。
性能影响:每次异常都走一次 json.loads() + 字段提取,比纯状态码判断慢约 0.2ms,但在异常路径上可忽略;真正的代价是没约定好结构导致 fallback 逻辑反复返工。
- 强制要求所有跨语言 RPC 接口文档中,明确写出 success 和 error 响应的 JSON Schema,至少包含
code、message、request_id三个字段 - Python 客户端统一用
requests.Response.raise_for_status()检查 HTTP 状态码,再用预定义的ErrorResponse类解析 body - Java 端若返回非标准格式(如直接 throw RuntimeException),Python 必须 fallback 到兜底异常
UnknownRemoteError,并记录原始 response.text
统一异常规范最难的不是代码,是让所有服务团队接受同一套 ErrorCode 常量类、同一份错误体定义、同一个 BaseAppException 继承体系——哪怕只有一方不遵守,整条链路的可观测性就断了。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










