---
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 ASC与LIMIT;避免过深offset导致资源耗尽。

发表评论 取消回复