Node.js crypto.randomBytes同步异步性能对比及生产选型问题
问题背景
crypto.randomBytes 同时支持同步、异步两种调用方式。提问者开发了一个公共库,基于这个API实现了生成16字符长度唯一transactionId的方法,用于微服务间通信场景,目前已经被100多个基于Node.js构建的微服务接入使用。
提问者对两种调用方式做了基准测试,结果和预期差异很大,想确认生产环境应该选异步还是同步实现。
基准测试代码
异步方案测试代码
// asyncRandomBytes.js crypto = require("crypto"); for (var i = 0; i < 2000000; i++) { crypto.randomBytes(16, function(err) {}); }
执行耗时统计:
% time node asyncRandomBytes.js node asyncRandomBytes.js 20.79s user 13.67s system 210% cpu 16.336 total
同步方案测试代码
// syncRandomBytes.js crypto = require("crypto"); for (var i = 0; i < 2000000; i++) { crypto.randomBytes(16); }
执行耗时统计:
% time node syncRandomBytes.js node syncRandomBytes.js 3.92s user 1.41s system 132% cpu 4.017 total
测试结果显示异步版本耗时远高于同步版本,提问者咨询:在单核机器部署、单服务承载20K RPM API请求的生产环境下,是否应当选用异步版本?
回答
直接选同步版本即可,不需要用异步版,原因如下:
- 你的基准测试结果是准确的,16字节的随机数生成属于极轻量的CPU操作,本身耗时只有微秒级。异步版本需要把任务提交到libuv线程池、等待线程调度、完成后再把回调塞回主线程执行,整套上下文切换的开销远大于生成随机数本身的逻辑开销,性能比同步版差是必然的,不是测试写错了。
- 算下实际场景的负载:20K RPM换算下来每秒大概334个请求,按你测的同步版性能,200万次调用总耗时4秒,单次调用耗时仅2微秒。这点事件循环阻塞时间完全可以忽略,根本不会导致请求排队、响应延迟升高的问题。
- 你是单核机器部署,异步版依赖的线程池根本拿不到额外的CPU核心,多线程切换反而会带来额外的CPU开销,进一步拉低服务整体吞吐,和你测试里看到的异步版CPU占用更高、总耗时更长的结果完全匹配。
- 同步版本代码逻辑更简单,不需要封装Promise、处理回调,公共库接入方用起来也更顺手,不会出现异步上下文丢失、错误捕获遗漏的问题。
只有当你单次生成的随机数长度达到KB级、或者单服务QPS超过数万且同时跑着其他重CPU逻辑的时候,才需要考虑异步版本,你当前的场景完全达不到需要用异步的阈值。
内容的提问来源于stack exchange,提问作者Siddharth Kumar
相关产品推荐
相关产品推荐

