微服务异常处理需建立可传递、可识别、可协同的错误语义体系:统一JSON响应结构(code/message/requestId/timestamp/details),分层映射HTTP状态码,RPC/HTTP调用出入口标准化转换,网关注入X-Error-Code,补齐定时任务、消息消费等非Controller场景兜底机制。

大型单体应用向微服务转型时,异常处理不能简单沿用原有“统一 try-catch + 返回码枚举”的做法。核心矛盾在于:单体里异常是本地调用链上的信号,而微服务中异常会跨进程、跨网络、跨语言——一次失败可能源于参数错误、服务不可达、熔断触发或下游返回了含糊的 500。规范化重点不是“让所有服务抛同一个异常类”,而是建立**可传递、可识别、可协同**的错误语义体系。
定义跨服务一致的错误响应结构
所有服务(无论 Java 或非 Java)对外必须返回统一 JSON 格式,且字段含义严格对齐:
- code:纯业务错误码(如 USER_NOT_FOUND、PAY_TIMEOUT),字符串类型,不与 HTTP 状态码耦合,也不用数字魔数
- message:面向调用方的简明提示(如“用户不存在”“支付超时,请重试”),支持 i18n 占位符
- requestId:全链路唯一 ID(如 SkyWalking traceId),必须透传到下游并记录在每条日志中
- timestamp:错误发生毫秒时间戳,用于排查时序问题
- details(可选):结构化上下文,如校验失败的字段名、订单 ID、库存余量等,便于前端展示或运维定位
分层映射 HTTP 状态码,不靠硬编码
HTTP 状态码只表达协议层结果,不承载业务逻辑。同一业务错误在不同上下文中应映射不同状态码:
- GET /users/{id} 中用户不存在 → 404 NOT_FOUND(资源确实不存在)
- POST /orders 中收货人 ID 校验失败 → 400 BAD_REQUEST(客户端传参非法)
- 调用用户服务时连接被拒绝或超时 → 503 SERVICE_UNAVAILABLE 或 504 GATEWAY_TIMEOUT(依赖故障,可重试)
- 内部逻辑崩溃(如 NPE、DB 连接池耗尽)→ 500 INTERNAL_SERVER_ERROR(需人工介入)
关键:禁止在 @ResponseStatus 中给 BusinessException 注解固定状态码;应在 @ExceptionHandler 方法内根据 ErrorCode 的语义动态设置 response.setStatus()。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
构建可透传的异常载体与远程传播机制
本地异常不能直接序列化传给下游。需在 RPC 或 HTTP 调用出入口做标准化转换:
- 出参侧(服务提供方):全局异常处理器捕获 BusinessException 后,返回 200 OK + 上述标准 JSON 响应体(不是 4xx/5xx)
- 入参侧(服务消费方):Feign Client 或 RestTemplate 拦截器自动解析响应体中的 code 字段;若 code 非成功值(如非 "0000"),则包装为本地 BusinessException 并抛出,保持调用链语义一致
- 网关层补全:API 网关对所有非 2xx 响应,自动注入 X-Error-Code header,值取自响应体 code 字段,方便前端或第三方系统快速路由错误处理逻辑
补齐非 Controller 场景的异常兜底能力
@RestControllerAdvice 只覆盖 Web 层,但转型中常见异步任务、定时调度、消息监听等场景,这些地方异常不会被拦截:
- 定时任务(@Scheduled):用 AOP 切面包裹执行逻辑,在 catch 块中统一记录 requestId + error log,并触发告警
- 消息消费(@RabbitListener / @KafkaListener):配置 defaultErrorHandler,将反序列化失败、业务异常等转为死信或重试队列,避免消息丢失
- 线程池任务(ExecutorService):设置 ThreadFactory,为每个线程指定 UncaughtExceptionHandler,记录堆栈并上报监控平台
- Filter / Interceptor 中异常:单独定义 FilterExceptionHandler,避免因前置逻辑失败导致整个请求无响应
所有兜底逻辑都不构造 HTTP 响应,只做日志、告警、状态标记,确保职责单一。

















