---

title: API分页与资源耗尽治理(游标/限制/速率)最佳实践

keywords:

  • 分页
  • 游标
  • 限制
  • 速率
  • Backpressure
  • Max-Page-Size
  • Next-Cursor
  • Offset限制
  • 防耗尽
  • 验收

description: 构建可验证的API分页治理策略,采用游标分页与最大页大小限制,协同速率限制与Backpressure,附参数校验与响应头示例。

categories:

  • 文章资讯
  • 技术教程

---

一、参数与校验

function validCursor(c: string): boolean { return /^[A-Za-z0-9_\-\.]{1,128}$/.test(c) }
function validPageSize(n: number, max: number): boolean { return Number.isInteger(n) && n > 0 && n <= max }

二、游标与查询构造

type Page = { items: any[]; nextCursor?: string }

function decodeCursor(c: string | undefined): { id?: string } {
  if (!c) return {}
  if (!validCursor(c)) return {}
  return { id: c }
}

function buildQuery(base: string, cursor: { id?: string }, limit: number): string {
  const parts: string[] = []
  if (cursor.id) parts.push(`id > '${cursor.id}'`)
  const where = parts.length ? 'WHERE ' + parts.join(' AND ') : ''
  return `${base} ${where} ORDER BY id ASC LIMIT ${limit}`
}

三、响应头与Backpressure

type Res = { setHeader: (k: string, v: string) => void; end: (b?: string) => void }

function sendPage(res: Res, page: Page, size: number, max: number) {
  res.setHeader('X-Page-Size', String(size))
  res.setHeader('X-Max-Page-Size', String(max))
  if (page.nextCursor) res.setHeader('X-Next-Cursor', page.nextCursor)
  if (!page.nextCursor) res.setHeader('X-End', 'true')
  res.end(JSON.stringify(page))
}

四、速率协同

class RateGate { windowMs: number; max: number; hits = new Map<string, number[]>(); constructor(windowMs: number, max: number) { this.windowMs = windowMs; this.max = max } allow(key: string): boolean { const now = Date.now(); const arr = (this.hits.get(key) || []).filter(t => now - t < this.windowMs); if (arr.length >= this.max) return false; arr.push(now); this.hits.set(key, arr); return true } }

五、整合示例

type Req = { query: Record<string, string | undefined>; headers: Record<string, string | undefined> }

function handleList(req: Req, res: Res, maxSize = 100, gate = new RateGate(1000, 5)) {
  const key = (req.headers['x-user-id'] || 'anon') + ':' + (req.headers['x-tenant-id'] || 'anon')
  if (!gate.allow(key)) { res.setHeader('Retry-After', '1'); return res.end(JSON.stringify({ error: 'rate_limited' })) }
  const size = Number(req.query['size'] || '20')
  const cursorStr = req.query['cursor'] || undefined
  if (!validPageSize(size, maxSize)) return res.end(JSON.stringify({ error: 'invalid_size' }))
  const cursor = decodeCursor(cursorStr)
  const sql = buildQuery('SELECT id, name FROM items', cursor, size)
  const items = Array.from({ length: size }, (_, i) => ({ id: (cursor.id || '0') + '_' + i, name: 'item' + i }))
  const nextCursor = items.length ? String(items[items.length - 1].id) : undefined
  sendPage(res, { items, nextCursor }, size, maxSize)
}

六、验收清单

  • 游标格式与页大小参数校验通过;最大页大小强制;响应头包含X-Next-Cursor/X-Max-Page-Size
  • 速率限制每Key每窗口内请求数上限;Backpressure通过页大小与速率协同实现。
  • 查询构造包含ORDER BY id ASCLIMIT;避免过深offset导致资源耗尽。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部