Go 学习路线 MOC
从零基础到可独立交付 Go 服务的系统学习路线:七个阶段、依赖关系、逐篇目录与 0 到 1 完成标准。
[!info] related notes
- 所属总入口:Go MOC
- 对象介绍:Go
- 设计哲学:Go 的工程约束与设计哲学
- 阶段地图:Go 语言基础 MOC · Go 类型系统与抽象 MOC · Go 工具链与工程结构 MOC · Go 并发 MOC · Go 测试与质量 MOC · Go 标准库与应用 MOC · Go 高级运行时与抽象 MOC · Go Web 与后端 MOC · Go 数据访问 MOC · Go 服务工程化 MOC · Go 性能与诊断 MOC
Go 学习路线 MOC
这个概念为什么出现
没有依赖顺序的笔记库会制造假进度:会写 go 却不懂值语义,会调框架却不会关 channel。本路线把「从零到可交付服务」拆成七个阶段,作为学习与复习的主路径。
[!abstract] 路线结论 先建立“值如何流动”的心智,再学习“能力如何抽象”,随后进入错误、资源与包边界;并发必须连同同步、取消和测试一起学,最后用标准库与工程化能力完成真实程序,需要时再下沉到运行时。
学 Go 最怕把“语法会写”和“语言模型真正建立起来”混为一谈。整条路线围绕五个问题展开:
- 这门语言默认按值还是按引用思考
- 为什么它强调组合而不是类继承
- 为什么错误处理没有异常主导
- 为什么并发是语言主线,而不是框架附加能力
- 为什么工具链和代码组织是语言体验的一部分
如何使用这份路线
| 场景 | 做法 |
|---|---|
| 从零开始 | 按阶段编号顺序读;每篇先跑最小示例,再看规则与边界 |
| 已有 JS/TS 经验 | 可加速阶段一语法,但不要跳过值语义、接口、错误与并发 |
| 只抓主干 | 看文末「8 个主干主题」和「0 到 1 完成标准」 |
| 查某一主题 | 先定位阶段表中的文件名,再打开对应笔记;细节在原子笔记,不在本页展开 |
难度标记
- L1 入门 · L2 基础巩固 · L3 核心心智 · L4 工程/并发 · L5 运行时与进阶
- 源码:否 / 建议 / 需要 —— 是否值得对照规范、标准库或 Runtime 源码加深
依赖关系总图
flowchart TD
S1["阶段一 基本认知"] --> S2["阶段二 数据与类型模型"]
S2 --> S3["阶段三 错误、资源与抽象"]
S3 --> S4["阶段四 并发编程"]
S3 --> S5["阶段五 工程化开发"]
S4 --> S5
S5 --> S6["阶段六 底层与进阶"]
S5 --> S7["阶段七 真实项目能力"]
S4 --> S7
S6 --> S7
箭头表示认知依赖,不表示前一阶段必须全部背完。每个阶段都应运行最小例子并观察结果。
阶段与子 MOC 对照
| 阶段 | 目标 | 主要子 MOC |
|---|---|---|
| 一 | 建立 Go 的默认思维方式与程序骨架 | Go 语言基础 MOC · Go 工具链与工程结构 MOC |
| 二 | 理解数据、值语义、方法与接口 | Go 语言基础 MOC · Go 类型系统与抽象 MOC |
| 三 | 错误、defer、I/O 抽象与 context | Go 类型系统与抽象 MOC · Go 标准库与应用 MOC |
| 四 | 任务生命周期、通信、同步与内存可见性 | Go 并发 MOC |
| 五 | 模块、测试、质量工具与项目结构 | Go 工具链与工程结构 MOC · Go 测试与质量 MOC · Go 服务工程化 MOC |
| 六 | 编译、逃逸、调度、GC、泛型与反射 | Go 高级运行时与抽象 MOC · Go 性能与诊断 MOC |
| 七 | CLI、HTTP、数据访问与服务交付 | Go 标准库与应用 MOC · Go Web 与后端 MOC · Go 数据访问 MOC · Go 服务工程化 MOC |
阶段一:建立 Go 的基本认知
目标:能解释 Go 解决什么问题;能安装并运行程序;理解 package、变量、类型、控制流与函数的基本规则。
完成标准:
- 能从零写出
package main并用go run/go test验证 - 能说明零值、短变量声明与类型推断的边界
- 能读懂函数多返回值与基础
defer
| 序号 | 笔记 | 核心问题 | 难度 | 源码 |
|---|---|---|---|---|
| 1.1 | Go 的工程约束与设计哲学 | Go 为什么这样设计 | L2 | 建议 |
| 1.2 | 安装 Go 并运行第一个程序 | 如何安装并跑通第一个程序 | L1 | 否 |
| 1.3 | Go 程序结构 | package、import、声明与 main | L1 | 否 |
| 1.4 | Go 的变量、常量与零值 | 变量如何声明,零值解决什么 | L1 | 否 |
| 1.5 | Go 基本类型 | 基本类型如何表示值 | L1 | 否 |
| 1.6 | Go 类型别名与类型定义 | 别名与定义类型差在哪 | L2 | 否 |
| 1.7 | Go 类型转换与可赋值性 | 何时可赋值、何时必须转换 | L2 | 否 |
| 1.8 | Go 运算符与表达式 | 运算符与表达式求值规则 | L1 | 否 |
| 1.9 | Go 流程控制 | if/for/switch 如何组织控制流 | L1 | 否 |
| 1.10 | Go range 语义 | range 复制什么、迭代变量语义 | L2 | 建议 |
| 1.11 | Go 函数 | 多返回值、命名返回值、可变参数 | L2 | 否 |
| 1.12 | Go 闭包 | 闭包捕获的是变量还是值 | L3 | 建议 |
| 1.13 | Go iota 与枚举 | iota 如何表达相关常量组 | L2 | 否 |
| 1.14 | 使用 Scoop 安装并配置 Go 环境 | Windows 上如何稳定配置 Go | L1 | 否 |
阶段二:理解 Go 的数据模型
目标:建立数组/slice/map/string/struct/指针的准确模型;分清值复制与共享底层数据;理解方法、方法集、接口与 nil。
完成标准:
- 能解释 slice 的
len/cap/底层数组与append - 能根据方法集判断值或指针是否满足接口
- 能区分“接口值为 nil”和“动态值为 typed nil”
| 序号 | 笔记 | 核心问题 | 难度 | 源码 |
|---|---|---|---|---|
| 2.1 | Go 数组 | 数组长度为何属于类型 | L2 | 否 |
| 2.2 | Go Slice | slice 描述符如何观察底层数组 | L3 | 建议 |
| 2.3 | Go Map | map 的键约束、零值与并发边界 | L3 | 建议 |
| 2.4 | Go 字符串 | string、byte、rune 与不可变性 | L2 | 否 |
| 2.5 | Go 指针 | 指针何时用于共享与修改 | L3 | 否 |
| 2.6 | Go 的 struct 与方法 | struct 如何聚合数据并附着方法 | L2 | 否 |
| 2.7 | Go 中的值、指针与引用语义 | 传参都是值复制与共享如何共存 | L3 | 建议 |
| 2.8 | Go 方法集与接收者 | 值/指针接收者如何决定方法集 | L3 | 建议 |
| 2.9 | Go 接口 | 接口如何由方法集隐式满足 | L3 | 建议 |
| 2.10 | Go 接口值与 nil | 接口值由什么组成,nil 有哪些形态 | L4 | 建议 |
| 2.11 | Go 类型断言与 type switch | 类型断言与 type switch 的安全用法 | L3 | 否 |
| 2.12 | Go 嵌入与组合 | embedding 如何实现组合优于继承 | L3 | 否 |
| 2.13 | Go struct 标签 | struct tag 如何服务编解码与校验 | L2 | 否 |
| 2.14 | Go 中 nil 的多种形态 | nil 在 pointer/slice/map/channel/func/interface 中的差异 | L4 | 建议 |
阶段三:错误、资源与抽象
目标:把错误当作常规控制流;掌握 defer/panic/recover 边界;用小接口做 I/O 组合;用 context 传递截止与取消。
完成标准:
- 能为错误增加上下文并保留错误身份(
errors.Is/As) - 知道 panic 不该替代普通错误返回
- 能用
io.Reader/Writer组合数据流 - 能沿调用链传递并响应
context取消
| 序号 | 笔记 | 核心问题 | 难度 | 源码 |
|---|---|---|---|---|
| 3.1 | Go 错误处理 | 为什么 error 是值而不是异常主路径 | L3 | 否 |
| 3.2 | Go 错误包装与链式错误 | 如何包装错误并保留比较身份 | L3 | 否 |
| 3.3 | Go 的 defer、panic 与 recover | defer 求值时机与 panic 边界 | L3 | 建议 |
| 3.4 | Go 资源释放与清理 | 文件、连接、锁如何与 defer 配对释放 | L3 | 否 |
| 3.5 | Go io.Reader 与 io.Writer | 小接口如何统一文件/网络/内存流 | L3 | 建议 |
| 3.6 | Go context | 取消、超时与请求范围值如何传播 | L4 | 建议 |
| 3.7 | Go 组合式接口设计 | 标准库如何用组合接口设计 API | L3 | 建议 |
阶段四:并发编程
目标:建立任务启动、通信、退出、取消与同步的完整心智;理解 happens-before,而不是只背 API。
完成标准:
- 能说明谁启动 goroutine、谁负责退出与关闭 channel
- 能在共享状态与消息传递之间做合理选择
- 能用
go test -race验证测试覆盖到的竞态 - 能解释 context 取消如何结束下游工作
| 序号 | 笔记 | 核心问题 | 难度 | 源码 |
|---|---|---|---|---|
| 4.1 | Go goroutine | goroutine 与 OS 线程有何不同,如何退出 | L3 | 建议 |
| 4.2 | Go channel | 发送/接收/缓冲/关闭/nil channel 语义 | L4 | 建议 |
| 4.3 | Go select | 如何在多个通信事件间等待 | L4 | 否 |
| 4.4 | Go channel 所有权与关闭 | 谁关闭 channel,关闭后如何排空 | L4 | 否 |
| 4.5 | Go sync 包 | Mutex/WaitGroup/Once/Cond 适用边界 | L4 | 否 |
| 4.6 | goroutine、channel、context 与取消传播的关系 | 任务、通信与取消如何组成生命周期 | L4 | 否 |
| 4.7 | Go happens-before 与同步保证 | 何时一次写入对其他 goroutine 可见 | L5 | 需要 |
| 4.8 | Go 调度器与 G-M-P 模型 | G-M-P 调度模型解决什么问题 | L5 | 需要 |
| 4.9 | Go 内存模型 | Go 内存模型保证什么、不保证什么 | L5 | 需要 |
| 4.10 | Go 并发模式概览 | pipeline、fan-in/out、超时与泄漏防护 | L4 | 否 |
| 4.11 | Go worker pool | 如何限制并发并回收 worker | L4 | 否 |
| 4.12 | Go pipeline 模式 | 如何用 stage 与 channel 组织流水线 | L4 | 否 |
阶段五:工程化开发
目标:把单文件练习组织成可构建、可测试、可发布的模块与服务骨架。
完成标准:
- 理解 package 可见性与 module 版本边界
- 会写表驱动测试,并用 race/fuzz/benchmark 形成反馈
- 能说明项目分层与配置、日志的基本位置
| 序号 | 笔记 | 核心问题 | 难度 | 源码 |
|---|---|---|---|---|
| 5.1 | Go 包、导入与可见性 | 包边界与导出规则如何约束设计 | L2 | 否 |
| 5.2 | Go Modules | 依赖、版本与校验和如何工作 | L2 | 否 |
| 5.3 | Go 构建与部署 | 构建、交叉编译与发布边界 | L3 | 否 |
| 5.4 | Go 项目结构与分层 | 目录与分层如何映射依赖方向 | L3 | 否 |
| 5.5 | Go internal 包 | internal 目录如何强制包边界 | L2 | 否 |
| 5.6 | Go 测试 | testing 包如何组织测试 | L2 | 否 |
| 5.7 | 在 Go 中编写表驱动测试与子测试 | 如何扩展用例并保持可读性 | L2 | 否 |
| 5.8 | Go benchmark | 如何控制变量并解读 benchmark | L3 | 否 |
| 5.9 | Go race detector | race detector 能抓什么、不能抓什么 | L3 | 建议 |
| 5.10 | Go 模糊测试 | 模糊测试如何发现边界错误 | L3 | 否 |
| 5.11 | Go 中的测试替身 | mock/stub/fake 在 Go 中如何克制使用 | L3 | 否 |
| 5.12 | Go 静态分析与代码检查 | gofmt / go vet / staticcheck 的分工 | L2 | 否 |
| 5.13 | Go 配置管理 | 配置如何注入且保持可测试 | L3 | 否 |
| 5.14 | Go 日志 | 日志字段、级别与请求关联 | L2 | 否 |
| 5.15 | Go API 设计与代码约定 | 导出 API、命名与错误策略约定 | L3 | 建议 |
| 5.16 | Go 手动依赖注入 | 不用容器时如何组装依赖 | L3 | 否 |
阶段六:底层与进阶
目标:在会写业务代码之后,补齐编译、分配、GC、调度、泛型与反射;明确区分语言规范与当前实现。
完成标准:
- 能区分规范保证与实现细节
- 能用
go build -gcflags="-m"观察逃逸,而不是凭感觉优化 - 知道泛型、反射、unsafe 的适用边界
| 序号 | 笔记 | 核心问题 | 难度 | 源码 |
|---|---|---|---|---|
| 6.1 | Go 编译流程概览 | 从源码到可执行文件经过哪些阶段 | L4 | 建议 |
| 6.2 | Go 逃逸分析 | 值何时逃逸到堆 | L5 | 需要 |
| 6.3 | Go 中的栈与堆 | 栈与堆在 Go 中如何协作 | L5 | 需要 |
| 6.4 | Go 内存分配器概览 | 分配器如何服务小对象与 span | L5 | 需要 |
| 6.5 | Go 垃圾回收 | GC 如何与写屏障、辅助标记协作 | L5 | 需要 |
| 6.6 | Go 接口的底层表示 | iface/eface 如何表示接口值 | L5 | 需要 |
| 6.7 | Go Slice 与 Map 的实现要点 | slice/map 运行时结构与复杂度 | L5 | 需要 |
| 6.8 | Go channel 的实现要点 | channel 的底层队列与 sudog | L5 | 需要 |
| 6.9 | Go 泛型 | 类型参数如何在保持简单的前提下抽象 | L4 | 建议 |
| 6.10 | Go 反射 | reflect 何时值得用、代价是什么 | L4 | 建议 |
| 6.11 | Go unsafe 包 | unsafe 破坏了哪些保证 | L5 | 需要 |
| 6.12 | Go cgo | cgo 的成本与部署边界 | L5 | 需要 |
| 6.13 | Go embed | embed 如何打包静态资源 | L2 | 否 |
| 6.14 | Go pprof | 如何用 pprof 定位 CPU/内存/阻塞 | L4 | 否 |
| 6.15 | Go 性能优化方法论 | 性能优化的测量与取舍流程 | L4 | 否 |
运行时结构体名(如 hchan、mcache、eface)是当前实现的教学模型,不是语言规范承诺。
阶段七:真实项目能力
目标:用标准库与克制的工程模式交付 CLI、HTTP 服务、数据访问与可运维部署。
完成标准:
- 能独立完成标准库 CLI 与 HTTP 服务
- 能设计路由、中间件、优雅关闭与健康检查
- 能接入 database/sql,并理解 ORM/缓存/迁移位置
- 能把日志、配置、限流、观测纳入服务骨架
| 序号 | 笔记 | 核心问题 | 难度 | 源码 |
|---|---|---|---|---|
| 7.1 | 使用 Go 标准库构建 CLI | 如何用标准库做可测试 CLI | L3 | 否 |
| 7.2 | 使用 Go 标准库构建 HTTP 服务 | 如何用 net/http 做可测试服务 | L3 | 否 |
| 7.3 | Go net/http | Handler、Server 与请求生命周期 | L3 | 建议 |
| 7.4 | Go 路由模式 | 路由如何组织且保持可测试 | L3 | 否 |
| 7.5 | Go HTTP 中间件 | 中间件如何组合横切能力 | L3 | 否 |
| 7.6 | Handler-Service-Repository 分层 | 请求层与领域/数据层如何分界 | L3 | 否 |
| 7.7 | Go JSON 与序列化 | JSON 标签、零值与解码边界 | L2 | 否 |
| 7.8 | Go 请求验证 | 请求验证放在哪一层 | L3 | 否 |
| 7.9 | Go 中的 database/sql | database/sql 的连接池与上下文 | L4 | 建议 |
| 7.10 | GORM | ORM 的收益与泄漏边界 | L3 | 否 |
| 7.11 | 数据库迁移设计(golang-migrate) | 迁移如何版本化 | L3 | 否 |
| 7.12 | Go 认证与 JWT | 认证如何接入 HTTP 边界 | L3 | 否 |
| 7.13 | Go 服务优雅关闭 | 如何排空请求并关闭依赖 | L4 | 否 |
| 7.14 | 健康检查端点设计 | 健康检查检查什么、不检查什么 | L2 | 否 |
| 7.15 | Go 可观测性 | 指标、日志、追踪如何联动 | L4 | 否 |
| 7.16 | Go 缓存策略 | 缓存一致性与穿透/击穿边界 | L4 | 否 |
| 7.17 | Go 限流 | 限流放在网关还是服务内 | L3 | 否 |
| 7.18 | Go Docker 部署 | 容器化 Go 服务的构建与运行 | L3 | 否 |
| 7.19 | Go gRPC | 何时用 gRPC 而不是 JSON HTTP | L4 | 否 |
| 7.20 | SSE 代理模式 | 如何透传 SSE 流 | L3 | 否 |
| 7.21 | Redis 在 Go 后端的应用 | Redis 在 Go 中的连接与缓存实践 | L3 | 否 |
| 7.22 | Gin HTTP 框架 | 何时用 Gin 而不是只用 net/http | L3 | 否 |
| 7.23 | JWT 认证中间件设计 | JWT 中间件如何设计与吊销 | L3 | 否 |
如果只抓主干,优先这 8 个主题
- Go 的变量、常量与零值
- Go Slice
- Go 指针 / Go 中的值、指针与引用语义
- Go 的 struct 与方法
- Go 接口 / Go 方法集与接收者
- Go 错误处理
- Go goroutine
- Go channel
一条实用学习原则
不要把 Go 学成“背库函数和框架路由”。先把 值语义、接口、错误处理、并发 四条主线学稳,再进入测试、Web、数据库和部署。
0 到 1 的完成标准
- 能解释 slice 复制后为什么仍可能共享数据
- 能根据方法集判断一个值或指针是否满足接口
- 能为错误增加上下文,同时保留错误身份
- 能说明 goroutine 如何退出,谁负责关闭 channel,取消如何传播
- 能写表驱动测试并用
go test -race ./...验证 - 能完成 使用 Go 标准库构建 CLI 和 使用 Go 标准库构建 HTTP 服务
- 能区分“语言规范保证”和“当前 Runtime 实现细节”
认知跳跃提醒(按路线可规避)
- 未理解值复制就学接口方法集 → 先 2.7 / 2.8
- 未理解错误与 context 就上并发取消 → 先 3.1 / 3.6
- 未理解 channel 所有权就写 worker pool → 先 4.2 / 4.4 / 4.6
- 未测量就谈逃逸与 GC 优化 → 先 5.8 / 6.14,再 6.2 / 6.5
相关入口
- 总地图:Go MOC
- 对象定位:Go
- 设计哲学:Go 的工程约束与设计哲学
- 安装验收:安装 Go 并运行第一个程序 · 使用 Scoop 安装并配置 Go 环境
自测题
- 为什么并发阶段要放在错误与 context 之后?
- 阶段六运行时笔记与阶段二接口笔记应如何配合阅读?
- 若时间有限,优先抓哪 8 个主干主题?
答案
- 取消、错误传播与生命周期是并发正确性的前置,不是附加。
- 先掌握语言层接口/方法集语义,再读实现表示;实现细节不回写为规范。
- 零值、slice、指针/值语义、struct 与方法、接口/方法集、错误处理、goroutine、channel。
BodySense 项目实战入口(把 Go 基础落到真实代码)
[!info] BodySense 的 API 服务用 Go + Gin + GORM 实现。下面的代码是你把「context / 错误 / 接口 / 并发 / 切片」知识落到实处的样本。阅读顺序:先读代码注释 → 翻对应笔记 → 回看代码。
| 想练的知识点 | 去看的代码(仓库内相对路径) | 对应笔记 |
|---|---|---|
| context 传播、错误包装、接口抽象、Mutex 批处理 | apps/api/internal/service/runtime_event_service.go | Go context · Go 错误包装 · Go 接口 · Go sync 包 |
- 流式链路教材(仓库内):
docs/learning/06-javascript-typescript-streaming.md - 由易到难的实战任务(仓库内):
docs/learning/05-practice-tasks.md - 跨语言对照(同一契约的 Python/TS 实现):Python 学习路线 · TypeScript 学习路线 · BodySense MOC