3. OpenAPI 规范与缓存:核心字段与缓存策略
在微服务与 API 网关架构中,缓存是降低延迟、减少后端压力的关键手段。OpenAPI 3.0 规范通过标准字段定义了服务端与客户端之间的缓存契约。本章聚焦于 Cache-Control、ETag、Last-Modified 三个核心字段,并区分响应缓存与请求缓存的适用场景。
3.1 缓存相关字段详解
OpenAPI 规范在响应对象(Response Object)和头对象(Header Object)中定义了缓存字段。下表对比了三个字段的语义、使用位置与典型场景:
| 字段 | 所属对象 | 核心语义 | 典型场景 |
|---|---|---|---|
| Cache-Control | Response Header | 指令集:max-age、no-cache、no-store、private、public | 控制缓存生命周期与可见性 |
| ETag | Response Header | 实体标签:资源内容的哈希或版本标识 | 条件请求(If-None-Match)验证缓存有效性 |
| Last-Modified | Response Header | 资源最后修改时间戳(RFC 1123 格式) | 条件请求(If-Modified-Since)时间戳验证 |
设计建议:ETag 比 Last-Modified 更精确(可检测内容变化,即使时间未变),但 Last-Modified 更易实现。推荐在 OpenAPI 规范中同时声明两者,让客户端选择验证策略。
3.2 响应缓存 vs 请求缓存
缓存策略需要根据数据流向分为两类:
- 响应缓存(Response Cache):缓存服务端返回的完整响应。适用于 GET 请求的幂等资源,如用户列表、配置信息。由网关或客户端根据 Cache-Control 指令决定存储时长。
- 请求缓存(Request Cache):缓存客户端发出的请求结果,避免重复发送相同请求到后端。适用于查询参数固定的搜索、分页接口。通常由 API 网关或反向代理实现。
下表从多个维度对比两种缓存:
| 维度 | 响应缓存 | 请求缓存 |
|---|---|---|
| 缓存对象 | HTTP 响应(状态码 + 头 + 体) | 请求 URL + 参数 + 方法 |
| 触发条件 | Cache-Control: public / max-age | 幂等请求(GET / HEAD / OPTIONS) |
| 失效方式 | TTL 过期 / ETag 验证 | 手动清除 / 参数变化 |
| 典型位置 | 浏览器、CDN、网关 | API 网关、反向代理 |
| OpenAPI 声明 | response.headers 中定义 | parameters 中定义缓存键 |
3.3 在 OpenAPI 规范中声明缓存字段
以下示例展示如何在 OpenAPI 3.0 的响应头中声明 Cache-Control、ETag 和 Last-Modified:
openapi: 3.0.0
info:
title: 用户服务 API
version: 1.0.0
paths:
/users/{userId}:
get:
summary: 获取用户详情
parameters:
- name: userId
in: path
required: true
schema:
type: string
responses:
'200':
description: 成功返回用户信息
headers:
Cache-Control:
description: 缓存控制指令
schema:
type: string
example: public, max-age=3600
ETag:
description: 资源实体标签
schema:
type: string
example: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Last-Modified:
description: 资源最后修改时间
schema:
type: string
format: date-time
example: Thu, 01 Dec 2025 16:00:00 GMT
content:
application/json:
schema:
$ref: '#/components/schemas/User'
常见陷阱:不要在 OpenAPI 规范中硬编码 ETag 或 Last-Modified 的值。这些字段应由服务端运行时动态生成。规范中只定义 schema 类型和示例值,实际值在响应时填充。
3.4 缓存一致性流程:条件请求
ETag 和 Last-Modified 通常与条件请求头配合使用,实现缓存一致性。下图展示了客户端与服务器之间的缓存验证流程:
3.5 缓存键设计与服务边界
在分布式系统中,缓存键的设计直接影响缓存命中率与一致性。OpenAPI 规范中的 parameters 定义决定了缓存键的组成:
- 基础缓存键:请求方法 + 路径 + 查询参数(按规范顺序排序)
- 扩展缓存键:加入
Accept、Authorization(需谨慎)等头字段 - 服务边界隔离:不同微服务的缓存键应包含服务标识,避免键冲突
实践要点:
- 在 OpenAPI 规范中为每个 GET 端点显式声明 Cache-Control 指令
- 对频繁变更的资源使用 ETag + max-age=0(始终验证)
- 对静态配置使用 max-age=86400(一天)并配合 Last-Modified
- 避免在 POST/PUT/DELETE 响应中设置长 TTL 缓存
3.6 缓存失效策略
缓存一致性不仅依赖写入时的 TTL,更需要主动失效机制。下表列出常见失效策略及其适用场景:
| 策略 | 机制 | 适用场景 | 一致性保证 |
|---|---|---|---|
| TTL 过期 | Cache-Control: max-age | 读多写少、可容忍短暂不一致 | 弱 |
| 条件验证 | ETag / Last-Modified + 条件请求 | 高一致性要求、资源频繁更新 | 强 |
| 主动清除 | 网关收到写请求后清除相关缓存键 | 写操作后立即失效 | 最终一致 |
| 版本化键 | 缓存键包含资源版本号 | 静态资源、版本化 API | 强 |
推荐组合:对于关键业务数据,使用 ETag 条件验证 + 短 TTL(如 60 秒)作为兜底。这样既能保证数据一致性,又能在网关或客户端无法发送条件请求时自动过期。