没有不值得去解决的问题,也没有不值得去学习的技术!

A Tour of Go 多语言翻译项目:接入 Google Analytics 与百度统计,从 systemd 注入到 EdgeOne 与真实上报验证

图 3:GA4 g/collect 请求返回 HTTP 204,并携带当前课程页面地址,确认 Google Analytics 已产生真实事件上报

作者:

在

A Tour of Go 多语言翻译项目

图3:访问过去的 Go Tour 简体中文站 tour.go-zh.org 时,当前已经无法正常建立连接。

(1) 从「A Tour of Go 中文版」这个搜索词开始:我决定做一个持续维护的 Go Tour 中文版

本地运行的 A Tour of Go「Methods continued」课程页面,左侧为课程说明,右侧为 Go 代码编辑器及运行结果。

(2) A Tour of Go 中文版项目设计冻结:从 101 页到 103 页,从 Gin 转向 Cobra CLI

A Tour of Go 中文版开发实战:英文基线、在线运行与 101 页上游同步体系

(3) A Tour of Go 中文版开发实战:英文基线、在线运行与 101 页上游同步体系

go-tour-i18n 项目完成首个简体中文课程页面 welcome/1 的整页翻译、结构校验和本地预览。

(4) A Tour of Go 多语言翻译项目实录:完成首个 zh-CN 页面翻译闭环

图 1:DeepSeek 官方更新日志显示,本次只更新 DeepSeek-V4-Flash,DeepSeek-V4-Pro API 与 APP、Web 模型均未更新

(5) A Tour of Go 中文翻译项目进展:完成前 8 页、修复 present 语法,并暂缓 DeepSeek 对比

图 1:generics/1 简体中文页面本地预览

(6) 一个页面重试五次:使用 GLM-5.2 翻译 A Tour of Go 时遇到的 Token 与 Present 结构问题

图 1:methods/16 简体中文页面浏览器预览

(7) A Tour of Go 中文翻译实录:如何只翻译教学代码注释,而不破坏 Go 代码

图 2:A Tour of Go methods/20「练习:错误」中文候选页面最终渲染效果

(8) A Tour of Go 多语言翻译:当正确的中文语序被受保护标记顺序校验误判

图 3:methods/24 中文页面,静态代码、Bounds 行内代码和链接内的 image.Rectangle 均正确渲染

(9) A Tour of Go 中文翻译完成 7 个代表页校准:最后 3 页又发现了哪些真实问题

图 2:首批 10 个普通页面经过试跑与流程校准后全部进入 ready

(10) A Tour of Go 多语言翻译项目:首批 10 个普通页面试跑,从 2 个 blocked 到全部 ready

图 2:flowcontrol/6 最终中文页面,return 与 v 为适应自然中文语序发生整体换位

(11) A Tour of Go 中文翻译第二批实跑:从 Inline Code 顺序误判到历史响应重新验证

图 1:flowcontrol/6 首次原始输入实验直接通过统一自动校验

(12) A Tour of Go 多语言翻译项目:从 swap 问题重新审视 Token 保护,原始输入与最小保护的一次真实实验

图 7:moretypes/1 最终中文页面预览。左侧课程正文、静态代码块、行内代码和教学注释均正常渲染,右侧官方 Go 示例继续保持原样。

(13) A Tour of Go 多语言翻译实战:第三批 10 页全部 Ready,继续校准 Protected Token 的结构角色

图 4:上游源码确认共有 103 个课程页面,zh-CN 最终达到 ready=103、pending=0、blocked=0

(14) A Tour of Go 多语言翻译项目:zh-CN 103 个课程页面全部 ready,课程正文翻译阶段完成

图 4:GitHub 与 ChatGPT 已成功连接

(15) ChatGPT 连接 GitHub 实测:让 AI 直接读取 go-tour-i18n 仓库参与译文审核

图 3:中文 Go 术语校准完成后,将确定的译法写入 zh-CN glossary

(16) 为什么 channel 最终选择“通道”:A Tour of Go 中文术语统一的一次实践

图 1:技术含义基本正确,但“返回一个返回……”已经明显影响阅读,因此仍被列入 C 类修订

(17) 103 页逐页审核后,只返修 16 页:A Tour of Go 中文正文发布前质量审计

图 2:中文课程页面以及已经完成本地化的编辑器控制区

(18) A Tour of Go 多语言翻译项目:公共 UI 本地化完成,从课程译文走向完整中文界面

图 2:完整 zh-CN 正式投影成功生成,103 个课程页面被重新组装为 7 个 .article

(19) 从 103/103 ready 到完整站点验收:A Tour of Go 中文版补齐正式投影与课程元数据本地化

图 3:从 production bundle 独立启动的 A Tour of Go 简体中文页面,课程正文、公共 UI 和官方 Go 示例均已进入最终发布形态。

(20) A Tour of Go 多语言翻译项目:103 页全部完成后,我终于生成了可独立部署的 zh-CN Production Bundle

图 1:A Tour of Go 简体中文版正式生产页面,远程运行已经成功输出“Hello, 世界”。

(21) A Tour of Go 多语言翻译项目:zh-CN 正式上线,从生产发布到 go.dev Playground 的完整部署记录

图 1:新生产域名 go-dev.shuijingwanwq.com 已正常访问,Go 示例远程运行成功

(22) A Tour of Go 多语言翻译项目:从 go-tour 到 go-dev,完成 EdgeOne、Nginx 与 HTTPS 生产域名迁移

图 1:A Tour of Go 多语言翻译项目正式生产首页

(23) A Tour of Go 多语言翻译项目正式上线:从项目首页、公共 Footer 到生产发布元数据的完整收尾

图 3:GA4 g/collect 请求返回 HTTP 204,并携带当前课程页面地址,确认 Google Analytics 已产生真实事件上报

(24) A Tour of Go 多语言翻译项目:接入 Google Analytics 与百度统计,从 systemd 注入到 EdgeOne 与真实上报验证

图 1:生产环境 robots.txt 与 sitemap.xml 已正确对外提供,Sitemap 共包含 104 个规范 URL。

(25) A Tour of Go 中文站上线后:补齐 robots.txt、Sitemap,并完成五大搜索引擎提交

图 4:发布生产并刷新 EdgeOne 缓存后,再次通过真实手机访问,顶栏项目名称已经完整保持在一行,最终移动端验收通过。

(26) A Tour of Go 多语言翻译项目:修复手机端首页顶栏标题换行,并完成真机验证

图 3:生产自动部署脚本第一次真实运行时,systemd 已经显示 active,但首次 localhost 探测仍然得到 HTTP 000;随后连续 3 次同时满足 active + HTTP 200 后,脚本才判定部署成功,并继续完成公网 HTTP 200 验收。这次真实运行验证了连续应用层健康检查的必要性。

(27) A Tour of Go 多语言翻译项目:从一次 203/EXEC 故障到生产自动部署脚本落地

图 1:阿里云生产环境访问 go.dev Playground 时出现 TLS 握手超时

(28) A Tour of Go 多语言翻译项目:为 Playground 搭建独立的 ZgoCloud 执行代理

图 3:清除 /tour/script.js 缓存后,Run 已直接请求 ZgoCloud /compile 并返回 HTTP 200

(29) A Tour of Go 多语言翻译项目:将生产 Run / Format 切换到 ZgoCloud 执行节点

【图 1:微信中运行 Go 示例时出现 Error communicating with remote server.】

(30) A Tour of Go 中文版手机端运行与格式化偶发失败排查:从间歇性报错到补强 Nginx 可观测性

【图 4:向 golang-dev 公开邮件列表发送 Playground 使用告知邮件并投递成功】

(31) A Tour of Go 多语言项目:为 Playground 代理增加唯一 User-Agent,并主动告知 Go 官方

【图 3:百度剩余 9 个核心 URL 提交成功,remain 归零】

(32) A Tour of Go 搜索引擎收录实测:从 Google 自然索引到百度主动提交与 Bing IndexNow

【图 1:当前正式中文 methods/24 页面,右侧示例代码运行成功】

(33) A Tour of Go 中文翻译完成后,我为什么重新评估 minimal-protect 翻译模式

【图 3:5 个页面 Request SHA256 完全一致,但 Response SHA256 全部不同】

(34) A Tour of Go 翻译实验:更多原始上下文为什么没有更好?从 157 个保护标记到 30 次重复盲评

图 3:3 个代表页 × 4 个评审模型,共 12 次匿名评审的完整排名与第一名票数。

(35) A Tour of Go 翻译质量再评估:minimal-protect 未达预期后,我开始比较 ChatGPT、Codex 与 GLM-5.2

图 2:GitHub 中的 chatgpt-zh-CN-008 重译批次。一个 Batch 同时保存 inputs、raw-responses、candidates、validation 和 retry 记录,GitHub 成为 ChatGPT 与本地翻译流水线之间的数据交接层。

(36) 没有 OpenAI API,我如何用 ChatGPT + GitHub 完成 A Tour of Go 103 页全量重译

图 1:103 个正式课程页面完成最终语义质量审核,结果为 A=103、B/C/D=0,同时不存在缺失、重复或额外页面。A/B/C/D 是本轮项目内部语义质量分级,并不是自动结构 validator 的结果。

(37) 103 页翻完还不能发布:A Tour of Go ChatGPT 译文的语义审核与 Canonical Promotion

图 3:7 个 article endpoint 的 production origin 与 EdgeOne 公网响应全部 exact match,同时新的 ChatGPT 译文标记存在,旧版译文标记已经消失。

(38) 103/103 与 7/7 Exact Match:A Tour of Go ChatGPT 新译文的最终线上验收

【图 2:upstream source preview】

(39) A Tour of Go 多语言翻译项目首次实现 upstream 同步机制:从一次性翻译到长期维护

图 2:A/B/C/D 质量评级规则,展示正式质量 rubric 与 Promotion gate。

(40) A Tour of Go 多语言翻译项目引入 Translation Quality Review:从自动验证到 AI 翻译质量控制

图 4:翻译后 Playground Example,展示中文注释与保持不变的 Go 代码结构。

(41) A Tour of Go 多语言翻译项目实现 Playground Example 本地化:从代码注释翻译到完整学习体验

图 5:公网页面验证广告脚本输出

(42) A Tour of Go 多语言翻译项目接入广告系统:从 AdSense 专用配置到通用 HTML 注入架构

图 3:百度统计单页应用设置

(43) go-dev 单页网站接入统计与 Google AdSense Auto Ads 验证记录

图 1:GitHub 仓库中的多语言翻译项目结构

(44) A Tour of Go 多语言翻译项目:选择日语作为第二语言并启动翻译验证

图 5:模型身份描述不一致

(45) A Tour of Go 多语言翻译项目中的 AI 协作异常排查:一次长期工程会话失效分析

【图 1:ChatGPT GitHub 写入流程调整说明截图】

(46) go-tour-i18n 翻译流程调整:从 ChatGPT GitHub 写入到本地 artifact 导入

图 4 展示了本次异常状态。

(47) go-tour-i18n 翻译自动化流程复盘:已验证的 ChatGPT 翻译能力在新任务中的稳定性问题

【图 6:全部评审结束以后才读取 secret key,完成最终揭盲】

(48) A Tour of Go 翻译质量再评估:Codex High 已接近 ChatGPT High,我决定调整默认翻译引擎

图 4:ja-JP 生产课程页实际运行 Go 示例

(49) A Tour of Go 日语版正式上线:第二门语言 ja-JP 完成生产发布

图 1:ja-JP 上线以后,先提交“新增语言标准化流程”

(50) A Tour of Go 多语言扩展复盘:为第三门语言建立标准化流程

图 1:课程正文结束以后,页面左侧仍然存在大块视觉空白,但 AdSense 并没有在这里插入展示广告。

(51) A Tour of Go 明明有大片空白,为什么 AdSense 就是不显示展示广告?

图 2:golang/website 当前的 _content/tour/。课程内容、静态资源和模板都在这个上游目录中持续维护。

(52) 为了 AdSense,要不要放弃 SPA?A Tour of Go 页面架构的一次取舍

图 4:最终的手动课程广告真实展示在课程内容区域内,而不是 footer 之后。

(53) 保留 SPA 以后,广告应该放在哪里?从 AdSense 预览到方案 B

图 1:Google Search Console 中,多个不同的 A Tour of Go 课程 URL 被判断为“重复网页,用户未选定规范网页”。

(54) SPA 的 SEO 老问题:为什么最后还是给 103 个 A Tour of Go 课程页生成了 Prerender HTML

图 2:方案 B 生产环境中,源码已经正常,但课程主体高度异常缩短,footer 提前进入第一屏。

(55) 从源码空白到 AdSense 高度污染:A Tour of Go 方案 B 的生产收尾

图 1:A Tour of Go 德语版 de-DE 已正式运行在生产环境

(56) A Tour of Go de-DE 上线复盘:第三门语言用了约 16 小时,下一门如何压到 8 小时?

图 1:fr-FR 首次 production 验收完成后,我没有立即开始 ko-KR,而是先连续完成新增语言与首次生产流程的优化

(57) 法语版上线后,我没有马上开始韩语:A Tour of Go 新增语言流程的一轮完善与简化

图 1:根据 ko-KR 期间 76 次 Git 提交估算实际工作时间

(58) 原本预计 6 小时,最后用了约 16~18 小时:A Tour of Go 韩语版上线复盘

图 2:英文 source 与当时发生 validation failure 的 ko-KR candidate 对比

(59) Validator 报错,到底该改译文还是改规则?一次 A Tour of Go 韩语翻译实战

图 2:法语 A Tour of Go 仓库目前已经归档,页面仍保留 553 Commits 和 61 Contributors 的历史

(60) 从法语 553 次提交到韩语两代离线:A Tour of Go 社区翻译为什么难以长期维护

【图 4:es-ES 首次 Production 收口后连续完成的流程优化 commit】

(61) 从 es-ES 首次上线复盘:我把 A Tour of Go 新增语言的 Production 流程又收敛了一轮

图 1:旧公网验收链路连续出现 curl 28 超时,最终导致 public-machine 阶段失败

(62) A Tour of Go Production 公网验收踩坑:从跨境 SOCKS 链路改为 zgocloud direct

图 1:两次 Production Machine Acceptance 都已经通过,但 Headless Chrome 分别在 / 和 /tour/ 判定页面没有完成渲染

(63) 页面明明能打开,为什么 Headless Chrome 仍然判定 Production 失败?

图 3:Bing 从 Google Search Console 中识别到新的 it-IT 站点,并同时发现已有的 1 个 sitemap

(64) GSC → Bing Webmaster Tools:多语言站点如何减少重复验证

图 3:英文站 3 小时内出现 565 个 Cloudflare 来源 URL

(65) IndexNow 实战:手工提交长期不显示,Cloudflare Crawler Hints 却很快出现在 Bing Webmaster Tools

【图 1:A Tour of Go 项目首页目前的语言版本列表】

(66) 从 nl-NL 到 tr-TR:A Tour of Go 连续上线三门语言后,我又优化了哪些流程

【图 4:Go local 页面中已经出现 French、German、Korean、Simplified Chinese】

(67) 从邮件无人回复到进入 Go 官方 Go local:我的 4 个 A Tour of Go 翻译站终于被收录

图8:最终只保留一把启用的新 API Key

(68) 腾讯云 EdgeOne API 自动化:从 CAM 子用户到最小权限 API Key 的完整配置

【图 7:Production Release Batch 最终汇总】

(69) 从同步 Go 官方上游到一个命令发布 10 个语言站:A Tour of Go Production 流程的再次收敛

【图 3:Cloudflare Smart Tiered Cache 已启用】

(70) Cloudflare 缓存 HIT 很快,MISS 却不稳定:回源阿里云杭州的一次真实优化

【图 3:简体中文站 Support 页面】

(71) 广告之外,社区项目如何长期活下去?我给 A Tour of Go 多语言项目加了 Support

图5:评论成功发布

(72) 从 Go Weekly 到 Reddit、DEV 和 Go Forum:第一次系统推广 A Tour of Go 多语言项目

【图 4:RTL 桌面课程布局修复后的效果】

(73) 第一次把 A Tour of Go 做成 RTL:阿拉伯语版本上线,以及我踩过的几个坑

A Tour of Go 简体中文站完成正式部署以后,我开始补齐生产环境的访问统计。

这次准备接入两套已经在博客中长期使用的统计服务:Google Analytics 4 和百度统计。

一开始看,这件事似乎很简单:把两段 JavaScript 放进页面 <head> 就可以了。

但真正开始处理以后,发现还涉及不少生产环境问题:

  • Go Tour 现有代码是否已经预留统计代码入口;
  • 本地预览与生产环境是否走相同的初始化逻辑;
  • 首页 / 和 /tour/... 是否共用同一个模板;
  • 统计代码应该直接写进模板,还是通过生产环境配置注入;
  • systemd 应该怎样持久加载包含 HTML 和 JavaScript 的环境变量;
  • 新版程序上线后,EdgeOne 会不会继续返回旧 HTML;
  • 最后怎样证明统计代码不仅“出现在网页源码里”,而且真的向 Google 和百度完成了数据上报。

最终我没有为统计功能另外设计一套配置系统,而是继续利用项目原来就存在的 TOUR_ANALYTICS,并把验收分成三个层次:

Plaintext
源站
→ EdgeOne 公网
→ 浏览器真实上报
图 1:已经正式运行的 A Tour of Go 简体中文站点
图 1:已经正式运行的 A Tour of Go 简体中文站点

一、项目原来已经存在 AnalyticsHTML

首先需要确认的是:现有 Go Tour 到底有没有统计代码注入机制。

搜索代码以后,可以看到共享 Tour 模板 _content/tour/template/index.tmpl 中本来就存在:

Plaintext
{{.AnalyticsHTML}}

继续沿着 AnalyticsHTML 往上追踪,最终得到生产环境的大致调用链:

Plaintext
cmd/tour-i18n publish
→ 构建 cmd/tour-production
→ tour.NewProductionHandler(...)
→ RegisterHandlersLocale(...)
→ analyticsHTML = template.HTML(os.Getenv("TOUR_ANALYTICS"))
→ initTour(...)
→ renderIndex(...)
→ {{.AnalyticsHTML}}

也就是说,项目原本已经设计了一套生产统计代码入口:

TOUR_ANALYTICS

生产进程启动时读取这个环境变量,再把里面的内容作为 HTML 注入页面 <head>。

这里还有一个容易造成误解的地方:读取 TOUR_ANALYTICS 的逻辑位于 internal/tour/appengine.go。

但现在的 A Tour of Go 中文站并没有运行在 Google App Engine 上。

当前生产链路仍然是:

Plaintext
EdgeOne
→ Nginx
→ 127.0.0.1:3999
→ Go production binary

appengine.go 只是上游项目历史演进留下来的文件名。

这也再次说明,在维护一个基于上游项目改造而来的仓库时,不能仅凭文件名判断实际生产行为,还是应该沿着真正的调用关系继续追踪。


二、本地开发默认不加载生产统计代码

项目中的普通本地运行使用 local.go。

这里定义的是一个默认空值:

Go
var analyticsHTML template.HTML

本地预览不会主动读取 TOUR_ANALYTICS。

而 production 初始化时才会执行:

Go
analyticsHTML = template.HTML(os.Getenv("TOUR_ANALYTICS"))

这正好符合目前项目的需求:

Plaintext
本地开发
→ 默认没有生产统计

正式生产
→ 通过 TOUR_ANALYTICS 开启统计

因此没有必要再引入 .env 解析库,也没有必要增加新的命令行参数或者配置系统。

统计仍然只是一个生产部署配置。


三、真正缺失的是首页 /

继续审计以后发现,现有统计入口其实还不完整。

Tour 课程页面使用 _content/tour/template/index.tmpl,其中已经包含:

Plaintext
{{.AnalyticsHTML}}

但独立首页 / 使用的是另外一个模板:

_content/tour/template/home.tmpl

这个模板原来没有 AnalyticsHTML。

也就是说,如果此时直接给 production 设置 TOUR_ANALYTICS,结果会是:

Plaintext
/tour/...
→ 有统计代码

/
→ 没有统计代码

对于一个完整的独立站点来说,这显然不理想。

因此这次真正需要修改的 Go 项目代码其实非常少:让首页模板也复用已经存在的 AnalyticsHTML。

对应提交为:

cac4d6e feat: 为首页接入统一统计代码

修改后,首页 / 与课程页面 /tour/... 使用同一个 TOUR_ANALYTICS。

同时增加自动测试,分别覆盖两种状态:

Plaintext
analyticsHTML 为空
→ 首页和 Tour shell 都没有统计代码

analyticsHTML 已配置
→ 首页和 Tour shell 都恰好注入一次

这里没有增加第二套环境变量,也没有把 Google Analytics 或百度统计代码硬编码进模板。


四、百度统计继续与博客共用

我的 WordPress 中文站和英文站原来已经共用同一套百度统计代码。

在百度统计后台的“受访域名”报告中,可以分别看到:

  • www.shuijingwanwq.com
  • en.shuijingwanwq.com

也就是说,即使多个子域共用一套百度统计代码,仍然可以按照受访域名拆分查看数据。

因此对于新的:

go-dev.shuijingwanwq.com

我最终没有新建一个独立的百度统计站点,而是继续复用博客现有的百度统计代码。

这样以后既可以查看所有站点的总体数据,也可以通过受访域名分别分析中文博客、英文博客和 A Tour of Go 中文站。

Google Analytics 也采用了相同思路,继续使用博客现有的 GA4 配置。

对我当前的使用场景来说,这比维护多套统计账号和多个独立配置更简单。


五、没有采用普通 .env,而是使用 systemd EnvironmentFile

接下来需要解决的是生产服务器上的配置存放问题。

Go 程序读取的是标准环境变量:

Go
os.Getenv("TOUR_ANALYTICS")

它本身并不会自动读取项目目录中的 .env 文件。

而生产服务本来就由 systemd 管理,因此没有必要为了一个统计变量,再给 Go 项目增加 .env 加载代码。

服务器当前使用的 systemd 版本为 239,已经支持 EnvironmentFile=。

所以我最终建立了专用环境变量文件:

/etc/go-tour/go-tour.env

权限设置为:

Plaintext
root:root
600

然后通过 systemd drop-in:

/etc/systemd/system/go-tour.service.d/analytics.conf

加载这个文件:

INI
[Service]
EnvironmentFile=/etc/go-tour/go-tour.env

最终形成的生产配置关系很清楚:

Plaintext
/etc/go-tour/go-tour.env
        ↓
systemd EnvironmentFile
        ↓
TOUR_ANALYTICS
        ↓
Go production binary
        ↓
{{.AnalyticsHTML}}
        ↓
首页 / + /tour/... 页面

Google Analytics 和百度统计两套代码都放在同一个 TOUR_ANALYTICS 中。

实际统计代码和具体统计 ID 不进入 Git 仓库。


六、为什么这个环境变量仍然需要严格控制权限

统计 ID 本身最终会出现在网页源码中,并不是严格意义上的服务器秘密。

但 TOUR_ANALYTICS 本身却值得更谨慎地管理。

原因在于程序执行的是:

Go
template.HTML(...)

也就是说,Go 模板明确把这个环境变量里的内容当作可信 HTML,不会按照普通字符串再次转义。

这是统计代码能够正常注入 <script> 的基础,但同时也意味着:

谁能够修改 TOUR_ANALYTICS,谁实际上就能够向生产页面 <head> 注入任意 HTML 或 JavaScript。

因此,把环境变量文件设置为:

Plaintext
root:root
600

是合理的。

这也是为什么我没有把完整统计代码直接放进 Git,也没有把它作为普通启动参数写在命令行里。


七、先验证 systemd → Go → Tour 页面

systemd drop-in 配置完成以后,先执行:

Bash
systemctl daemon-reload
systemctl restart go-tour.service

然后从源站本机直接请求课程页面。

结果确认:

Plaintext
Google ID count: 2
Baidu ID count: 1

Google ID 出现两次是正常的,因为标准 GA4 代码中通常会分别出现在标签加载地址和 gtag("config", ...) 中。

百度统计 ID 出现一次。

这一步证明:

Plaintext
EnvironmentFile
→ TOUR_ANALYTICS
→ production binary
→ Tour shell

整条链路已经正常。


八、重新生成 production release,让首页也获得统计代码

不过此时生产服务器运行的还是旧 binary。

旧版已经支持 /tour/... 的 TOUR_ANALYTICS,但还没有包含刚刚为首页 / 增加的统计入口。

因此接下来重新生成了一份 production bundle:

Plaintext
/tmp/go-tour-release-20260812-zh-CN-cac4d6e

构建结果仍然保持:

Plaintext
locale=zh-CN
ready=103
pending=0
blocked=0
pages=103
articles=7

同时检查完整的 SHA256SUMS,所有文件均通过。

发布包里的两个模板也分别确认包含:

Plaintext
{{.AnalyticsHTML}}

之后把新 bundle 上传至:

/data/go-tour/releases/20260812-zh-CN-cac4d6e

先在独立 release 目录中完成服务器端 SHA-256 校验和 go-tour 用户访问权限检查,没有马上切换 production。

确认全部通过以后,才把:

/data/go-tour/current

切换至新 release,并重启 go-tour.service。

服务状态正常:

active

随后重新检查源站首页 / 和 /tour/welcome/1:

Plaintext
/                       Google=2 Baidu=1
/tour/welcome/1         Google=2 Baidu=1

至此,Go 服务本身已经完成统计接入。

图 2:生产服务、systemd 环境变量以及源站与 EdgeOne 公网页面的统计代码最终验收
图 2:生产服务、systemd 环境变量以及源站与 EdgeOne 公网页面的统计代码最终验收

九、源站已经更新,EdgeOne 却仍然返回旧 HTML

这次部署中最值得记录的问题,反而出现在 CDN 层。

源站已经明确包含 Google Analytics 和百度统计代码,但通过公网域名访问时却得到:

Plaintext
Google ID count: 0
Baidu ID count: 0

继续查看响应头以后,发现:

Plaintext
EO-Cache-Status: HIT
Age: 4727

这意味着问题并不在 Go 服务,也不在 TOUR_ANALYTICS。

而是 EdgeOne 仍然在返回大约一个多小时前缓存的旧 HTML。

这也是一个很典型的生产环境问题:

源站验证成功,并不代表公网用户已经看到新版本。

尤其这次修改的是 HTML shell,而不是带版本号的静态 JS 或 CSS。

如果 CDN 缓存仍然命中,统计代码即使已经在源站上线,真实访客仍然不会获得它。

因此我在 EdgeOne 中执行了 go-dev.shuijingwanwq.com 的 Hostname 级缓存清除。

没有只刷新 / 或某一个课程页面,因为整个 /tour/... 下已经存在大量可能被缓存的 URL。

清除以后再次测试,响应变成:

Plaintext
EO-Cache-Status: MISS

并且公网 HTML 已经变为:

Plaintext
Google=2
Baidu=1

随后部分请求重新出现 HIT 也完全正常,因为此时缓存的已经是包含统计代码的新版页面。

这次最终整理出的生产验收摘要为:

Plaintext
Release: 20260812-zh-CN-cac4d6e
Service: active
EnvironmentFile: /etc/go-tour/go-tour.env

Source /                  Google=2 Baidu=1
Source /tour/welcome/1    Google=2 Baidu=1
Public /                  Google=2 Baidu=1
Public /tour/welcome/1    Google=2 Baidu=1

这一步让我把统计部署验收明确拆成了两层:

Plaintext
源站正确
≠
公网一定正确

CDN 必须单独验证。


十、代码出现在 HTML 中,还不代表统计真的工作了

到这里,其实仍然只能证明:

Google Analytics 和百度统计代码已经进入访客拿到的 HTML。

但统计服务是否真的工作,还需要浏览器运行 JavaScript 后才能确认。

因此最后一层验收放在 Firefox 开发者工具的 Network 面板中完成。


十一、Google Analytics:真实 g/collect 返回 204

首先过滤:

collect

可以看到请求发往:

analytics.google.com/g/collect

请求中包含当前 GA4 的 tid,同时 dl 参数已经正确记录:

https://go-dev.shuijingwanwq.com/tour/welcome/1

其中一次请求是 90% 页面滚动事件:

Plaintext
en=scroll
epn.percent_scrolled=90

响应状态:

204

这就不再只是“Google 标签加载成功”。

它证明浏览器已经真正执行 GA4 代码,并把 A Tour of Go 中文站上的实际用户事件发送给 Google Analytics。

图 3:GA4 g/collect 请求返回 HTTP 204,并携带当前课程页面地址,确认 Google Analytics 已产生真实事件上报
图 3:GA4 g/collect 请求返回 HTTP 204,并携带当前课程页面地址,确认 Google Analytics 已产生真实事件上报

十二、百度统计:hm.gif 返回 200

随后把 Network 过滤条件改成:

hm.baidu

可以看到两类主要请求:

  • hm.js
  • hm.gif

其中 hm.js 是百度统计脚本,而 hm.gif 则包含实际统计上报。

请求参数中同样可以看到当前页面:

https://go-dev.shuijingwanwq.com/tour/welcome/1

响应状态为:

200 OK

因此百度统计也完成了真正的数据上报。

图 4:百度统计 hm.gif 请求返回 HTTP 200,并携带当前课程页面地址,确认百度统计已经完成真实数据上报
图 4:百度统计 hm.gif 请求返回 HTTP 200,并携带当前课程页面地址,确认百度统计已经完成真实数据上报

十三、最终形成三层统计验收

经过这次部署以后,我把生产统计功能的验收方式固定成了三个层次。

第一层是源站。

直接检查:

Plaintext
http://127.0.0.1:3999/
http://127.0.0.1:3999/tour/welcome/1

这里验证的是:

Plaintext
systemd
→ EnvironmentFile
→ TOUR_ANALYTICS
→ Go 模板

第二层是公网。

检查:

Plaintext
https://go-dev.shuijingwanwq.com/
https://go-dev.shuijingwanwq.com/tour/welcome/1

这里除了统计代码本身,还要注意:

Plaintext
EO-Cache-Status
Age

避免 CDN 仍然返回旧 HTML。

第三层才是浏览器真实请求。

Google Analytics 看:

analytics.google.com/g/collect

百度统计看:

hm.baidu.com

最终确认响应状态分别正常。

这样得到的结论比单纯“网页源码里有 <script>”可靠得多。


十四、把生产配置写进运维文档

全部验证完成以后,我又把这套配置补进了:

docs/PRODUCTION_RUNBOOK.md

对应提交:

0759457 docs: 补充生产统计配置与验收流程

文档现在明确记录了:

  • TOUR_ANALYTICS 的统一统计入口;
  • /etc/go-tour/go-tour.env;
  • systemd EnvironmentFile drop-in;
  • 首页 / 和 /tour/... 共用统计机制;
  • 实际统计 ID 与完整代码不进入 Git;
  • 修改环境变量后的服务重启要求;
  • EdgeOne HTML 缓存可能继续返回旧页面;
  • “源站 → 公网 → 浏览器”的三层验收方式;
  • Google g/collect 和百度 hm.gif 的真实上报验证。

这样即使以后迁移服务器、重建 systemd 服务或者重新部署 A Tour of Go,也不会因为配置只存在于服务器上而忘记统计系统的接入方式。


十五、这次真正解决的不是“加两段统计代码”

回头看,这次工作真正有价值的部分,并不是把 Google Analytics 和百度统计的 JavaScript 复制到网页里。

更重要的是把统计能力纳入了现有生产架构:

Plaintext
生产配置
→ systemd
→ Go 服务
→ 模板
→ EdgeOne
→ 浏览器
→ 第三方统计服务

而且每一层都有可以单独验证的结果。

最终形成的是:

Plaintext
本地开发
→ 默认没有生产统计

生产环境
→ EnvironmentFile 注入 TOUR_ANALYTICS

首页 /
→ 有统计

/tour/...
→ 有统计

EdgeOne
→ 已确认新版 HTML

Google Analytics
→ g/collect HTTP 204

百度统计
→ hm.gif HTTP 200

这比直接把统计脚本硬编码进模板多走了一些步骤,但长期维护会简单得多。

下一次需要替换统计代码时,不需要重新修改 Go 源码;迁移生产服务器时,也有明确的环境变量文件和 systemd 配置可以恢复。

而 CDN 是否已经拿到新版 HTML、浏览器是否完成真实上报,也都有固定的验收方法。

对于一个刚刚完成正式发布的 A Tour of Go 多语言项目来说,这算是又补齐了一块比较重要的生产基础设施。

A Tour of Go 多语言翻译项目正式上线:从项目首页、公共 Footer 到生产发布元数据的完整收尾 A Tour of Go 中文站上线后:补齐 robots.txt、Sitemap,并完成五大搜索引擎提交

A Tour of Go 多语言翻译项目

本系列完整记录 A Tour of Go 多语言翻译项目从架构设计、整页翻译、结构保护、自动校验,到生产发布与后续维护的实际开发过程。

项目入口:
✅ Brazilian Portuguese — Português (Brasil)
✅ Dutch — Nederlands
✅ French — Français
✅ German — Deutsch
✅ Italian — Italiano
✅ Japanese — 日本語
✅ Korean — 한국어
✅ Simplified Chinese — 简体中文
✅ Spanish — Español
✅ Turkish — Türkçe
✅ 项目源码:GitHub:shuijingwan/go-tour-i18n

项目正在持续扩展更多语言版本,并长期维护翻译质量、生产发布与后续更新。项目属于非官方社区多语言翻译项目,与 Go 官方无隶属或授权关系。