标签 指针 下的文章

Go 1.25中值得关注的几个变化

本文永久链接 – https://tonybai.com/2025/08/15/some-changes-in-go-1-25

大家好,我是Tony Bai。

北京时间2025年8月13日,Go 团队如期发布了 Go 语言的最新大版本——Go 1.25。按照惯例,每次 Go 大版本发布时,我都会撰写一篇“Go 1.x 中值得关注的几个变化”的文章。自 2014 年的 Go 1.4 版本起,这一系列文章已经伴随大家走过了十一个年头。

不过,随着我在版本冻结前推出的“Go 1.x 新特性前瞻”系列,以及对该大版本可能加入特性的一些独立的解读文章,本系列文章的形式也在不断演变。本文将不再对每个特性进行细致入微的分析,因为这些深度内容大多已在之前的《Go 1.25 新特性前瞻》一文中详细讨论过。本文将更聚焦于提炼核心亮点,并分享一些我的思考。

好了,言归正传,我们来看看Go 1.25带来了哪些惊喜!

语言变化:兼容性基石上的精雕细琢

正如 Go 一贯所做的,新版 Go 1.25 继续遵循 Go1 的兼容性规范。最令 Gopher 们安心的一点是:Go 1.25 没有引入任何影响现有 Go 程序的语言级变更

There are no languages changes that affect Go programs in Go 1.25.

这种对稳定性的极致追求,是 Go 成为生产环境首选语言之一的重要原因。

尽管语法层面波澜不惊,但语言规范内部却进行了一次“大扫除”——移除了“core types”的概念。这一变化虽然对日常编码无直接影响,但它简化了语言规范,为未来泛型可能的演进铺平了道路,体现了 Go 团队在设计层面的严谨与远见。关于此变化的深度解读,可以回顾我之前的文章《Go 1.25 规范大扫除:移除“Core Types”,为更灵活的泛型铺路》。

编译器与运行时:看不见的性能飞跃

如果说 Go 1.24 的运行时核心是优化 map,那么 Go 1.25 的灵魂则在于让 Go 程序更“懂”其运行环境,并对 GC 进行了大刀阔斧的革新。

容器感知型 GOMAXPROCS

这无疑是 Go 1.25 最具影响力的变化之一。在容器化部署已成事实标准的今天,Go 1.25 的运行时终于具备了 cgroup 感知能力。在 Linux 系统上,它会默认根据容器的 CPU limit 来设置 GOMAXPROCS,并能动态适应 limit 的变化。

这意味着,只需升级到 Go 1.25,你的 Go 应用在 K8s 等环境中的 CPU 资源使用将变得更加智能和高效,告别了过去因 GOMAXPROCS 默认值不当而导致的资源浪费或性能瓶颈。更多细节,请参阅我的文章《Go 1.25 新提案:GOMAXPROCS 默认值将迎 Cgroup 感知能力,终结容器性能噩梦?》。

实验性的 Green Tea GC

Go 1.25 迈出了 GC 优化的重要一步,引入了一个新的实验性垃圾收集器。通过设置 GOEXPERIMENT=greenteagc 即可在构建时启用。

A new garbage collector is now available as an experiment. This garbage collector’s design improves the performance of marking and scanning small objects through better locality and CPU scalability.

据官方透露,这个新 GC 有望为真实世界的程序带来 10%—40% 的 GC 开销降低。知名go开发者Josh Baker(@tidwall)在Go 1.25发布正式版后,在X上分享了自己使用go 1.25新gc(绿茶)后的结果,他开源的实时地理空间和地理围栏项目tile38的GC开销下降35%:

这是一个巨大的性能红利,尤其对于重度依赖GC的内存密集型应用。虽然它仍在实验阶段,但其展现的潜力已足够令人兴奋。对 Green Tea GC 设计原理感兴趣的朋友,可以阅读我的文章《Go 新垃圾回收器登场:Green Tea GC 如何通过内存感知显著降低 CPU 开销?》。

此外,Go 1.25 还修复了一个存在于 Go 1.21 至 1.24 版本中可能导致 nil pointer 检查被错误延迟的编译器 bug,并默认启用了 DWARFv5 调试信息,进一步缩小了二进制文件体积并加快了链接速度,对DWARFv5感兴趣的小伙伴儿可以重温一下我之前的《Go 1.25链接器提速、执行文件瘦身:DWARF 5调试信息格式升级终落地》一文,了解详情。

工具链:效率与可靠性的双重提升

强大的工具链是 Go 生产力的核心保障。Go 1.25 在此基础上继续添砖加瓦。

go.mod 新增 ignore 指令

对于大型 Monorepo 项目,go.mod 新增的 ignore 指令是一个福音。它允许你指定 Go 命令在匹配包模式时应忽略的目录,从而在不影响模块依赖的前提下,有效提升大型、混合语言仓库中的构建与扫描效率。关于此特性的详细用法,请见《Go 工具链进化:go.mod 新增 ignore 指令,破解混合项目构建难题》。

支持仓库子目录作为模块根路径

一个长期困扰 Monorepo 管理者和自定义 vanity import 用户的难题在 Go 1.25 中也得到了解决。Go 命令现在支持在解析 go-import meta 标签时,通过新增的 subdir 字段,将 Git 仓库中的子目录指定为模块的根。

这意味着,你可以轻松地将 github.com/my-org/my-repo/foo/bar 目录映射为模块路径 my.domain/bar,而无需复杂的代理或目录结构调整。这个看似微小但备受期待的改进,极大地提升了 Go 模块在复杂项目结构中的灵活性。想了解其来龙去脉和具体配置方法,可以参考我的文章《千呼万唤始出来?Go 1.25解决Git仓库子目录作为模块根路径难题》。

go doc -http:即开即用的本地文档

这是一个虽小但美的改进。新的 go doc -http 选项可以快速启动一个本地文档服务器,并在浏览器中直接打开指定对象的文档。对于习惯于离线工作的开发者来说,这极大地提升了查阅文档的便捷性。详细介绍见《重拾精髓:go doc -http 让离线包文档浏览更便捷》。

go vet 新增分析器

go vet 变得更加智能,新增了两个实用的分析器:

  • waitgroup:检查 sync.WaitGroup.Add 的调用位置是否错误(例如在 goroutine 内部调用)。
  • hostport:诊断不兼容 IPv6 的地址拼接方式 fmt.Sprintf(“%s:%d”, host, port),并建议使用 net.JoinHostPort。

这些静态检查能帮助我们在编码阶段就扼杀掉一批常见的并发和网络编程错误。

标准库:功能毕业与实验探索

标准库的演进是每个 Go 版本的重要看点。

testing/synctest 正式毕业

在 Go 1.24 中以实验特性登场的 testing/synctest 包,在 Go 1.25 中正式毕业,成为标准库的一员。它为并发代码测试提供了前所未有的利器,通过虚拟化时间和调度,让编写可靠、无 flakiness 的并发测试成为可能。我曾撰写过一个征服 Go 并发测试的微专栏,系统地介绍了该包的设计与实践,欢迎大家订阅学习。

encoding/json/v2 开启实验

这是 Go 1.25 最受关注的实验性特性之一!通过 GOEXPERIMENT=jsonv2 环境变量,我们可以启用一个全新的、高性能的 JSON 实现。

Go 1.25 includes a new, experimental JSON implementation… The new implementation performs substantially better than the existing one under many scenarios.

根据官方说明,json/v2 在解码性能上相较于 v1 有了“巨大”的提升。这是 Go 社区多年来对 encoding/json 包性能诟病的一次正面回应。虽然其 API 仍在演进中,但它预示着 Go 的 JSON 处理能力未来将达到新的高度。对 v2 的初探,可以参考我的文章《手把手带你玩转 GOEXPERIMENT=jsonv2:Go 下一代 JSON 库初探》。jsonv2支持真流式编解码的方法,也可以参考《Go json/v2实战:告别内存爆炸,掌握真流式Marshal和Unmarshal》这篇文章。

sync.WaitGroup.Go:并发模式更便捷

Go 语言的并发编程哲学之一就是让事情保持简单。Go 1.25 在 sync.WaitGroup 上新增的 Go 方法,正是这一哲学的体现。

这个新方法旨在消除 wg.Add(1) 和 defer wg.Done() 这一对经典的样板代码。现在,你可以直接调用 wg.Go(func() { … }) 来启动一个被 WaitGroup 追踪的 goroutine,Add 和 Done 的调用由 Go 方法在内部自动处理。这不仅让代码更简洁,也从根本上避免了因忘记调用 Add 或 Done 而导致的常见并发错误。

关于这个便捷方法的来龙去脉和设计思考,可以回顾我之前的文章《WaitGroup.Go 要来了?Go 官方提案或让你告别 Add 和 Done 样板代码》。

其他:Trace Flight Recorder

最后,我想特别提一下 runtime/trace 包新增的 Flight Recorder API。传统的运行时 trace 功能强大但开销巨大,不适合在生产环境中持续开启。

trace.FlightRecorder 提供了一种轻量级的解决方案:它将 trace 数据持续记录到一个内存中的环形缓冲区。当程序中发生某个重要事件(如一次罕见的错误)时,我们可以调用 FlightRecorder.WriteTo 将最近一段时间的 trace 数据快照保存到文件。这种“事后捕获”的模式,使得在生产环境中调试偶发、疑难的性能或调度问题成为可能,是 Go 诊断能力的一次重大升级。更多详情可以参阅《Go pprof 迎来重大革新:v2 提案详解,告别默认注册,拥抱飞行记录器》。

小结

Go 1.25 的发布,再次彰显了 Go 语言务实求进的核心哲学。它没有追求华而不实的语法糖,而是将精力聚焦于那些能为广大开发者带来“无形收益”的领域:更智能的运行时、更快的 GC、更可靠的编译器、更高效的工具链

这些看似底层的改进,正是 Go 作为一门“生产力语言”的价值所在。它让开发者可以专注于业务逻辑,而将复杂的系统优化和环境适配,放心地交给 Go 语言自身。

我鼓励大家尽快将 Go 1.25 应用到自己的项目中,亲自感受这些变化带来的提升。Go 的旅程,仍在继续,让我们共同期待它在未来创造更多的可能。

感谢阅读!

如果这篇文章让你对 Go 1.25 新特性有了新的认识,请帮忙 点赞分享,让更多朋友一起学习和进步!


你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

GCP大面积故障,Go语言是“元凶”还是“背锅侠”?

本文永久链接 – https://tonybai.com/2025/06/16/go-avoid-critical-incident

大家好,我是Tony Bai。

科技圈的每一次“风吹草动”,尤其是大型云服务的故障,总能引发我们技术人无数的讨论与反思。最近,一则关于“Google Cloud Platform (GCP) Service Control 在 2025 年 6 月发生重大故障”的消息,及其事后分析报告中直指的“null pointer crash loop”,在技术社区掀起了不小的波澜。

故障报告中还提到了几个雪上加霜的因素:没有特性标志 (Feature Flags) 进行高风险部署、缺乏优雅的错误处理(二进制文件直接崩溃而非优雅降级)、以及没有回退机制导致系统过载。

考虑到 Go 语言在 Google 内部(如 Kubernetes, Cloud Run 等)以及整个云原生领域的广泛应用,一个自然而然的疑问浮出水面:Go语言是否是这次 GCP 故障的“元凶”?或者说,Go 的某些特性,是否在某种程度上“助长”了这类问题的发生?反过来,Go 的设计又是否本可以帮助避免这样的灾难?

这这篇文章中,我们就结合社区的智慧,从Go语言特性和更广泛的软件工程实践角度,来剖析一下这类故障背后的深层原因。这不仅是对一个故障的假想复盘,更是对我们日常开发实践的一次警醒。

Go 语言特性:是“防火墙”还是“导火索”?

社区论坛上的讨论,首先就聚焦在了 Go 语言本身的一些特性上。

显式错误返回 (if err != nil):万无一失还是“防君子不防小人”?

有开发者认为,Go 标志性的显式错误返回设计(即函数返回 (value, error),调用者必须检查 err),本应是避免错误的有力武器。但也有观点指出,这种模式的“简洁性”(或者说,可以通过 _ 忽略错误的便利性)有时反而可能在项目压力大、追求快速上线时,被开发者有意或无意地跳过,导致潜在的错误处理缺失。比如常见的 value, _ := someFunction() 写法。

Go的显式错误返回,确实为构建健壮软件提供了坚实的基础。它将错误视为一等公民,迫使开发者直面错误处理。但语言提供的机制,终究不能替代开发者的责任心和良好的编码习惯。正如有些开发者提到的,golangci-lint 这样的静态检查工具可以有效地发现未检查的错误,但这需要团队将其融入开发流程并严格执行。**语言设计提供了“防火墙”,但工程师的素养和流程的完备性,才是决定防火墙是否真正起作用的关键。

Nil Pointer Panic:Go 也难逃的“魔爪”?

针对报告中提到的“null pointer crash loop”,许多评论者指出,nil 指针 panic 在 Go 中也并非罕见。Go 语言本身允许指针存在,也允许指针为 nil,并且不像 Rust 的 Option/Result 类型或 C# 的可空引用类型那样,在语言层面强制开发者处理潜在的 nil 情况。

的确,Go 语言的设计哲学是简洁,它相信开发者有能力正确处理指针。避免 nil panic 的核心在于良好的编码实践:防御性编程(在使用指针前进行检查)、最小化指针使用(Go 鼓励值传递,许多场景可以完全避免指针)、以及充分的测试(特别是边界条件和异常路径)。虽然 Go 没有语言层面的强制 nil 检查,但其简洁性也使得这类检查的成本相对较低。

panic/recover 机制:救命稻草还是饮鸩止渴?

有开发者分享经验,倾向于用 panic/recover 包裹所有核心逻辑,试图捕获所有潜在的运行时崩溃。但针对像故障中提到的 Service Control 这样的有状态、高关键性的系统,这种做法也引发了质疑:recover 后的程序状态是否真的可靠?强行“续命”一个可能已处于不一致状态的进程,是否比让它快速失败并由外部监控系统(如 Kubernetes)重启更安全?关于这个问题,我曾在《“这代码迟早出事!”——复盘线上问题:六个让你头痛的Go编码坏味道》一文中也讨论过。

panic/recover 在 Go 中有其特定的适用场景,例如在库的边界将内部的 panic 转换为 error 返回给调用者,或者处理真正意外且难以通过常规错误处理覆盖的严重问题。但对于关键业务服务,尤其是有状态的服务,“fail fast” 依然是目前社区认为的更可取的设计。让服务在遇到严重内部错误时快速、干净地退出,依赖外部的健康检查和自动重启机制来恢复服务,往往比试图在不确定的状态下继续运行更稳妥。

这样来看,Go 语言的设计,如显式错误处理,确实为构建可靠系统提供了工具。但它并不提供“银弹”,也不能完全消除诸如 nil 指针解引用这类逻辑错误的可能性。语言特性是基础,但绝非全部。

超越语言:流程、测试与工程文化的“灵魂拷问”

在针对该故障的讨论中,一个压倒性的共识是:这类大型系统故障,往往更多是软件工程流程、测试策略和工程文化上的问题,而非单一语言设计所能左右。

“100% 测试覆盖率”的迷思与测试策略的缺位

有开发者提出“你可以覆盖 100% 的代码行,但你永远无法覆盖 100% 的输入和状态组合。” 这句话一针见血。过度迷信行覆盖率,而忽略了测试的深度和广度,是许多团队的通病。

那么真正有效的测试策略应该是什么呢?显然单一的测试策略是无法保证程序上线后的质量的。下面是几种常见的测试策略:

  • 单元测试 (Unit Testing): 验证开发者对代码单元在预期输入下的行为。
  • 模糊测试 (Fuzz Testing): 通过自动生成大量随机或变异输入,探索代码的边缘情况和未知缺陷。Go 1.18 已将 Fuzz Testing 内置到标准工具链中,这是一个强大的武器。
  • 集成测试 (Integration Testing): 验证模块间的交互。
  • 端到端测试 (End-to-End Testing): 模拟真实用户场景。
  • 生产测试/灰度发布 (Staged Rollouts / Canary Releases): 在真实生产环境中,小范围、逐步地验证变更的可靠性,这是大型系统发布的“金丝雀”。

这些策略显而易见,但又有多少团队能真正全面的做到呢?

特性标志 (Feature Flags):高风险变更的“安全阀”

故障报告中提到了“没有特性标志进行风险部署”,这几乎是大型系统发布的“大忌”。特性标志允许团队在不重新部署代码的情况下,动态地开启或关闭某项功能,从而:

  • 安全地进行 A/B 测试。
  • 逐步向用户灰度上线新功能,控制风险。
  • 在出现问题时,能够快速关闭故障功能,实现秒级“回滚”(功能层面)。

缺乏特性标志,意味着任何高风险的变更都像是在“裸奔”。

优雅降级与回滚预案:Plan B 的重要性

系统出错在所难免,关键在于出错后如何表现。故障报告中“二进制崩溃而非优雅降级”以及“没有随机回退导致过载”,都指向了系统鲁棒性的缺失。

  • 优雅降级: 当核心服务出现问题时,非关键功能是否可以降级服务,保证核心可用性?例如,推荐系统不可用时,是否可以展示默认热门内容,而不是整个页面崩溃?
  • 回滚计划: 任何部署都应该有明确、经过演练的回滚计划。出现问题时,能否快速、安全地回退到上一个稳定版本?

代码审查、自动化工具与工程文化

  • 严格的代码审查: 是发现逻辑错误、不规范写法(如忽略错误、滥用指针)的重要手段。
  • 静态分析与 Linter:golangci-lint 等工具可以自动化地检查出大量潜在问题,包括未处理的错误、不安全的并发操作等。但正如有些开发者在评论中所言,“linters can be disabled”,关键还是在于流程的执行。
  • 警惕“Vibe Coding”:有开发者犀利地指出“Garbage in, garbage out”。如果团队强依赖AI的“氛围”编码,而缺乏对生成代码的审查,那么无论用什么语言,都可能埋下隐患。
  • 重视流程而非迷信工具:许多评论都强调,即使有再好的语言特性或工具,如果缺乏健全的开发、测试、部署流程,以及对质量负责的工程文化,故障依然难以避免。

AI 辅助编程:是“帮手”还是新的“风险源”?

一个有趣的衍生讨论是关于 AI 辅助编程(如 GitHub Copilot、Google Gemini Code Assist)在其中的角色。

有开发者提到,Google 内部已有大量代码由 Gemini 生成。也有人分享使用 AI 辅助编程的体验,认为其在作为“结对编程伙伴”或“辅助搜索”时有价值,但完全自动生成的代码质量参差不齐,有时甚至会引入“幻觉”和新的 bug。

AI 辅助编程无疑是未来的趋势,它有可能提高开发效率,辅助开发者处理重复性工作。但目前来看,AI 生成的代码更需要、而不是更不需要人类的严格审查和充分测试。将 AI 视为一个能提供建议、加速编码的助手是合适的,但如果过度依赖,甚至将其生成的代码不经审视直接合入生产,那无异于引入了新的、更不可控的风险源。特别是在错误处理、并发安全、边界条件这些需要深度思考的领域,AI至少目前还难以完全替代经验丰富的工程师,尤其是一些mission critical的系统中。不要被那些用AI生成一个简单工具站的“AI战果”所迷惑。

小节:语言是利器,工程实践才是灵魂

回到最初的问题:GCP Service Control 的这次故障,Go 语言是“元凶”还是“背锅侠”?

从 社区的讨论和我们的分析来看,将板子完全打在 Go 语言身上,显然是有失公允的。Go 语言的设计,如其显式错误处理、简洁性带来的高可读性、以及强大的并发能力,都为构建健壮、高效的系统提供了良好的基础。

然而,语言终究只是工具,它不能替代健全的软件工程流程和严谨的工程文化。 此次 GCP 故障所暴露出的问题——无论是可能的 nil 指针解引用,还是更宏观的缺乏特性标志、部署策略失当、错误处理不优雅——更多地指向了在测试、部署、风险控制、质量保障等一系列工程实践环节可能存在的缺失。

对于我们 Go 开发者而言,这次事件给我们带来的启示应该是:

  • 充分利用 Go 的优势: 写出符合 Go 惯例的、清晰的错误处理逻辑;审慎使用指针,做好 nil 检查;发挥 Go 并发模型的威力。
  • 拥抱并严格执行工程最佳实践: 将单元测试、集成测试、模糊测试落到实处;在重要变更上线时,务必使用特性标志和灰度发布策略;建立严格的代码审查机制;利用好静态分析工具。
  • 对 AI 保持理性: 善用 AI 辅助工具提高效率,但绝不能放松对代码质量的把控和人工审查的力度。

最终,构建一个真正高可用、高可靠的大型系统,依赖的绝不仅仅是选择一门“好”的语言,更在于整个团队对卓越工程实践的持续追求和严格执行。

你对这次讨论有什么看法?或者在你的 Go 项目中,是如何保障系统稳定性的?欢迎在评论区留下你的宝贵经验!


精进有道,更上层楼

极客时间《Go语言进阶课》上架刚好一个月,受到了各位读者的热烈欢迎和反馈。在这里感谢大家的支持。目前我们已经完成了课程模块一『语法强化篇』的 13 讲,为你系统突破 Go 语言的语法认知瓶颈,打下坚实基础。

现在,我们即将进入模块二『设计先行篇』,这不仅包括 API 设计,更涵盖了项目布局、包设计、并发设计、接口设计、错误处理设计等构建高质>量 Go 代码的关键要素。

这门进阶课程,是我多年 Go 实战经验和深度思考的结晶,旨在帮助你突破瓶颈,从“会用 Go”迈向“精通 Go”,真正驾驭 Go 语言,编写出更优雅、
更高效、更可靠的生产级代码!

扫描下方二维码,立即开启你的 Go 语言进阶之旅!

感谢阅读!

如果这篇文章让你对Go语言有了新的认识,请帮忙转发,让更多朋友一起学习和进步!


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言进阶课 AI原生开发工作流实战 从 0 开始构建 Agent Harness Go语言精进之路1 Go语言精进之路2 Go语言第一课 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com
这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats