ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

bm2,Node.js 与 Bun 项目部署新选择

bm2,Node.js 与 Bun 项目部署新选择 写了十来年前端了用的一直都是 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.0pm2 7.0.4安装体积5.5 MB2 个二进制23 MB 3036 个文件运行时依赖无Node.js ≥ 16空闲守护进程内存~2.6 MB~50 MB命令响应延迟~1 ms~200-400 msESM 顶层 await原生支持需 CJS wrapperdaemon 崩溃后原地收养存活实例应用无感按 dump 重启应用会有中断内置通知10 事件 × 7 平台✅❌ 需第三方模块已停止维护bm2 是什么一个用 MoonBit 写的 Linux 进程管理器编译后就是两个原生二进制bm2CLI和bm2d守护进程加起来 5.5 MB没有任何运行时依赖。它管理 bun 和 Node.js 应用fork 模式每个实例独立进程、独立端口适合 Nginx upstreamcluster 模式所有实例绑同一端口内核分发不需要 Nginx 也能负载均衡崩溃自动重启带预算防止无限重启内存超限自动重启掉电安全的状态持久化原子写 fsyncdaemon 崩溃后原地收养存活实例应用零感知内置通知系统10 类事件 × 7 平台飞书/钉钉/企业微信/Slack/Discord/webhook/邮件即时 cron 周期两种上报配置是一个 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为什么不用 pm21. 管理器不应该是一个运行时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 ESMpm2 需要 wrapperbm2 原生befly我的后端框架入口是 ESM 顶层 awaitconstappawaitcreateBeflyappConfig menuConfig;constserverawaitapp.start;pm2 fork 容器用require加载入口直接报错require async module is unsupported。必须额外写一个 CJS wrapper 做动态import还得手工注入端口、实例号、NODE_ENV 这些变量——漏一个就启动失败。bm2 直接bun index.jsESM 顶层 await、JSON import、Bun.env 全部原生工作。4. daemon 崩溃pm2 重建bm2 收养pm2 daemon 崩溃后重启按 dump 文件重新 spawn所有应用先杀掉残留再起新的——应用会经历一次完整重启。bm2 daemon 崩溃后新守护进程逐实例验证 pidfd 归属活着的进程原地收养不中断死去的按持久化状态标记。状态文件每次变更原子写入fsync rename掉电最坏情况是回退到上一次变更前。这个能力是我在真实部署里反复验证过的kill -9 daemon4 个实例的 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 点发一份昨日统计[[notify.report]] name 健康巡检 cron */5 * * * * template health [[notify.report]] name 每日统计 cron 0 8 * * * template stats事件日志所有管理事件启动/退出/重启/通知失败写 JSONL 结构化日志与应用 stdout/stderr 严格分离方便 jq 查询和日志采集器采集。安装# 安装 MoonBit 工具链一次性curl-fsSLhttps://cli.moonbitlang.com/install/unix.sh|bash# 安装 bm2装到 ~/.moon/bin无需配 PATH后面的三个点没写错直接复制mooninstallchensuiyi/bm2/...# 验证bm2 version在你的项目目录创建bm2.toml然后bm2 start# 注册并启动bm2 list# 查看状态bm2killapi# 停止并注销项目地址github.com/chenbimo/moonbit-xbm2 是 moonbit-x 仓库中的 bm2 成员。
返回列表