跳到正文
知识库 / Go 博客教程返回主站 ↗

长期学习 · Go 后端实战 · 10 课

Go 初学者实战教程:从零手写一个博客系统(10 课)

面向 完全没写过 Go、但有其他语言基础 的人。目标不是抄一份能跑的代码,而是把博客系统里每一个关键函数为什么这么写签名、参数怎么来、返回值怎么处理、哪里最容易错讲透,代码留出 TODO 让你自己补完。

教程日期:实测环境:Go 1.25.0 · MySQL 8.4.5 · Redis代码全部在真项目里 go build / go vet / go test 跑过

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) 标记的地方是留给你写的,正文会说明期望行为和验收方式。

每课固定结构

  1. 本课目标 — 学完能独立写出什么
  2. 前置检查 — 一条命令确认上一课的成果还能跑
  3. 核心概念 — 尽量短,只讲会影响你写代码的部分
  4. 函数逐个精讲 — 签名 → 完整代码 + 逐行注解 → 易错点
  5. 跑起来验证curl / go test / SQL 实测命令
  6. TODO 练习 — 3–5 个,带验收标准
  7. 自检清单 — 打勾确认掌握

怎么用这套教程

# 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 代码,全部由你自己敲出来。