跳转至

Go 工程基础

学习目标

学完本章后,学习者应该能够:

  1. 安装和管理 Go 版本。
  2. 理解 Go workspace、Go module、包管理和项目组织。
  3. 熟练使用 go rungo buildgo testgo modgo fmtgo vet
  4. 能搭建一个可维护的 Go 后端项目骨架。

Go module

Go module 是 Go 当前主流依赖管理方式。一个项目通常从 go.mod 开始:

module example.com/apikey

go 1.22

常用命令:

go mod init example.com/apikey
go mod tidy
go test ./...
go mod init example.com/apikey
go mod tidy
go test ./...

go mod tidy 会移除无用依赖,补齐缺失依赖。

常用 go 命令

命令 用途
go run 编译并运行
go build 编译
go test 运行测试
go test -race 检查数据竞争
go fmt / gofmt 格式化
go vet 静态检查常见问题
go mod tidy 整理依赖
go list 查看包信息

这些命令应该进入日常开发习惯,而不是等 CI 报错再处理。

项目组织

一个小型后端项目可以先保持简单:

apikey/
  cmd/server/main.go
  internal/config/
  internal/httpapi/
  internal/service/
  internal/repository/
  internal/domain/
  pkg/
  go.mod
  README.md

internal/ 下的包不能被项目外部直接导入,适合放业务内部代码。pkg/ 只放真的需要对外复用的包,不要把所有代码都塞进去。

包设计

包名应该简短、清晰,表达能力而不是文件夹层级。

好习惯:

  • 包内名字不要重复包名。
  • 避免 utilscommon 变成垃圾桶。
  • 包之间依赖方向要稳定。
  • 小包不等于每个文件一个包。

Go 后端实际应用例子

例子一:Makefile 或脚本统一命令

即使在 Windows 上开发,也可以在 README 中明确命令:

go fmt ./...
go vet ./...
go test ./...
go build ./cmd/server
go fmt ./...
go vet ./...
go test ./...
go build ./cmd/server

统一命令能减少“我本地可以”的协作成本。

例子二:配置入口集中管理

type Config struct {
    HTTPAddr string
    DBDSN    string
}

func LoadConfig() Config {
    return Config{
        HTTPAddr: getenv("HTTP_ADDR", ":8080"),
        DBDSN:    os.Getenv("DB_DSN"),
    }
}

配置读取集中管理,后续接入环境变量、配置文件或配置中心都更容易。

常见误区

  • 误区一:一开始就搭很复杂的目录。

目录结构应该服务于业务复杂度。小项目先简单,边界清晰后再拆。

  • 误区二:pkg 是所有公共代码目录。

pkg 应该放真正希望外部项目导入的包。内部业务代码优先放 internal

  • 误区三:依赖报错就手动改 go.sum

通常应该通过 go mod tidygo get 等命令管理依赖,不要手动乱改。

实战任务

创建一个最小 Go HTTP 服务项目:

  1. 使用 Go module。
  2. 入口放在 cmd/server/main.go
  3. 提供 /healthz 接口。
  4. README 写出运行、测试、构建命令。
  5. 运行 go fmtgo vetgo test
参考答案

目录可以这样组织:

demo/
  cmd/server/main.go
  go.mod
  README.md

main.go 可以使用标准库 net/http

package main

import (
    "log"
    "net/http"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusNoContent)
    })

    log.Fatal(http.ListenAndServe(":8080", mux))
}

README 至少写清楚 go run ./cmd/servergo test ./...go build ./cmd/server

面试题

1. Go module 解决什么问题?

参考答案

Go module 解决依赖声明、版本选择、可复现构建和模块边界问题。go.mod 描述模块路径、Go 版本和依赖版本,go.sum 记录依赖校验信息。

它让项目不再依赖 GOPATH 固定目录,也方便 CI 和团队成员使用一致依赖。

2. internal 目录有什么作用?

参考答案

internal 是 Go 的特殊目录。放在 internal 下的包只能被其父目录树内的代码导入,外部模块无法直接导入。

它适合放业务内部实现,帮助控制包的可见性,避免内部代码被外部依赖后难以重构。

3. go fmtgo vet 分别做什么?

参考答案

go fmt 负责统一代码格式,减少风格争论。go vet 是静态检查工具,用于发现一些可疑代码,例如格式化参数不匹配、不可达代码、错误的 struct tag 等。

两者都应该进入日常开发和 CI。