只用标准库做一个服务器
目标
仅使用标准库构建 HTTP 服务器,观察 Go 会强制开发者做什么。在选择框架之前,先确认 net/http 已经提供了哪些能力。
为什么重要
初次使用 Go 的人常说 if err != nil 很烦琐。但这种篇幅不是成本,而是显式性的代价:类型中明确写出了哪一行可能失败,若要忽略错误,也必须明确写出忽略行为。
本实验会让你亲手体会这种代价及其收益,还会直接制造 Go 中真正容易引发事故的问题(goroutine 泄漏、取消未传播)。
环境
由于没有网络,go get 无法运行。本实验只使用标准库,因此不受影响——这反而正是本课程想说明的重点。
go mod init <이름> 모듈 시작
go build -o srv . 빌드
go vet ./... 흔한 실수 찾기
gofmt -l . 포맷이 어긋난 파일 나열
步骤
- 初始化模块 →
/root/work/gosrv/go.mod /health服务器- 将错误作为值返回(用
%w包装) errors.Is/As→/root/work/gosrv/04-errors.txt- JSON 响应 →
/root/work/gosrv/05-json.txt - 通过 context 传播取消 →
/root/work/gosrv/06-ctx.txt - goroutine 泄漏 →
/root/work/gosrv/07-leak.txt go vet/gofmt→/root/work/gosrv/08-vet.txt
参考
- 启动服务器后,请用
kill %1将其清理。若 8080 端口仍被占用,下一个构建结果就无法启动。 - 错误消息以小写字母开头,末尾不加句号,因为它们会被嵌入其他消息中。
创建模块
初始化模块 → /root/work/gosrv/go.mod
执行 mkdir -p /root/work/gosrv && cd /root/work/gosrv && go mod init labhub.local/gosrv。由于没有网络,无法下载外部包;本实验只使用标准库。生成 go.mod 即可。
一个 HTTP 服务器
/health 服务器
在 main.go 中使用 net/http 创建 /health,并监听 8080 端口。用 go build -o srv . 构建,再用 ./srv & 启动,然后执行 curl -s localhost:8080/health 验证。无需框架,标准库就已提供这些能力。
将错误作为值返回
将错误作为值返回(用 %w 包装)
创建 parseAge(s string) (int, error),当输入不是数字时返回错误。包装时请像 fmt.Errorf("나이 파싱(%s): %w", s, err) 一样使用 %w。若用 %v 包装,就会丢失原始错误,无法再通过 errors.Is 查询。
查询错误原因
errors.Is/As → /root/work/gosrv/04-errors.txt
加入使用 errors.Is 或 errors.As 判断被包装错误原因的代码,并将输出保存到 /root/work/gosrv/04-errors.txt。目的是亲眼确认可以从包装后的错误中取出原始错误。
返回 JSON
JSON 响应 → /root/work/gosrv/05-json.txt
使用 encoding/json 返回结构体。若不添加字段标签(json:"name"),字段名会保留大写形式,这是 Go 的 export 规则所致。将 curl -s localhost:8080/api | jq . 的结果保存到 /root/work/gosrv/05-json.txt。
传播取消信号
通过 context 传播取消 → /root/work/gosrv/06-ctx.txt
创建一个较慢的 handler,监听 r.Context() 的 Done(),使客户端断开时停止工作。用 curl --max-time 1 中断请求,查看服务器日志是否记录了取消。将输出保存到 /root/work/gosrv/06-ctx.txt。
观察 goroutine 泄漏
goroutine 泄漏 → /root/work/gosrv/07-leak.txt
故意创建一个会泄漏的 goroutine(向没有接收方的 channel 发送),在前后分别输出 runtime.NumGoroutine(),并保存到 /root/work/gosrv/07-leak.txt。数量增加后不再下降,就表示发生了泄漏。
先询问工具
go vet / gofmt → /root/work/gosrv/08-vet.txt
运行 go vet ./... 和 gofmt -l .,将结果保存到 /root/work/gosrv/08-vet.txt。两者都没有输出即表示通过。Go 不争论格式:gofmt 就是标准答案,因此代码评审中不再需要讨论格式。