You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// https://github.com/nodejs/node/blob/main/src/node.cc#L486intmain(intargc, char*argv[]) {
// ...// Disable stdio buffering, it interacts poorly with printf()// calls elsewhere in the program (e.g., any logging from V8.)setvbuf(stdout, nullptr, _IONBF, 0);
setvbuf(stderr, nullptr, _IONBF, 0);
returnnode::Start(argc, argv);
}
背景
Q 同学反映自己负责的 SSR 项目线上环境 Node.js 性能指标「首字节 TTFB 」上涨了约 30ms, 而当次发布没有涉及配置的变更、基础包的升级等, 甚至同样的代码重新构建发布又恢复正常了...
问题排查
Cat 监控
首先查看了一下监控系统, 发现 SSR 项目涉及的渲染时延 Transaction , 后端接口时延 Transaction 等关键指标均没有明显波动
于是只能追加了更多的时延 Transaction 打点, 最终发现竟然是一行日志输出时而达到了 15ms 时而又小于 1ms 到可以忽略, 每个请求存在两条这样的日志输出故导致性能上涨了 30ms 🤯
pino 日志
对于上面一个小于 200 字符串的日志输出为什么会消耗了 15ms, 继续查看代码发现使用的 logger 其实是基于 pino 6.11.3 版本
然后再查看 pino.destination 的实现发现了比较刺眼的
sync: true
传参继续深挖发现当传参是
sync: true
时会调用fs.writeSync(process.stdout.fd
去写日志, 可以理解为等同于console.log
k8s 支持
最后询问了 k8s 的同学, 为什么一行同步输出的日志时延波动这么大? k8s 同学给出的回答是我们的 Pod 上的标准输出会落到云盘中, 网络抖动就可能造成这样的波动
ChatGPT 支持
当然 k8s 的同学不会忽悠我, 但本着学习的心理让我们继续向 ChatGPT 🤖️ 来探索一下这个问题。当 k8s 标准输出落到云盘的场景中, 同步日志输出是否会阻塞程序直到云盘写入成功?
问题解决
通过传入
sync: false
来进行异步写入日志即可解决本次的问题问题探索
这里一个疑问是 ChatGPT 指出同步输入日志会先写入缓冲区, 所以不会阻塞, 那么实际情况我们还是被阻塞了 15ms 了?
对 Node.js 源码比较了解的同学应该知道 Node.js 程序刚开始运行的代码中就通过 setvbuf 系统调用把缓冲区给禁止掉了...
所以我们把传参改为
sync: false
时, 看看 sonic-boom 是如何实现虽然 Node.js 禁止了系统输出流的缓冲行为, 不代表 JavaScript 层面不可以通过把数据缓存在内存中进行模拟实现, 实际 sonic-boom 就是这样去做的
最后要说明的一点是当 sonic-boom 设置的缓存数据上限后, 开始进行 i/o 写入操作时会有一个请求会被阻塞?
其实是不会的, 当达到设置的上限值 sonic-boom 会调用
fs.write
去异步写入日志, 而写入这个操作是在子线程中进行的, 故不会阻塞主线程, 这部分内容见我之前写的【libuv 源码学习笔记】线程池与i/oThe text was updated successfully, but these errors were encountered: