长期学习 · Go 后端实战 · 10 课
Go 初学者实战教程:从零手写一个博客系统(10 课)
面向 完全没写过 Go、但有其他语言基础 的人。目标不是抄一份能跑的代码,而是把博客系统里每一个关键函数为什么这么写签名、参数怎么来、返回值怎么处理、哪里最容易错讲透,代码留出 TODO 让你自己补完。
10 课 · 约 1.1 万行每课 800–1800 行关键函数写完整,其余留 TODO 骨架。
1–6 课只用标准库7–10 课才上框架先懂本质,再看 Gin / GORM / Redis 替你做了什么。
手敲,不要复制粘贴go build 报的每个错都在教你从第 1 课开始 →
这套教程的设计原则
| 原则 | 说明 |
|---|---|
| 标准库打底 | 第 1–6 课只用 net/http + database/sql,把路由、中间件、SQL 映射的本质讲清楚 |
| 框架后置 | 第 7–10 课才引入 Gin / GORM / Redis,让你看懂框架帮你做了什么、代价是什么 |
| 关键函数精讲 | 每课挑 3–6 个核心函数写完整代码 + 逐行中文注解,其余留骨架 |
| 留白练习 | 每课末尾 3–5 个 TODO,答案思路在下一课开头对照 |
| 可验证 | 每课都有「怎么确认我写对了」的 curl / go test / SQL 命令 |
课程地图
| 课 | 文件 | 主题 | 你会写出的东西 |
|---|---|---|---|
| 01 | 01-go-basics-and-scaffold.md | Go 语法地基 + 项目骨架 | go.mod、第一个 HTTP 服务、error 的正确姿势 |
| 02 | 02-net-http-and-routing.md | net/http 与路由 |
ServeMux 路径参数、JSON 编解码、Handler 接口 |
| 03 | 03-mysql-database-sql.md | MySQL 与 database/sql |
连接池、Scan、预处理、事务、NULL 处理 |
| 04 | 04-layered-crud.md | 分层架构 + 文章 CRUD | repository / service / handler 三层与依赖注入 |
| 05 | 05-middleware-and-errors.md | 中间件与统一错误 | 日志/恢复/CORS 中间件链、context 传值 |
| 06 | 06-auth-jwt.md | 用户与认证 | bcrypt 存密码、JWT 签发校验、鉴权中间件 |
| 07 | 07-gin-refactor.md | 迁移到 Gin | 路由组、参数绑定校验、Gin 中间件对照 |
| 08 | 08-gorm.md | GORM | 模型/迁移/关联(标签多对多、评论)/预加载/事务 |
| 09 | 09-redis-cache.md | Redis 缓存 | 详情缓存、穿透击穿雪崩、热榜 ZSet、限流、分布式锁 |
| 10 | 10-production.md | 工程化上线 | 配置、slog 日志、优雅关闭、httptest 测试、并发 |
全套约 1.1 万行,每课 800–1800 行。教程里的代码不是凭记忆写的——每一课都在真项目里 go build / go vet / go test 跑过,连了本机的 MySQL 和 Redis,文中贴的报错文本、SQL 日志、curl 响应都是实测粘贴的输出。
统一约定(所有课共用,先看这里)
模块与目录
mkdir -p ~/go-blog && cd ~/go-blog
go mod init blog # 模块名固定为 blog,所有 import 路径都是 blog/xxx
第 10 课结束时的完整目录(前几课只会用到其中一部分,不要一开始就全建出来):
go-blog/
├── go.mod
├── cmd/
│ └── server/
│ └── main.go # 唯一入口:装配依赖 + 启动服务
├── internal/ # internal 下的包只能被本模块 import,天然防外部误用
│ ├── config/
│ │ └── config.go # 环境变量 → Config 结构体
│ ├── model/
│ │ ├── post.go # 领域结构体,不含任何 SQL/HTTP
│ │ └── user.go
│ ├── repository/
│ │ ├── post_repo.go # 只跟数据库说话
│ │ └── user_repo.go
│ ├── service/
│ │ └── post_service.go # 业务规则,不知道 HTTP 存在
│ ├── handler/
│ │ ├── post_handler.go # 只做 HTTP ↔ service 的翻译
│ │ └── response.go # 统一 JSON 响应
│ ├── middleware/
│ │ ├── logger.go
│ │ ├── recover.go
│ │ └── auth.go
│ ├── cache/ # 第 9 课
│ │ ├── redis.go # 连接池 + Ping
│ │ ├── post_cache.go # Cache-Aside + singleflight
│ │ ├── lock.go # SetNX + Lua 原子释放
│ │ └── blacklist.go # JWT 登出黑名单
│ ├── logging/ # 第 10 课:slog 封装
│ │ └── logger.go
│ └── job/ # 第 10 课:阅读量定时回写
│ └── viewsync.go
├── migrations/
│ └── schema.sql # 建表语句(本目录已提供)
└── .env # 本地配置,不提交
为什么用 internal/:Go 的硬规则——internal/ 目录下的包只允许被它的父目录子树 import。放这里能保证别人 go get 你的模块时碰不到内部实现,是 Go 官方推荐的项目布局。
本机服务(已验证可用)
| 服务 | 版本 | 连接信息 |
|---|---|---|
| Go | go1.25.0 darwin/arm64 | — |
| MySQL | 8.4.5 | 127.0.0.1:3306,用户 root,密码 123456 |
| Redis | 已启动 | 127.0.0.1:6379,无密码 |
第三方依赖(全部实测于 Go 1.25.0)
按课程顺序引入,不要一开始就全装:
| 课 | 命令 | 实测版本 |
|---|---|---|
| 03 | go get github.com/go-sql-driver/mysql |
v1.10.1 |
| 06 | go get golang.org/x/[email protected] |
必须钉版本,见下 |
| 06 | go get github.com/golang-jwt/jwt/v5 |
v5.3.1 |
| 07 | go get github.com/gin-gonic/gin |
v1.12.0(带 validator/v10 v10.30.1) |
| 08 | go get gorm.io/gorm gorm.io/driver/mysql |
v1.31.2 / v1.6.0 |
| 09 | go get github.com/redis/go-redis/v9 |
v9.22.0 |
| 09 | go get golang.org/x/sync |
v0.22.0(singleflight、errgroup) |
golang.org/x/crypto 必须钉 @v0.55.0:最新的 v0.56.0 声明 go >= 1.26.0,在 Go 1.25 上 go get 会静默触发 switching to go1.26.8,下载几百 MB 工具链。v0.55.0 是 Go 1.25 能直接编译的最新版。
三个「网上老教程满天飞、但已经错了」的 import 路径,别抄错:JWT 是 github.com/golang-jwt/jwt/v5(不是废弃且有 CVE 的 dgrijalva/jwt-go);GORM 是 gorm.io/gorm(不是停止维护的 github.com/jinzhu/gorm);Redis 是 github.com/redis/go-redis/v9(不是 go-redis/redis)。
DSN(第 3 课开始用):
root:123456@tcp(127.0.0.1:3306)/blog_dev?charset=utf8mb4&parseTime=True&loc=Local
parseTime=True 必须加,否则 MySQL 的 DATETIME 扫不进 Go 的 time.Time,会报 unsupported Scan。这是新手第一个必踩的坑。
建库建表
mysql -uroot -p123456 < migrations/schema.sql
表结构见本目录 schema.sql(第 3 课会逐字段解释为什么这么设计)。schema.sql 可在本页下载。
代码风格约定
- 注释一律中文,函数上方写「做什么 + 为什么」,关键行写行内注释。
- 错误一律
if err != nil { return ... }立即处理,不吞错。 - 每课代码块顶部标注它属于哪个文件,例如
// internal/repository/post_repo.go。 TODO(练习N)标记的地方是留给你写的,正文会说明期望行为和验收方式。
每课固定结构
- 本课目标 — 学完能独立写出什么
- 前置检查 — 一条命令确认上一课的成果还能跑
- 核心概念 — 尽量短,只讲会影响你写代码的部分
- 函数逐个精讲 — 签名 → 完整代码 + 逐行注解 → 易错点
- 跑起来验证 —
curl/go test/ SQL 实测命令 - TODO 练习 — 3–5 个,带验收标准
- 自检清单 — 打勾确认掌握
怎么用这套教程
# 1. 建项目(只做一次)
mkdir -p ~/go-blog && cd ~/go-blog && go mod init blog
# 2. 建库建表(只做一次)
mysql -uroot -p123456 < migrations/schema.sql
# 3. 从第 1 课开始,边读边在 ~/go-blog 里手敲
不要复制粘贴代码块。手敲是这套教程唯一有效的用法——go build 报的每一个错都是在教你 Go 的类型系统。
学完后的产物
一个能跑的博客后端:用户注册登录(JWT)、文章增删改查、标签多对多、评论、Redis 缓存热点文章、限流、结构化日志、优雅关闭、带单元测试。约 1500 行 Go 代码,全部由你自己敲出来。