async-sema 资源池实战用 initFn 自定义令牌构建 Redis 连接池【免费下载链接】async-semaSemaphore using async and await项目地址: https://gitcode.com/gh_mirrors/as/async-semaasync-sema是一个为async/await量身定制的轻量级信号量Semaphore库。它没有采用传统的计数器思路而是把并发许可管理成一张令牌列表让你不仅能限制同时执行的任务数量还能让令牌本身承载真实资源——比如一条 Redis 连接。本文将以构建 Redis 连接池为例一步步教你用initFn自定义令牌实现资源的创建、复用与优雅退出让并发控制真正落地。为什么 Node.js 需要信号量Node.js 是单线程事件循环但异步任务可以并发执行。假如你的服务要处理 1000 个并发请求每个请求都去 Redis 新建一条连接Redis 很快会不堪重负出现连接数暴涨、超时甚至崩溃。信号量正是解决这类问题的经典工具它只允许N 个任务同时执行其余任务安静排队等前面的任务释放位置后再继续。async-sema 遵循传统信号量的定义只放行固定数量的任务其余任务原地等待而不是像某些异步信号量那样让所有任务都先跑起来、最后再统一排队。async-sema 的核心机制令牌Token而非计数器大多数信号量用剩余许可数这个变量来控制并发。async-sema 则不同它在内部维护一个令牌队列free队列存放空闲令牌waiting队列存放等待者。这个设计带来一个非常实用的能力令牌可以是任意对象。也就是说你可以用信号量直接管理真实资源——数据库客户端、HTTP 连接甚至文件句柄。const { Sema } require(async-sema); const s new Sema(3); // 允许 3 个任务并发 async function task() { await s.acquire(); // 拿令牌 try { // 执行你的业务逻辑 } finally { s.release(); // 还令牌 } }initFn 是什么自定义令牌的关键入口Sema的构造函数接受一个选项对象其中最值得关注的就是initFnnew Sema(nr, { initFn })nr允许同时获取令牌的调用者数量必填initFn初始化令牌的函数默认是() 1即令牌只是一个占位符capacity预分配等待列表的大小适合高并发场景优化性能。关键点在于构造信号量时initFn会被调用nr次用来预先生成全部令牌。如果我们让initFn返回一个真正的 Redis 客户端那么每个令牌就等价于一条可用的连接——资源池就这样诞生了。这一行为在项目测试中有明确验证见test/sema.test.ts中的 initFn 用例。实战用 initFn 构建 Redis 连接池项目自带的examples/pooling.js就是最标准的连接池范例我们一步步拆解它。第 1 步安装依赖npm install --save async-sema promise-redis第 2 步用 initFn 生成 Redis 连接令牌const { Sema } require(async-sema); const redis require(promise-redis); const pool new Sema(3, { initFn: () redis().createClient(process.env.REDIS_URL) });这一行代码完成了三件事创建信号量、规定池子大小3 条连接、用initFn提前备好全部 Redis 客户端。第 3 步acquire 拿连接release 还连接const db await pool.acquire(); // 从池中取一条连接 try { console.log(await db.get(id)); // 用它执行业务 } finally { pool.release(db); // 用完归还供他人复用 }注意两点使用initFn时acquire()返回的令牌必须原样传回release()归还操作建议放在finally中即使业务抛错也能保证连接不泄漏。第 4 步drain() 优雅关闭进程退出前清理程序要退出时逐条释放连接太麻烦。async-sema 提供了drain()它会一次性取回所有令牌const clients await pool.drain(); // 取回全部 3 个 Redis 客户端 clients.forEach(db db.quit()); // 统一关闭干净退出drain()是进程退出前的安全网确保没有遗留的等待任务和未释放的连接。连接池使用的最佳实践永远在 finally 中 release防止异常导致连接泄漏这是资源池的第一守则用 tryAcquire() 做非阻塞尝试池子满时立即返回undefined适合拿不到就算了的场景用 nrWaiting() 监控排队情况返回正在等待的调用者数量可用于告警和容量评估设置 capacity 提升性能高并发场景下预分配等待列表可减少扩容开销具体实现可参考src/index.ts。进阶玩法限流与背压控制除了连接池async-sema 还附带两把利器RateLimit 限流器用极简 API 实现每秒请求数限制支持自定义时间窗口与均匀分布见examples/rate-limiting.jspauseFn / resumeFn 背压控制当等待者堆积时暂停输入流避免内存被撑爆见examples/pausing.js非常适合处理超大文件的场景。小结一句话总结async-sema 用令牌机制把并发控制和资源管理合二为一。initFn让你自定义令牌内容acquire/release负责借还drain负责收尾。无论你是想给 Redis 建连接池还是给数据库、HTTP 客户端做资源复用这套模式都同样适用。想亲手跑一遍完整的连接池示例克隆仓库后进入examples目录运行即可git clone https://gitcode.com/gh_mirrors/as/async-sema cd async-sema/examples node pooling.js 从信号量到资源池就差一个initFn的距离【免费下载链接】async-semaSemaphore using async and await项目地址: https://gitcode.com/gh_mirrors/as/async-sema创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考