用 Wails 写一个跨平台进程管理器

0 阅读5分钟

我机器上常年跑着几个自建服务:后端接口、队列消费者,还有几个想起来才用的临时脚本。每次开机都得手动恢复一遍,漏掉哪个就得翻终端找。

现成的工具试过一些。systemd 只认 Linux,supervisor 得先把它自己跑起来,pm2 更偏 Node。我的机器横跨 Windows、macOS 和 Linux,配置语法各不相同,为一个临时脚本写 unit 文件也不划算。

ProcHub 就是这个时候写的:Wails + Go + Vue 3,Apache 2.0 开源,三个系统都有构建产物。

壳子为什么是 Wails

界面只是一张表加几个弹窗,用 Electron 每个窗口塞一个 Chromium 进去,几十 MB 内存起步,不太值。Wails 用系统 WebView,Windows 上是 WebView2,macOS 上是 WKWebView,Go 侧的方法直接暴露给前端,最后打出来是一个可执行文件。

不好的一面也真实存在。WebView2 在 Windows 上不是必装组件,碰到干净的机器得先引导装;调试工具链比 Electron 生涩不少。我还是选了它,原因是容易出问题的地方在子进程那边,那部分代码在 Go 里,图形界面挂了也能用命令行复现。

停一个进程比启一个麻烦

spawn 子进程几行代码就完了,麻烦的是收尾。点停止时先发终止信号,给它一个超时窗口自己释放端口、把缓冲写完,超时还没退再强杀。

Windows 上没有 POSIX 信号,只能走 Job Object 或者 taskkill。这块是三平台里最费劲的,也最容易在测试时漏掉。

重启次数和最近一条错误都跟着记录走,每次拉起覆盖一次,退出时记下时间和退出码,表里那几个数字才有意义。

重启策略给了三个:always、on_failure、never。

always 给常驻服务用,但必须配最大重试次数。我吃过这个亏:端口被占用,进程起来就退,退完又起,日志几秒钟涨几十兆。

on_failure 只在退出码非零时重试,正常退出就停。

一次性任务选 never,跑完记下退出码,不要被自动拉起。

应用启动时还有一个自动拉起的开关,和系统开机自启是两件事。一开始我把它俩做在一起,结果关掉开机自启,进程也不起来了,后来又拆开。

ProcHub 主界面进程列表

日志写文件,不留内存

最省事的做法是内存里留个环形缓冲,前端拉最近几千行。我一开始也是这么写的,后来改成按进程 id 写到数据目录,滚动保留,份数可配。

改的原因是一次排查。某个服务半夜启动失败,早上打开应用,它已经在重启策略下跑起来了,内存里只剩最近几分钟输出,前面那次退出的堆栈没了。写文件之后至少还能往前翻。

stdout 和 stderr 分开存。混在一起看容易误判。进程反复退出的时候先看 stderr,一般就是端口占用、工作目录写错、环境变量没传进去这几样。

代价是磁盘和 IO。实时日志走文件增量读取,界面关掉也能读,因为读的是文件,不是进程内存。

开机自启,三个系统各写一套

这块我没做抽象。macOS 写 LaunchAgent 的 plist,Linux 用 XDG Autostart 放 .desktop 文件,Windows 改注册表的 Run 项。三者的格式、生效时机、卸载方式都不一样,硬包一层只会让排查更难。

设置页里的主题、语言、开机自启、版本更新都在同一个页面,底下调用的还是这三套系统机制,没有自建守护进程。卸载干净是我想要的,代价是每上一个平台都要重新测。

ProcHub 设置页,主题、语言与开机自启

命令行和界面是同一个二进制

不带参数启动是界面,带子命令走 CLI。

prochub process list
prochub process start api
prochub logs api --tail 200
prochub logs api --follow
prochub config set theme dark
prochub status

logs --follow 相当于 tail -f,写部署脚本的时候省事。config get / set 动的主题、语言、开机自启和界面是同一份配置,不会出现命令行改完界面不认的情况。

有个约束得提前说:CLI 得应用开着才能用,它操作的是进程里的运行状态。要做成常驻守护进程,那就是另一个产品了,这版没往那走。

WSL 是个单独的开关

WSL 里的服务没法当本地进程拉起,只能通过 wsl 命令转发。所以命令行里多了一组:

prochub wsl status
prochub wsl start
prochub wsl restart
prochub autostart-wsl on

autostart-wsl 我没有跟应用的开机自启绑在一起。WSL 拉起来要占内存和时间,不是每次开机都想让它跑。

WSL 里的输出和本地进程走同一条日志管线,界面上看不出区别,只是启动时多一步 wsl 转发。

构建要三台机器

需要 Go 1.23、Node 18 和 Wails CLI。Wails 的 GUI 产物不能交叉编译,三个平台各自构建:Windows 出 amd64,macOS 出 Intel 和 Apple Silicon 两份,Linux 出 amd64 和 arm64。

wails build

本地开发用 wails dev,前端热更新,Go 侧改完自动重编译,比每次手动打包省事。

代价在发布流程上。一旦改动碰到平台相关的代码,三个系统都得跑一遍,所以那部分我一直尽量往薄里写。

还没做的

进程之间没有依赖顺序,A 依赖数据库得自己保证;日志只有本地,没有集中收集;也没法管别的机器上的进程。

依赖顺序这件事我犹豫过。真要做得先定一套声明语法,再处理循环依赖和启动失败的降级,工作量比现在整个工具都大。暂时先放着,等我自己被它绌到再说。