微服务统一异常处理:全局异常码 + 标准化响应体
你有没有遇到过这种情况:
微服务 A 返回 {"code": 0, "msg": "成功"}
微服务 B 返回 {"status": "ok", "message": "操作成功"}
微服务 C 返回 {"success": true, "errorMsg": ""}
你的前端需要为每个服务写不同的解析逻辑。
这就是微服务异常处理的痛点:各服务返回格式不统一。
统一响应体结构
java
@Data
public class ApiResponse<T> {
private int code; // 业务状态码
private String message; // 状态描述
private T data; // 响应数据
private String traceId; // 链路追踪 ID
public static <T> ApiResponse<T> success(T data) {
ApiResponse<T> response = new ApiResponse<>();
response.setCode(200);
response.setMessage("success");
response.setData(data);
response.setTraceId(Tracer.getTraceId());
return response;
}
public static <T> ApiResponse<T> error(int code, String message) {
ApiResponse<T> response = new ApiResponse<>();
response.setCode(code);
response.setMessage(message);
response.setTraceId(Tracer.getTraceId());
return response;
}
}全局异常码设计
java
public interface ErrorCode {
// 系统级异常(1xxx)
int SYSTEM_ERROR = 1001; // 系统异常
int PARAM_ERROR = 1002; // 参数错误
int UNAUTHORIZED = 1003; // 未授权
int FORBIDDEN = 1004; // 禁止访问
// 业务级异常(2xxx)
int USER_NOT_FOUND = 2001; // 用户不存在
int ORDER_NOT_FOUND = 2002; // 订单不存在
int STOCK_NOT_ENOUGH = 2003; // 库存不足
}全局异常处理器
java
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public ApiResponse<Void> handleBusinessException(BusinessException e) {
return ApiResponse.error(e.getCode(), e.getMessage());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ApiResponse<Void> handleValidationException(MethodArgumentNotValidException e) {
String message = e.getBindingResult().getFieldError().getDefaultMessage();
return ApiResponse.error(ErrorCode.PARAM_ERROR, message);
}
@ExceptionHandler(Exception.class)
public ApiResponse<Void> handleException(Exception e) {
// 记录异常日志
log.error("系统异常", e);
return ApiResponse.error(ErrorCode.SYSTEM_ERROR, "系统异常,请稍后重试");
}
}跨服务异常传递
当一个微服务调用另一个微服务时,异常信息如何传递?
java
@FeignClient(name = "user-service")
public interface UserClient {
@GetMapping("/user/{id}")
ApiResponse<User> getUser(@PathVariable Long id);
}Feign 拦截器
java
@Component
public class FeignErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
try {
if (response.status() >= 400) {
String body = IOUtils.toString(response.body().asInputStream());
ApiResponse<?> apiResponse = objectMapper.readValue(body, ApiResponse.class);
return new BusinessException(apiResponse.getCode(), apiResponse.getMessage());
}
} catch (Exception e) {
log.error("解析响应失败", e);
}
return new BusinessException(ErrorCode.SYSTEM_ERROR, "服务调用失败");
}
}面试追问方向
- 为什么需要统一的异常码?(答:前端统一解析、微服务间通信、日志分析)
- 如何设计异常码体系?(答:分层设计、系统级 + 业务级、预留扩展空间)
- 全局异常处理器能捕获异步异常吗?(答:不能,需要用 CompletableFuture.exceptionally())
- 异常信息中放什么?(答:错误码、用户友好信息、TraceId)
小结
微服务异常处理需要统一标准:
- 统一响应体:所有服务返回相同格式
- 全局异常处理器:集中处理各类异常
- 跨服务异常传递:Feign 拦截器解码异常
- 链路追踪:异常信息带上 TraceId
统一异常处理是微服务间通信的基础。
