一、 缘起:为什么需要 mole-go?
在开发微信公众号、调试支付接口、以及演示本地开发网站时,或由于服务器资源限制需要在本地部署服务时,frp 是不可或缺的内网穿透神器。然而,原生的 frpc 存在几个显著的痛点:
- 运行隐形性差:必须开启命令行窗口,一旦误关服务即中断。
- 配置门槛高:新手难以记忆复杂的 .toml 参数。
为了解决这些问题,我开发了 mole-go。它是一个轻量级、跨平台的桌面客户端,旨在实现 frp 的配置、启动与监控一体化。我选择 Wails v3 则是看中了其原生渲染、系统托盘支持、Go 强力后端以及极小的打包体积。
二、 核心架构:Go + Wails v3 的化学反应
mole-go 采用了经典的“UI-Backend-Service”三层架构:
- Wails UI:负责前端展示,通过事件驱动(Event-Driven)与后端交互。
- Go Backend:核心大脑,负责业务逻辑、进程管理与系统级 API 调用。
- frpc 二进制:底层服务,通过 Go 的 embed 特性内嵌到二进制文件中。
三、 关键实现细节:从命令行到图形化的进化
- 前端:从“面条代码”到模块化数据驱动
早期版本中,我直接采用 window.startFrp,window.stopFrp这样的写法,导致代码碎片化严重,以及管理app运行状态不方便。在 mole-go 的正式版中,我将其重构为数据驱动模式,类似Vue,由数据驱动界面:
- 模块化封装:定义全局 window.App 对象,将数据状态与行为(Methods)统一封装,使代码结构清晰。
- 动态 UI 组件:针对 HTTP、TCP、UDP 等不同代理模型,不再机械地堆砌 HTML 片段,而是通过逻辑判断实现“按需渲染”,大大精简了 DOM 结构。
- 后端:全局实例与事件机制
为了保证服务层(Service)能随时与 UI 通信,我设计了一个全局 App 实例,这样可以方便得调用和管理。
- 状态约定:前后端约定一套状态码,通过 Wails v3 的 Events 机制,后端可以主动向前端推送 frpc 的运行状态、日志等信息。
- 独立服务层:将 frp 相关逻辑抽离到专门的文件中,通过 Wails 的 Binding 暴露给前端,保持代码的解耦。
- 系统深度集成
- 系统托盘(System Tray):利用 Wails v3 原生的托盘支持,实现了“关闭即隐藏”逻辑。
- 外部链接调用:使用 wails3自带的 Browser.OpenURL 方法,确保点击文档链接时能正确唤起系统浏览器。
可参考项目源码。
四、 深度避坑指南:跨平台开发的硬核干货
在开发过程中,碰到了以下问题,我也写出了自己的解决思路。
- 进程管理:优雅地“杀死”子进程
坑:主程序直接退出时,frpc 没有正常关闭往往会变成“僵尸进程”。
解:优化frpc关闭逻辑,并针对不同平台实现不同清理逻辑。
- Windows:使用 taskkill /F /T /PID 确保杀掉整个进程树。
- Unix-like:使用 syscall.Kill(-pid, syscall.SIGKILL) 杀掉进程组。
- 跨平台适配:Build Tags 的妙用
坑:不同平台的路径、命令处理都不相同,需要区分平台,我代码中使用了大量的if runtime.GOOS代码,这个是运行时判断,但是在编译跨平台时,Github Action会报错。
解:采用在文件中嵌入build标签,可以在go编译在相应平台编译相关平台代码,可以实现以下效果:
- 二进制嵌入:使用 go:embed 结合 Go Build Tags(如 //go:build windows),根据不同平台打包对应的 frpc 执行文件。
- 平台宏定义:启动逻辑和清理逻辑分别写在 service_windows.go 和 service_linux.go 中,编译时自动按需加载,避免了大量的 if runtime.GOOS == … 代码,也解决了编译时报错的问题。
Wails v3 的生命周期管理
坑:我先前在 Service 中释放资源,发现我无法有效杀掉frpc进程,我打印了日志,但是也没有发现相关输出日志。
解:经过多种实践,我将资源释放逻辑(如关闭 frpc、清理临时文件)从 Service 层调整到 App 生命周期钩子中,在app创建的实例中,添加了属性OnShutdown,确保程序退出前所有子进程被正确销毁。日志 OOM 风险控制以及输出到前端优化
坑:当frpc 日志量大时,前端 DOM 节点过多会有导致内存溢出的情况,以及当frpc大量日志输出时,会造成前端卡顿的情况。
解:在Go后端添加了日志切片,用来存储日志,然后固定500毫秒发送到前端,同时在前端逻辑中加入循环队列缓存,固定保留最新的 1000 行日志,旧日志自动丢弃,也优化了渲染代码,确保 UI 长时间运行依然流畅。
五、 性能与体验优化
- 单文件分发:以前计划是将frpc客户端通过打包的形式随主程序一块发布,但是发现这样有个很大的弊端,用户会看到两个文件,并且frpc很容易被本地电脑杀毒软件删除。我通过 embed 内嵌各平台 frpc 二进制方式,用户下载后只有一个文件,双击即可使用,同时在运行的时候释放frpc客户端,可以有效减缓杀毒问题发生。
- 静默运行:添加了系统托盘,通过托盘化设计,frpc 在后台静默工作,这个解决了frpc命令行窗口关闭就停止服务的问题,并且托盘可以仅在需要调整配置或查看日志时才唤起界面,极大地降低了心智负担。
六、 总结与未来计划
开发 mole 客户端的过程让我深切体会到:写出一个工具容易,但要做一个好用的工具很难。 从处理跨平台的路径差异,到精简前端的数据流,每一个细节的优化都是对开发者基本功的考验,也对技能提升有很大帮助。
目前 mole 客户端已经能够满足日常生产环境的使用。未来的方向将专注于稳定性维护与兼容性测试,我希望它能成为 frp 用户手中最趁手的“瑞士军刀”。
项目地址:GitHub - littletow/mole-go
如果你觉得 mole-go 对你有帮助,欢迎前往 GitHub 为项目点个 Star ⭐,或者在 Issues 中提出你的建议!