写了十来年前端了,用的一直都是 JavaScript,理所当然地,后端接口也早已习惯了用 JavaScript 实现。
用 JavaScript 写前端,一个不可避免的问题,就是要在线上部署运行。
与本地开发不同的是,线上可是正式环境,不能再用开发模式运行部署,否则终端一断开,或者用户量一大,包括各种报错导致应用崩溃就麻烦了。

社区一直以来的最佳方案就是用 pm2 部署,我也用了很多年,非常不错。
但时间久了,官方的维护力度,更新周期,都进入了老牛拉车阶段,当然,既然能用,确实也用不着频繁更新。
但从使用者来说,还是希望能时不时看到有新东西,新功能出来的,包括后面 Bun 从 Node.js 嘴里咬了一块肉,PM2 对 Bun 的支持也是二等公民。

这两年我接触,使用,推广 MoonBit编程语言,深深感受到了这门语言的魅力和优雅,与编程语言的当红辣子鸡 Rust 相比,除了生态方面目前正在追赶之外,其他方面都不遑多让。
MoonBit 不仅可以生成 exe/可执行文件,还能生成 js 和最好的 wasm,一个语言就能搞定好几个主流场景。

所以,就产生了用 MoonBit 来重写 pm2 的想法,命名为 bm2,前后大概总共消耗 50亿 token,别以为用 AI 写的项目都是玩具,光是 bm2 的配置方案,风格,就讨论了两天,并修改了十几版。

今天 bm2 正式发布 0.6.0 版本,欢迎大家使用和反馈,开源地址是:https://github.com/chenbimo/moonbit-x。
这个仓库是我所有的 MoonBit 生态项目的集合,会开发更多有用,好用的软件进行开源分享。
那么说了这么多,下面详细分析 bm2 的架构,评测,场景,用法和配置。

评测对比
| 指标 | bm2 0.5.0 | pm2 7.0.4 |
|---|---|---|
| 安装体积 | 5.5 MB(2 个二进制) | 23 MB + 3036 个文件 |
| 运行时依赖 | 无 | Node.js ≥ 16 |
| 空闲守护进程内存 | ~2.6 MB | ~50 MB |
| 命令响应延迟 | ~1 ms | ~200-400 ms |
| ESM 顶层 await | 原生支持 | 需 CJS wrapper |
| daemon 崩溃后 | 原地收养存活实例(应用无感) | 按 dump 重启应用(会有中断) |
| 内置通知(10 事件 × 7 平台) | ✅ | ❌ 需第三方模块(已停止维护) |
bm2 是什么
一个用 MoonBit 写的 Linux 进程管理器,编译后就是两个原生二进制:bm2(CLI)和 bm2d(守护进程),加起来 5.5 MB,没有任何运行时依赖。
它管理 bun 和 Node.js 应用:
- fork 模式:每个实例独立进程、独立端口(适合 Nginx upstream)
- cluster 模式:所有实例绑同一端口,内核分发(不需要 Nginx 也能负载均衡)
- 崩溃自动重启(带预算,防止无限重启)
- 内存超限自动重启
- 掉电安全的状态持久化(原子写 + fsync)
- daemon 崩溃后原地收养存活实例(应用零感知)
- 内置通知系统:10 类事件 × 7 平台(飞书/钉钉/企业微信/Slack/Discord/webhook/邮件),即时 + cron 周期两种上报
配置是一个 TOML 文件,强校验,有效防止无效配置:
toml
name = "api"
cwd = "/wwwroot/api"
script = "index.js"
runtime = "bun" # 或 "node"
instances = 4 # cluster 模式,SO_REUSEPORT 单端口
port = 3020
max_memory_mb = 512 # 内存超限自动重启
max_restarts = 5 # 连崩 5 次停止自愈,标记 errored
为什么不用 pm2
1. 管理器不应该是一个运行时
pm2 是 Node.js 写的,这意味着:管理 Node 应用的前提是先装好并维护一个 Node 运行时。
守护进程 God 本身占 50MB 内存,每次命令交互是一次 RPC 往返(200-400ms)。
你的服务器上可能跑着十几个服务,为了管它们,得先养一个 50MB 的 Node 进程,还要定期 npm update 防漏洞。
bm2 编译后是两个静态二进制,守护进程 2.6MB,命令响应 1ms。
升级是一条命令(bm2 upgrade),自动换入新守护进程,应用不停。
2. cluster 分发层:pm2 是单点,bm2 是内核
pm2 的 cluster 模式:master 进程 accept 连接再分发给 worker。
master 挂了,所有 worker 丢失服务 handle,整组中断。
bm2 的 cluster 模式用 Linux SO_REUSEPORT:所有实例独立 bind 同一端口,内核按连接四元组哈希分发。
没有中间层,任何实例(包括守护进程)崩溃都不影响其余实例接收连接。
守护进程崩溃后新实例通过 pidfd(内核进程句柄)验证归属,原地收养存活进程------应用零感知。
pm2 还有一个 bm2 没有(也不需要)的坑:pm2 对 bun cluster 模式的支持靠一个 500ms 定时器猜测应用是否 online(源码注释原话:"temporary workaround")------因为 bun 不触发 Node cluster 的 online 事件。

3. bun + ESM:pm2 需要 wrapper,bm2 原生
befly(我的后端框架)入口是 ESM + 顶层 await:
js
const app = await createBefly(appConfig, menuConfig);
const server = await app.start();
pm2 fork 容器用 require() 加载入口,直接报错:require() async module is unsupported。
必须额外写一个 CJS wrapper 做动态 import(),还得手工注入端口、实例号、NODE_ENV 这些变量------漏一个就启动失败。
bm2 直接 bun index.js,ESM 顶层 await、JSON import、Bun.env 全部原生工作。
4. daemon 崩溃:pm2 重建,bm2 收养
pm2 daemon 崩溃后,重启按 dump 文件重新 spawn 所有应用(先杀掉残留再起新的)------应用会经历一次完整重启。
bm2 daemon 崩溃后,新守护进程逐实例验证 pidfd 归属:活着的进程原地收养(不中断),死去的按持久化状态标记。
状态文件每次变更原子写入(fsync + rename),掉电最坏情况是回退到上一次变更前。
这个能力是我在真实部署里反复验证过的:kill -9 daemon,4 个实例的 uptime 连续不断。
bm2 还做了什么 pm2 没做的
reusePort 探测
cluster 模式要求应用监听时开启 SO_REUSEPORT。
忘开了会怎样。
第一个实例正常 bind,后续实例静默失败,实际上只有一个实例在服务------流量容量直接打了 1/N,没有任何报错。
bm2 在启动后主动探测:向端口发连接,检查是否以 REUSEPORT 方式绑定。
未开启则整组停止,标记 reuseport_missing 错误,通知你修复。
防患于未然。
内存超限重启
max_memory_mb 配置后,bm2 每 5 秒采样 RSS,超限按异常重启处理(计入重启预算,防止内存泄漏应用无限循环重启)。
pm2 的 max_memory_restart 也可以,但轮询粒度和重启策略的可控性不如 bm2。
内置通知
bm2 内置了通知系统,不需要装任何第三方模块:
- 10 类事件:启动/停止/崩溃/预算耗尽/内存超限/OOM 判定/端口探测/磁盘压力/启动超时/阈值告警
- 7 个平台:飞书/钉钉/企业微信/Slack/Discord/webhook/邮件
- 两种周期:cron 健康巡检(全量体检)+ 每日定点统计(窗口峰值/重启次数)
- 双语 :配置
lang = "zh",聊天群里直接看中文
比如让 bm2 每 5 分钟给飞书机器人发一条健康巡检,每天 8 点发一份昨日统计:
toml
[[notify.report]]
name = "健康巡检"
cron = "*/5 * * * *"
template = "health"
[[notify.report]]
name = "每日统计"
cron = "0 8 * * *"
template = "stats"
事件日志
所有管理事件(启动/退出/重启/通知失败)写 JSONL 结构化日志,与应用 stdout/stderr 严格分离,方便 jq 查询和日志采集器采集。
安装
bash
# 安装 MoonBit 工具链(一次性)
curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash
# 安装 bm2(装到 ~/.moon/bin,无需配 PATH,后面的三个点没写错,直接复制!)
moon install chensuiyi/bm2/...
# 验证
bm2 version
在你的项目目录创建 bm2.toml,然后:
bash
bm2 start # 注册并启动
bm2 list # 查看状态
bm2 kill api # 停止并注销
项目地址:github.com/chenbimo/mo...(bm2 是 moonbit-x 仓库中的 bm2 成员)。
有问题欢迎提 issue 或私聊反馈,公众号 陈随易,官网:https://yicode.tech。