Using node with fork(2)
Node.js And ShadowNode
Why we want them
- Runtime 冷启动通常需要约 50~100ms
- 嵌入式设备性能相当紧凑
- 函数运行时请求响应时间敏感
嵌入式环境
- 多进程适应多使用场景切换
- 紧凑的设备性能、能耗限制
函数运行时
- 需要良好的运行环境隔离
- 需要控制每次执行资源使用
函数运行时
- 单进程服务无法隔离运行环境
- VM Contexts 无法隔离运行时操作
- WorkerThreads 无法细分单次资源使用
What we want
- 更低的 CPU 时间使用
- 更快到达进程可使用状态
- 能够良好地隔离运行环境
- 能够良好地控制运行资源
What happens on launch
- 初始化 uv/vm/platform
- (code-cache/snapshot)
- bootstrap loader/node/env
- main/pre_execution
- user scripts
- libraries
启动时间都消耗在哪儿了
- Parsing/Compiling
- Interpreting
- Repeat
Alternatives
- code cache
- snapshot
- fork(2)
Recovering from fork(2)
- 需要重新创建工作线程
- 需要保护同步原语不被污染
- 重新初始化 IPC 句柄
fork(2) with node.js
- 事件驱动范式
- 单线程模型不代表单线程实现
- Native Addon 访问系统 API
Recover: uv_loop_fork
- epoll backend fd
- async backend fd
- signals
- Threaded tasks from VM
- Threaded tasks from JS land
- Threaded tracing
- 暂不支持 worker_threads
Recover: node addons
- 没有成标准 API 提供 addon 恢复线程工作
- 同样 builtin 模块线程工作也无法恢复
Limitations
- 需要持续运行 seed 进程
- seed 进程特性使用有较大限制
- 需要有对系统的权限控制权限
using 'node' as runtime... (node v12.8.1)
fork/hive-fork-child-process.js n=100: 5.954ms
fork/node-fork-child-process.js n=100: 27.218ms
fork/node-spawn-child-process.js n=100: 24.239ms
using 'node' as runtime... (node v12.8.1)
require-lodash/hive-fork-child-process.js n=100: 5.872ms
require-lodash/node-fork-child-process.js n=100: 37.843ms
require-lodash/node-spawn-child-process.js n=100: 34.628ms
using 'iotjs' as runtime... (iotjs v0.11.7)
fork-child-process.js n=100: 2.863ms
node-fork-child-process.js n=100: 36.566ms
node-spawn-child-process.js n=100: 25.797ms
What's next
- Collect Usage Case
- Standardize Fork API