4. 错误处理设计原则:面向API的错误码设计哲学
在缓存一致性治理中,API 错误处理不是简单的“返回 500”或“抛异常”。错误码是契约的核心组成部分,它决定了调用方能否快速定位问题、自动恢复或优雅降级。本节提出语义化、层次化、可扩展三大设计哲学,并给出具体的错误码结构规范。
4.1 三大设计哲学
| 哲学 | 核心要求 | 反例 | 正例 |
|---|---|---|---|
| 语义化 | 错误码本身携带业务含义,无需查文档即可大致理解 | ERR-001 |
CACHE_STALE_READ |
| 层次化 | 错误码按模块、场景、具体原因分层,便于聚合和告警 | 所有错误统一归为 500 |
4.3.2 → 缓存层·一致性·版本冲突 |
| 可扩展 | 新增错误码不破坏已有解析逻辑,不产生歧义 | 使用连续数字,中间插入新码导致范围冲突 | 使用预留段 + 命名空间前缀 |
设计原则:错误码是给机器读的,错误消息是给人读的。两者必须分离,且错误码的解析成本应低于 1μs。
4.2 语义化:从数字到可读标识符
传统 HTTP 状态码(如 400、500)粒度太粗,无法表达缓存一致性场景的细微差异。语义化要求错误码使用英文驼峰短语,让调用方 SDK 可以直接将其映射为枚举或异常类型。
// 推荐:语义化错误码
CACHE_MISS_AND_LOCK_FAILED
CACHE_VERSION_CONFLICT
CACHE_BACKEND_TIMEOUT
CASCADE_INVALIDATION_SKIPPED
// 不推荐:无意义数字
ERR_1001
ERR_1002
ERR_1003
缓存一致性场景示例:当写请求发现缓存版本号与数据库版本号不一致时,返回
CACHE_VERSION_CONFLICT,调用方收到后自动触发重读+重试,而不是直接抛 500。
4.3 层次化:错误码的树状结构
层次化设计将错误码拆分为域·模块·场景·原因四级,每级用点号或下划线分隔。这样既保留了机器可解析的紧凑性,又保留了人类可读的语义。
层次化优势:监控系统可以按“域”聚合告警,按“模块”定位瓶颈,按“场景”分析失败模式,按“原因”触发自动化恢复脚本。
4.4 可扩展:预留段与命名空间
错误码系统必须预见到未来新增缓存策略、新增一致性模型、新增后端存储的情况。可扩展设计遵循以下规则:
- 预留段:每个模块预留 20% 的错误码空间(如 0-79 为当前使用,80-99 为预留)。
- 命名空间前缀:使用
CACHE_、DB_、NET_等前缀,避免与其他系统冲突。 - 版本化:在错误码响应体中携带
error_schema_version字段,支持向后兼容。
// OpenAPI 3.1 错误码 schema 示例
components:
schemas:
CacheError:
type: object
properties:
error_code:
type: string
description: "语义化错误码,如 CACHE_VERSION_CONFLICT"
error_hierarchy:
type: string
description: "层次化路径,如 4.3.2"
error_schema_version:
type: string
description: "错误码 schema 版本,用于兼容性判断"
example: "1.2"
message:
type: string
description: "人类可读的错误描述"
details:
type: object
description: "可扩展的上下文信息,如冲突的版本号"
4.5 缓存一致性场景错误码清单
| 错误码 | 层次路径 | HTTP 状态码 | 说明 |
|---|---|---|---|
CACHE_MISS_AND_LOCK_FAILED |
4.1.1 | 503 | 缓存未命中且分布式锁获取失败,无法回源 |
CACHE_VERSION_CONFLICT |
4.3.2 | 409 | CAS 写入时版本号不匹配,需重试 |
CACHE_BACKEND_TIMEOUT |
4.4.1 | 504 | 缓存后端(Redis/Memcached)响应超时 |
CASCADE_INVALIDATION_SKIPPED |
4.2.3 | 200 | 级联失效被跳过(非错误,但需记录) |
DB_STALE_READ_DETECTED |
3.1.4 | 200 | 检测到数据库脏读,返回旧数据并触发异步修复 |
注意:错误码一旦发布进入生产,禁止删除或修改语义。只能通过新增或废弃(deprecate)来演进。废弃的错误码在响应中仍可返回,但需附带
deprecated: true 标记。
4.6 错误码在 OpenAPI 契约中的声明方式
在 OpenAPI 3.1 中,错误码应作为 response 的 oneOf 或 discriminator 来定义,确保每个错误码都有独立的 schema 和示例。
paths:
/cache/{key}:
get:
responses:
'200':
description: 成功返回缓存值
'409':
description: 版本冲突
content:
application/json:
schema:
$ref: '#/components/schemas/CacheError'
example:
error_code: "CACHE_VERSION_CONFLICT"
error_hierarchy: "4.3.2"
message: "缓存版本与数据库版本不一致,请重试"
details:
expected_version: 5
actual_version: 3
'503':
description: 缓存后端不可用
content:
application/json:
schema:
$ref: '#/components/schemas/CacheError'
example:
error_code: "CACHE_BACKEND_TIMEOUT"
error_hierarchy: "4.4.1"
message: "缓存后端连接超时,已触发降级策略"
最佳实践:在 API 网关层统一拦截错误码,将层次化路径写入日志的
error_hierarchy 字段,便于后续的根因分析和告警聚合。
通过语义化、层次化、可扩展三大设计哲学,错误码从“调试辅助信息”升级为“可编程的契约元素”。调用方可以根据错误码自动选择重试、降级、熔断或告警,而无需解析冗长的错误消息。这是缓存一致性治理中错误处理链路设计的基石。