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 通常与条件请求头配合使用,实现缓存一致性。下图展示了客户端与服务器之间的缓存验证流程:

条件请求缓存验证流程 客户端 API 网关 服务端 GET /users/123 If-None-Match: "abc123" 转发请求 比较 ETag 304 Not Modified 200 + 新 ETag 响应返回客户端 ETag 匹配 → 304(缓存有效);ETag 不匹配 → 200(更新缓存)

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 秒)作为兜底。这样既能保证数据一致性,又能在网关或客户端无法发送条件请求时自动过期。