在 Next.js/Vercel 项目中使用 async-sema:API 限流与并发保护的完整方案

📅 2026/8/20 16:30:34
在 Next.js/Vercel 项目中使用 async-sema:API 限流与并发保护的完整方案
在 Next.js/Vercel 项目中使用 async-semaAPI 限流与并发保护的完整方案【免费下载链接】async-semaSemaphore using async and await项目地址: https://gitcode.com/gh_mirrors/as/async-semaasync-sema 是 Vercel 团队开源的一个轻量级信号量Semaphore库核心代码位于src/index.ts它基于async和await实现专门用于解决 Node.js 应用中的API 限流与并发保护问题。在 Next.js 的 API 路由和 Vercel Serverless 函数中async-sema 能以几行代码限制同一时刻执行的任务数量帮助你稳定调用第三方 API、保护数据库连接池避免服务被突发流量打垮。本文将用最通俗的方式带你从零掌握 async-sema 的完整用法。为什么 Next.js 项目需要 API 限流与并发保护先理解为什么要。在日常开发中你会遇到三类典型问题典型场景痛点调用第三方 API如 AI 接口超出配额被限流甚至封禁操作数据库连接池被打满请求排队超时高并发访问 API 路由内存暴涨Serverless 实例崩溃举个最直观的例子你用 Next.js 写了一个 AI 对话接口用户疯狂点击瞬间发出 1000 个并发请求而供应商的 API 只允许每分钟 60 次调用。此时你的接口大概率会收到一堆 429 错误甚至被供应商拉黑。而信号量Semaphore正是解决这类问题的经典工具它允许最多 N 个任务同时执行其余任务排队等待。 传统信号量只记录剩余名额这个数字而 async-sema 把名额管理为一组令牌这带来了一些有趣的高级玩法后面会讲到。async-sema 安装方法一条命令搞定在 Next.js 项目中使用 async-sema安装非常简单npm install async-sema如果你使用 yarn也可以这样yarn add async-sema安装完成后就可以在任意 API 路由中引入了。在 Next.js API 路由中实现并发保护核心三步这是 async-sema 最基础也最常用的场景。假设我们要限制某个 API 路由最多同时处理 4 个请求import { Sema } from async-sema; const sema new Sema(4); // 最多允许 4 个并发任务 export async function GET(request) { await sema.acquire(); // 1. 获取令牌 try { // 2. 在这里执行真正的业务逻辑 return Response.json(await fetchExternalApi()); } finally { sema.release(); // 3. 无论成功失败都要释放令牌 } }只要记住这核心三步你就掌握了信号量的基本用法acquire()尝试获取一个令牌没有就排队等待执行业务只有拿到令牌的任务才能进入关键代码release()用完后释放令牌放行下一个等待者⚠️一定要把 release() 放在 finally 里否则一旦业务代码抛错令牌永远不会释放最终导致死锁。用 RateLimit 实现 API 限流最省心的方案如果你的需求是每秒最多调用 N 次async-sema 还内置了一个开箱即用的限流器RateLimit源码同样位于src/index.tsimport { RateLimit } from async-sema; const lim RateLimit(5); // 每秒最多 5 次 async function callAI(prompt) { await lim(); // 排队等待直到速率允许 return fetch(https://api.example.com/v1/chat, { method: POST, body: JSON.stringify({ prompt }), }); }RateLimit还有两个实用参数参数作用timeUnit自定义时间窗口毫秒如RateLimit(5, { timeUnit: 60000 })表示每分钟 5 次uniformDistribution开启均匀分布把 N 次请求均匀摊到整个时间窗口避免前 5 次秒发、后 59 秒干等当限流调用非常密集比如逐行读取大文件并发请求时建议开启uniformDistribution: true效果更平滑。async-sema 高级配置capacity、initFn 与背压控制如果你的项目对性能有更高要求async-sema 还提供了三个进阶选项完整示例都可以在项目的examples/目录中找到。capacity预分配等待队列capacity参数用于预分配等待队列的内存适合高并发场景下减少动态扩容的开销const sema new Sema(13, { capacity: 1000 });initFn用令牌管理真实资源这是 async-sema 最有特色的功能令牌可以不只是一个计数器而是真实的资源对象。例如用 3 个 Redis 客户端组成连接池参考examples/pooling.jsconst redis require(promise-redis); const pool new Sema(3, { initFn: () redis().createClient(process.env.REDIS_URL) }); const db await pool.acquire(); // 拿到一个真实的 Redis 客户端 // ... 使用 db pool.release(db); // 用完归还这相当于用信号量实现了一个优雅的连接池。pauseFn / resumeFn背压控制当等待队列过长时内存可能被撑爆。通过pauseFn和resumeFn你可以在队列积压时主动暂停数据流入比如暂停读取输入流参考examples/pausing.js的实现思路const sema new Sema(5, { pauseFn: () rl.pause(), // 队列满了暂停流 resumeFn: () rl.resume(), // 有空位了恢复流 });Next.js 实战保护上游 AI API 调用的完整示例把上面的知识串起来一个典型的 Next.js AI 代理接口长这样import { Sema } from async-sema; // 全局共享一个信号量限制全站同时最多 10 个 AI 调用 const sema new Sema(10); export async function POST(request) { await sema.acquire(); try { const { prompt } await request.json(); // 模拟调用上游 AI 服务 const res await fetch(https://api.example.com/ai, { /* ... */ }); return Response.json(await res.json()); } finally { sema.release(); } }几个要点信号量要定义在模块顶层全局共享而不是函数内部否则每次请求都会新建一个限流就失效了在 Vercel Serverless 环境中模块级变量在同一实例内是共享的足以应对大多数场景配合nrWaiting()可以在日志中观察排队人数console.log(sema.nrWaiting())async-sema 常见问题与避坑指南问题解决方案请求全部卡住不动检查是否忘了在 finally 中调用release()限流不生效确认 Sema 实例是模块级共享的而非每个请求新建进程退出前还有未完成任务用await sema.drain()排空信号量等待所有任务结束想不等待、直接尝试用tryAcquire()拿到令牌立即返回拿不到返回undefineddrain()和tryAcquire()都是非常实用的补充 API它们的行为可以在test/sema.test.ts的测试用例中看到完整验证。总结现在就给你的 Next.js 项目加上并发保护async-sema 用最精简的 API 解决了 Node.js 世界里最棘手的两个问题API 限流与并发保护。无论你是要保护第三方 AI 接口、数据库连接池还是防止 Serverless 函数被突发流量打垮它都能用几行代码搞定用Sema控制并发数量acquire()/release()成对使用用RateLimit快速实现请求频率限制用initFn把信号量升级为资源池用drain()优雅收尾现在就打开你的 Next.js 项目安装 async-sema给 API 加上第一道并发保护吧【免费下载链接】async-semaSemaphore using async and await项目地址: https://gitcode.com/gh_mirrors/as/async-sema创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考