4. 错误类型定义:系统错误、业务错误、第三方服务错误、自定义错误码体系设计
在 BFF(Backend For Frontend)层构建健壮的错误处理机制,首要任务是建立一套清晰、可扩展的错误类型体系。错误类型的合理划分决定了中间件能否精准捕获、熔断器能否正确触发、客户端能否有效处理。本章将系统定义三大核心错误类别,并设计一套面向生产环境的自定义错误码体系。
4.1 错误分类的三维模型
BFF 层处于前端与后端微服务之间,其错误来源可归纳为三个维度:系统内部、业务逻辑、外部依赖。三者之间的边界决定了错误处理策略的差异。
核心原则:系统错误应快速失败(Fail Fast),业务错误应优雅降级(Graceful Degradation),第三方服务错误应触发熔断(Circuit Breaking)。
4.2 系统错误(System Error)
系统错误指 BFF 层自身运行时发生的不可预期故障,通常由基础设施、运行时环境或代码缺陷引发。这类错误的特点是:不可恢复、不可重试、需要运维介入。
| 错误类别 | 典型场景 | HTTP 状态码 | 处理策略 |
|---|---|---|---|
| 内存/资源溢出 | 堆内存泄漏、栈溢出、文件句柄耗尽 | 500 | 立即终止请求,记录完整堆栈,触发告警 |
| 配置异常 | 配置文件缺失、格式错误、环境变量未设置 | 500 | 启动时 Fail-Fast,拒绝提供服务 |
| 数据库连接失败 | 连接池耗尽、数据库宕机、网络分区 | 503 | 重试策略 + 降级缓存 |
| 序列化/反序列化错误 | JSON 解析失败、类型不匹配、字段缺失 | 500 | 记录原始数据,返回通用错误响应 |
注意:系统错误不应暴露内部实现细节给客户端。错误响应中应隐藏堆栈信息,仅返回通用错误码和简要描述。堆栈信息应写入服务端日志,供运维排查使用。
4.3 业务错误(Business Error)
业务错误由前端请求的语义或上下文不满足业务规则而产生。这类错误是可预期的、可恢复的,客户端通常需要根据错误码进行差异化处理。
业务错误的核心特征:
- 可预知性:业务规则在开发阶段已明确,错误场景可枚举。
- 可恢复性:客户端修正请求参数或状态后,可重新发起请求。
- 语义明确:错误信息应包含具体字段、约束条件和修复建议。
// Node.js 业务错误定义示例
class BusinessError extends Error {
constructor(code, message, details = null) {
super(message);
this.name = 'BusinessError';
this.code = code; // 业务错误码,如 'USER_NOT_FOUND'
this.httpStatus = 400; // 业务错误统一使用 4xx
this.details = details; // 附加字段,如 { field: 'email', reason: '格式不正确' }
this.timestamp = Date.now();
}
}
// 使用场景
throw new BusinessError(
'ORDER_STATUS_CONFLICT',
'订单当前状态不允许取消操作',
{ orderId: 'ORD20241001', currentStatus: 'SHIPPED', expectedStatus: 'PENDING' }
);
4.4 第三方服务错误(Third-Party Service Error)
BFF 层作为聚合网关,频繁调用下游微服务或外部 API。第三方服务错误是 BFF 层最复杂的错误类型,因为它涉及网络通信、服务可用性、超时、限流等多种因素。
| 错误子类型 | 触发条件 | BFF 处理策略 | 熔断阈值建议 |
|---|---|---|---|
| 超时错误 | 下游响应超过 BFF 设置的超时时间 | 快速失败 + 返回降级数据 | 连续 5 次超时触发熔断 |
| 限流错误 | 下游返回 429 Too Many Requests | 指数退避重试(最多 3 次) | 连续 10 次限流触发熔断 |
| 服务不可用 | 下游返回 503 或连接拒绝 | 立即熔断,返回缓存或默认值 | 连续 3 次不可用触发熔断 |
| 数据异常 | 下游返回的数据格式或内容不符合契约 | 记录异常,返回降级响应 | 连续 5 次数据异常触发熔断 |
// Go 第三方服务错误定义示例
type ThirdPartyError struct {
ServiceName string // 下游服务名称
Endpoint string // 请求端点
StatusCode int // 下游返回的 HTTP 状态码
Err error // 原始错误
Retryable bool // 是否可重试
CircuitOpen bool // 是否已触发熔断
}
func (e *ThirdPartyError) Error() string {
return fmt.Sprintf("[%s] %s failed with status %d: %v",
e.ServiceName, e.Endpoint, e.StatusCode, e.Err)
}
// 熔断判断逻辑
func (e *ThirdPartyError) ShouldCircuitBreak() bool {
return e.StatusCode == 503 || e.StatusCode == 0 || !e.Retryable
}
4.5 自定义错误码体系设计
一套优秀的错误码体系应满足:唯一性、可扩展性、可读性、可自动化处理。推荐采用分层编码结构,将错误码拆分为多个语义段。
4.5.1 错误码分配表
| 错误码范围 | 所属大类 | 模块标识 | 说明 |
|---|---|---|---|
| 1-00-0001 ~ 1-00-0999 | 系统错误 | 00(通用) | BFF 框架级错误,如配置缺失、内存溢出 |
| 1-01-1000 ~ 1-01-1999 | 系统错误 | 01(基础设施) | 数据库、缓存、消息队列连接异常 |
| 2-01-0001 ~ 2-01-0999 | 业务错误 | 01(用户模块) | 用户不存在、密码错误、账号锁定 |
| 2-02-1000 ~ 2-02-1999 | 业务错误 | 02(订单模块) | 订单状态冲突、库存不足、金额校验失败 |
| 3-05-0001 ~ 3-05-0999 | 第三方错误 | 05(支付服务) | 支付超时、支付拒绝、渠道异常 |
| 3-08-1000 ~ 3-08-1999 | 第三方错误 | 08(推荐服务) | 推荐引擎超时、模型加载失败 |
4.5.2 错误码注册与维护规范
- 集中注册:所有错误码必须在统一的
error-codes.yaml或ErrorCode.java中注册,禁止在代码中硬编码数字。 - 版本化管理:错误码变更需经过 Code Review,并更新 API 文档。
- 自动化校验:CI 阶段自动检查错误码是否重复、格式是否正确、是否已废弃。
- 文档同步:错误码表应自动生成前端可读的文档,便于客户端开发人员查阅。
# error-codes.yaml 示例
version: "1.0.0"
errors:
# 系统错误
- code: "1-00-0001"
message: "内部服务器错误"
httpStatus: 500
description: "BFF 层发生未预期的系统异常"
action: "请联系运维人员查看日志"
- code: "1-01-1001"
message: "数据库连接池耗尽"
httpStatus: 503
description: "数据库连接池已达到最大连接数"
action: "请稍后重试"
# 业务错误
- code: "2-01-0001"
message: "用户不存在"
httpStatus: 404
description: "请求的用户 ID 在系统中不存在"
action: "请检查用户 ID 是否正确"
- code: "2-02-1001"
message: "订单状态不允许取消"
httpStatus: 409
description: "当前订单状态已超出可取消的时间窗口"
action: "请联系客服处理"
# 第三方服务错误
- code: "3-05-0001"
message: "支付服务超时"
httpStatus: 504
description: "调用支付服务超过 3 秒未响应"
action: "请稍后重试或选择其他支付方式"
circuitBreak: true
retryable: true
最佳实践:错误码的
message 字段面向开发者,应保持技术准确;action 字段面向最终用户,应提供可操作的修复建议。BFF 层在返回错误响应时,可根据客户端类型(Web / App / 第三方)选择性地暴露 action 字段。
4.6 错误类型与中间件穿透策略
错误类型定义直接影响中间件的处理流程。不同类型的错误在中间件链中的穿透行为应有所区别:
| 错误类型 | 中间件穿透行为 | 是否触发熔断 | 是否记录日志 | 是否返回给客户端 |
|---|---|---|---|---|
| 系统错误 | 立即中断,不继续穿透 | 否 | 是(全量堆栈) | 仅返回通用错误码 |
| 业务错误 | 穿透至业务中间件处理 | 否 | 是(业务上下文) | 返回完整错误信息 |
| 第三方服务错误 | 穿透至熔断中间件 | 是 | 是(服务名+耗时) | 返回降级响应 |
通过上述分类与编码体系,BFF 层能够实现:
- 精确的异常定位:从错误码即可判断错误来源、所属模块、具体原因。
- 差异化的处理策略:系统错误快速失败,业务错误优雅提示,第三方错误触发熔断。
- 可观测性增强:错误码与监控指标、告警规则直接关联,提升运维效率。
- 前后端协作效率:前端根据错误码做差异化 UI 展示,无需解析模糊的文本信息。