我在 Mac 写了个服务,硬要它在 18 岁高龄的 Windows 服务器上跑,结果…
前言
事情是这样的。
我有个朋友(以下称他为"怨种朋友"),找到我说: "帮我写个 Go 服务,在你自己 Mac 上开发,最后要能跑在咱们公司那台快入土的 Windows 2008 服务器上。"
我当时的内心毫无波澜,甚至还想笑: "多大点事?Go 跨平台编译了解一下?分分钟搞定!"
然后我被现实按在地上摩擦了 72 个小时。
第一幕:自信如我
在 Mac 上用 Go 写服务,行云流水:
mkdir cool-service && cd cool-service
go mod init cool-service
# 噼里啪啦一顿猛写...
# 省略 1000 行代码...
go run main.go
# ✅ 完美运行!
怨种朋友问: "能编译成 Windows 的 exe 吗?"
我邪魅一笑: "一行命令的事!"
GOOS=windows GOARCH=amd64 go build -o cool-service.exe main.go
三天后,怨种朋友发来消息: "跑不起来,报错说缺少什么 DLL。"
我: "???"
第二幕:18 岁高龄的服务器
我后来才知道,那台 Windows 2008 服务器是 2008 年发布的——比我表弟的年龄都大。
Go 1.21+ 编译出来的 exe 依赖比较新潮的 Windows API,但这台老爷机表示: "这些是啥?不认识。"
而且最关键的是:64 位的 exe 在 Windows 2008 上各种水土不服,但 32 位的反而跑得欢快。
第三幕:Mac 上多版本 Go 共存
问题来了:我 Mac 上跑的是 Go 1.26,总不能为了编译一个 exe 就把整个开发环境降级吧?
答案是:当然不用!Go 官方本身就支持多版本并存,手动操作非常简单。
步骤一:下载老版本 Go
去 Go 官网下载页:go.dev/dl/
找到你需要的版本,比如 go1.18.10.darwin-arm64.tar.gz(注意选对架构,M 芯片选 arm64,Intel 选 amd64)。
# M 芯片 Mac
curl -O https://go.dev/dl/go1.18.10.darwin-arm64.tar.gz
# Intel Mac
curl -O https://go.dev/dl/go1.18.10.darwin-amd64.tar.gz
步骤二:解压到指定目录
mkdir -p ~/go-versions
tar -C ~/go-versions -xzf go1.18.10.darwin-arm64.tar.gz
解压后会得到 ~/go-versions/go/ 目录,这就是一个独立的 Go 工具链。
步骤三:验证一下
~/go-versions/go/bin/go version
# go version go1.18.10 darwin/arm64
# 你原来的 Go 一点没受影响
go version
# go version go1.26.1 darwin/arm64
两个版本和平共处,谁也不碍着谁。
步骤四:编译时指定老版本
GOOS=windows GOARCH=386 CGO_ENABLED=0 \
~/go-versions/go/bin/go build -ldflags="-s -w" -o cool-service.exe main.go
完事!1.26 照常用,1.18 单独放着编译老系统用的 exe。
小技巧: 你可以设个别名方便使用:
# 加到 ~/.zshrc alias go18='~/go-versions/go/bin/go' alias go26='go'之后
go18 build就是用 1.18 编译,go26 build就是用 1.26 编译。
第四幕:32 位才是 yyds
编译命令解析:
GOOS=windows GOARCH=386 CGO_ENABLED=0 \
go build -ldflags="-s -w" -o cool-service.exe main.go
# ┌──────────────┬──────────────────────────────────┐
# │ 参数 │ 含义 │
# ├──────────────┼──────────────────────────────────┤
# │ GOOS=windows │ 目标操作系统:Windows │
# │ GOARCH=386 │ 32 位架构(兼容老系统的关键!) │
# │ CGO_ENABLED=0│ 禁用 CGO,不依赖外部动态库 │
# │ -ldflags= │ 去掉符号表和调试信息 │
# │ "-s -w" │ exe 体积更小,大约能减 30% │
# └──────────────┴──────────────────────────────────┘
怨种朋友测试后: 跑起来了!🎉🎉🎉
我感动得差点落泪。
翻车与自救总结
| 我以为 | 实际上 |
|---|---|
| 一行命令搞定跨平台 | Windows 2008:你谁啊? |
| Go 版本越高越好 | 老服务器需要老版本 Go + 32 位编译 |
| 64 位才是主流 | 18 岁的服务器只认 32 位 |
| 网上说的都靠谱 | 还是自己踩坑最深刻 |
写在最后
经过这次血泪史,我深刻领悟了一个道理:
写代码之前,一定要问清楚目标服务器到底有多"老"。
如果再让我选一次,我一定会对怨种朋友说:
"兄弟,咱们商量一下,换台服务器?或者给这台烧点纸钱?它真的该入土了。"
不过话说回来,Go 的跨平台能力是真的香——在 Mac 上敲一行命令,就能扔给 Windows 服务器跑。要搁 C++ 时代,光配环境就能配三天三夜。
愿天下没有跑不起来的 exe。 🙏