Go 语言完全指南
1. 核心理念
Go 设计哲学:简洁、高效、并发。放弃继承和泛型复杂性,拥抱组合和接口。
1.1. 设计原则
- 少即是多: 25个关键字,语法简单
- 组合优于继承: 通过嵌入实现代码复用
- 显式优于隐式: 错误处理显式,无异常机制
- 并发内置: goroutine和channel是一等公民
- 快速编译: 依赖管理清晰,编译速度极快
2. 环境配置
2.1. 安装与工具链
# 安装 Go 1.23+ go version # 环境变量 export GOPATH=$HOME/go export GOROOT=/usr/local/go export PATH=$PATH:$GOROOT/bin:$GOPATH/bin # 模块代理(国内) export GOPROXY=https://goproxy.cn,direct
2.2. 关键工具
- go mod: 依赖管理
- go fmt: 代码格式化(强制统一)
- go vet: 静态分析
- go test: 测试框架
- gopls: LSP语言服务器
- golangci-lint: 集成linter
3. 基础语法
3.1. 包与导入
package main // 可执行程序必须是main包
import (
"fmt" // 标准库
"math/rand"
_ "image/png" // 仅执行init(),下划线导入
. "strings" // 点导入(不推荐,污染命名空间)
m "mymodule" // 别名导入
)
Tips: 未使用的导入会编译失败,使用 goimports 自动管理。
3.2. 变量与常量
// 变量声明
var a int = 10
var b = 20 // 类型推断
c := 30 // 短声明(仅函数内)
// 多变量
var x, y int = 1, 2
m, n := "hello", true
// 零值: int(0), string(""), bool(false), pointer(nil)
var z int // z = 0
// 常量(编译时确定)
const Pi = 3.14
const (
StatusOK = 200
StatusNotFound = 404
)
// iota枚举器
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
)
Tips: 用 := 简化局部变量声明,但包级别用 var 更清晰。
3.3. 基本类型
// 数值类型
int8, int16, int32, int64, int // 有符号
uint8, uint16, uint32, uint64, uint
byte // uint8别名
rune // int32别名,表示Unicode码点
float32, float64
complex64, complex128
// 字符串(UTF-8,不可变)
s := "你好"
s[0] // 字节访问
for i, r := range s { // rune遍历
fmt.Printf("%d: %c\n", i, r)
}
// 类型转换(显式)
var i int = 42
var f float64 = float64(i)
var u uint = uint(f)
3.4. 复合类型
3.4.1. 数组(固定长度)
var arr [5]int
arr := [3]int{1, 2, 3}
arr := [...]int{1, 2, 3} // 自动计算长度
// 数组是值类型
a := [2]int{1, 2}
b := a // 复制整个数组
3.4.2. 切片(动态数组)
// 切片是引用类型,底层指向数组
s := []int{1, 2, 3}
s := make([]int, 5) // len=5, cap=5
s := make([]int, 3, 10) // len=3, cap=10
// 切片操作
s = append(s, 4, 5)
s2 := s[1:3] // 左闭右开
s3 := s[:2]
s4 := s[2:]
// 复制
dst := make([]int, len(s))
copy(dst, s)
// 扩容机制: cap<256翻倍,之后增长约1.25倍
Tips: 预分配容量避免频繁扩容 make([]int, 0, expectedSize)
3.4.3. 映射(哈希表)
// 创建
m := make(map[string]int)
m := map[string]int{"a": 1, "b": 2}
// 操作
m["c"] = 3
val := m["a"]
val, ok := m["x"] // 检查键是否存在
delete(m, "b")
// 遍历(无序)
for k, v := range m {
fmt.Println(k, v)
}
// map是引用类型,未初始化为nil
var m map[string]int // nil map,不能写入
m = make(map[string]int) // 初始化后才能用
3.4.4. 结构体
type Person struct {
Name string
Age int
addr string // 小写私有
}
// 初始化
p1 := Person{"Alice", 30, "NYC"}
p2 := Person{Name: "Bob", Age: 25}
p3 := &Person{Name: "Carol"} // 指针
// 匿名字段(嵌入)
type Employee struct {
Person // 嵌入Person
EmployeeID int
}
e := Employee{
Person: Person{Name: "David", Age: 28},
EmployeeID: 1001,
}
fmt.Println(e.Name) // 直接访问嵌入字段
// 标签(反射元数据)
type User struct {
Name string `json:"name" db:"user_name"`
Age int `json:"age" validate:"min=0,max=150"`
}
Tips: 优先使用结构体组合而非继承模拟。
3.5. 函数
3.5.1. 基础
// 多返回值
func div(a, b int) (int, error) {
if b == 0 {
return 0, fmt.Errorf("division by zero")
}
return a / b, nil
}
// 命名返回值
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return // 裸返回
}
// 可变参数
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
sum(1, 2, 3, 4)
3.5.2. 函数类型与闭包
// 函数是一等公民
type Processor func(int) int
func apply(f Processor, x int) int {
return f(x)
}
// 闭包
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
c := counter()
fmt.Println(c()) // 1
fmt.Println(c()) // 2
3.5.3. Defer
// 延迟执行,LIFO顺序
func example() {
defer fmt.Println("1")
defer fmt.Println("2")
fmt.Println("3")
// 输出: 3 2 1
}
// 常用于资源清理
func readFile(path string) error {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close() // 确保关闭
// 读取操作...
return nil
}
// defer参数立即求值
i := 0
defer fmt.Println(i) // 打印0
i++
4. 方法与接口
4.1. 方法
type Rectangle struct {
Width, Height float64
}
// 值接收者
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
// 指针接收者(可修改)
func (r *Rectangle) Scale(factor float64) {
r.Width *= factor
r.Height *= factor
}
r := Rectangle{10, 5}
fmt.Println(r.Area()) // 50
r.Scale(2)
fmt.Println(r.Area()) // 200
Tips: 指针接收者用于: 1)修改接收者 2)大结构体避免拷贝 3)一致性(其他方法用指针时)
4.2. 接口
// 接口是隐式实现
type Shape interface {
Area() float64
Perimeter() float64
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return 3.14 * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * 3.14 * c.Radius
}
// Circle自动实现Shape接口
var s Shape = Circle{5}
// 空接口
var any interface{} // 可接收任何类型
any = 42
any = "hello"
4.2.1. 类型断言与类型开关
// 类型断言
var i interface{} = "hello"
s := i.(string) // 直接断言,失败panic
s, ok := i.(string) // 安全断言
// 类型开关
func describe(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("int: %d\n", v)
case string:
fmt.Printf("string: %s\n", v)
case Shape:
fmt.Printf("area: %f\n", v.Area())
default:
fmt.Printf("unknown type\n")
}
}
4.2.2. 常用接口
// io.Reader
type Reader interface {
Read(p []byte) (n int, err error)
}
// io.Writer
type Writer interface {
Write(p []byte) (n int, err error)
}
// fmt.Stringer
type Stringer interface {
String() string
}
// error
type error interface {
Error() string
}
设计理念: 接口小而精,单一职责。标准库多是1-3方法接口。
5. 错误处理
5.1. 基础错误
// 创建错误
err := errors.New("something went wrong")
err := fmt.Errorf("failed to process: %s", filename)
// 检查错误
result, err := doSomething()
if err != nil {
return err // 向上传播
}
// 包装错误(Go 1.13+)
if err != nil {
return fmt.Errorf("operation failed: %w", err)
}
5.2. 错误检查与包装
import "errors"
// errors.Is: 判断错误链中是否包含特定错误
var ErrNotFound = errors.New("not found")
if errors.Is(err, ErrNotFound) {
// 处理未找到
}
// errors.As: 提取特定类型错误
type ValidationError struct {
Field string
Msg string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Msg)
}
var valErr *ValidationError
if errors.As(err, &valErr) {
fmt.Println(valErr.Field)
}
5.3. 自定义错误
type PathError struct {
Op string
Path string
Err error
}
func (e *PathError) Error() string {
return e.Op + " " + e.Path + ": " + e.Err.Error()
}
func (e *PathError) Unwrap() error {
return e.Err // 支持errors.Is/As
}
Tips:
- 不要忽略错误: result, _ : doSomething()= 应谨慎使用
- 错误消息小写开头,无标点结尾
- Panic仅用于不可恢复错误,库函数应返回error
6. 并发编程
6.1. Goroutine
// 启动goroutine
go func() {
fmt.Println("异步执行")
}()
// 传递参数
for i := 0; i < 3; i++ {
go func(n int) { // 避免闭包陷阱
fmt.Println(n)
}(i)
}
// 等待goroutine(不推荐,用WaitGroup)
time.Sleep(time.Second)
Tips: Goroutine开销约2KB,可轻松启动百万级。
6.2. Channel
// 创建
ch := make(chan int) // 无缓冲
ch := make(chan int, 10) // 缓冲10
// 发送与接收
ch <- 42 // 发送
val := <-ch // 接收
val, ok := <-ch // ok为false表示channel已关闭
// 关闭channel
close(ch)
// range遍历(直到关闭)
for val := range ch {
fmt.Println(val)
}
// 单向channel
func producer(ch chan<- int) { // 只能发送
ch <- 1
}
func consumer(ch <-chan int) { // 只能接收
val := <-ch
}
6.2.1. Select多路复用
select {
case msg := <-ch1:
fmt.Println("ch1:", msg)
case msg := <-ch2:
fmt.Println("ch2:", msg)
case ch3 <- 42:
fmt.Println("sent to ch3")
case <-time.After(time.Second):
fmt.Println("timeout")
default:
fmt.Println("非阻塞操作")
}
6.2.2. 常用并发模式
// 1. 生产者-消费者
func producer(ch chan<- int) {
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
}
func consumer(ch <-chan int, done chan<- bool) {
for val := range ch {
fmt.Println(val)
}
done <- true
}
// 2. Fan-out/Fan-in
func fanOut(in <-chan int, workers int) []<-chan int {
outs := make([]<-chan int, workers)
for i := 0; i < workers; i++ {
outs[i] = worker(in)
}
return outs
}
func fanIn(channels ...<-chan int) <-chan int {
out := make(chan int)
var wg sync.WaitGroup
for _, ch := range channels {
wg.Add(1)
go func(c <-chan int) {
defer wg.Done()
for v := range c {
out <- v
}
}(ch)
}
go func() {
wg.Wait()
close(out)
}()
return out
}
// 3. Pipeline
func gen(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
func sq(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
out <- n * n
}
close(out)
}()
return out
}
// 使用: gen -> sq -> print
for n := range sq(gen(2, 3, 4)) {
fmt.Println(n)
}
6.3. Sync包
6.3.1. WaitGroup
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
fmt.Println(id)
}(i)
}
wg.Wait() // 等待所有goroutine完成
6.3.2. Mutex
type SafeCounter struct {
mu sync.Mutex
count int
}
func (c *SafeCounter) Inc() {
c.mu.Lock()
defer c.mu.Unlock()
c.count++
}
func (c *SafeCounter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.count
}
// RWMutex(读写锁)
type Cache struct {
mu sync.RWMutex
data map[string]string
}
func (c *Cache) Get(key string) string {
c.mu.RLock() // 读锁
defer c.mu.RUnlock()
return c.data[key]
}
func (c *Cache) Set(key, val string) {
c.mu.Lock() // 写锁
defer c.mu.Unlock()
c.data[key] = val
}
6.3.3. Once
var once sync.Once
func initDB() {
once.Do(func() {
// 仅执行一次,线程安全
fmt.Println("Initializing DB")
})
}
6.3.4. Atomic
import "sync/atomic" var counter int64 atomic.AddInt64(&counter, 1) val := atomic.LoadInt64(&counter) atomic.StoreInt64(&counter, 100) swapped := atomic.CompareAndSwapInt64(&counter, 100, 200)
6.4. Context
// 1. 取消信号
ctx, cancel := context.WithCancel(context.Background())
go func() {
select {
case <-ctx.Done():
fmt.Println("canceled")
}
}()
cancel() // 触发取消
// 2. 超时
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
// 3. 截止时间
deadline := time.Now().Add(5 * time.Second)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel()
// 4. 传递值(谨慎使用)
ctx := context.WithValue(context.Background(), "userID", 123)
userID := ctx.Value("userID").(int)
// 传递到函数
func doWork(ctx context.Context) error {
select {
case <-ctx.Done():
return ctx.Err() // context.Canceled 或 context.DeadlineExceeded
case <-time.After(1 * time.Second):
return nil
}
}
设计原则: Context应作为第一个参数,不要存储在结构体中。
7. 泛型 (Go 1.18+)
7.1. 类型参数
// 泛型函数
func Min[T constraints.Ordered](a, b T) T {
if a < b {
return a
}
return b
}
Min(1, 2) // int
Min(1.5, 2.3) // float64
// 泛型类型
type Stack[T any] struct {
items []T
}
func (s *Stack[T]) Push(item T) {
s.items = append(s.items, item)
}
func (s *Stack[T]) Pop() (T, bool) {
if len(s.items) == 0 {
var zero T
return zero, false
}
item := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
return item, true
}
// 使用
intStack := Stack[int]{}
intStack.Push(1)
val, ok := intStack.Pop()
7.2. 类型约束
// 自定义约束
type Number interface {
int | int64 | float64
}
func Sum[T Number](nums []T) T {
var total T
for _, n := range nums {
total += n
}
return total
}
// 近似约束(底层类型)
type MyInt int
type Integer interface {
~int | ~int64 // ~表示底层类型
}
// 方法约束
type Stringer[T any] interface {
String() string
}
Tips: 泛型用于数据结构和算法,不要过度使用。接口通常更灵活。
8. 包管理与模块
8.1. Go Modules
# 初始化模块 go mod init github.com/user/project # 添加依赖(自动) import "github.com/gin-gonic/gin" go mod tidy # 整理依赖 # 查看依赖 go list -m all go mod graph # 升级依赖 go get -u github.com/gin-gonic/gin go get github.com/gin-gonic/gin@v1.8.0 # 特定版本 # vendor目录(可选) go mod vendor
8.2. go.mod结构
module github.com/user/project
go 1.23
require (
github.com/gin-gonic/gin v1.9.0
golang.org/x/sync v0.5.0
)
require (
// 间接依赖
github.com/gin-contrib/sse v0.1.0 // indirect
)
replace (
// 替换依赖(本地开发)
github.com/old/module => github.com/new/module v1.0.0
github.com/local/module => ../local/module
)
exclude github.com/broken/module v1.2.3
8.3. 工作区 (Go 1.18+)
# 多模块开发 go work init ./module1 ./module2 go work use ./module3
9. 测试
9.1. 单元测试
// math.go
package math
func Add(a, b int) int {
return a + b
}
// math_test.go
package math
import "testing"
func TestAdd(t *testing.T) {
result := Add(2, 3)
expected := 5
if result != expected {
t.Errorf("Add(2, 3) = %d; want %d", result, expected)
}
}
// 表驱动测试
func TestAddTable(t *testing.T) {
tests := []struct {
name string
a, b int
want int
}{
{"positive", 2, 3, 5},
{"negative", -1, 1, 0},
{"zero", 0, 0, 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Add(tt.a, tt.b)
if got != tt.want {
t.Errorf("got %d, want %d", got, tt.want)
}
})
}
}
9.2. 基准测试
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(2, 3)
}
}
// 运行: go test -bench=. -benchmem
9.3. 示例测试
func ExampleAdd() {
fmt.Println(Add(2, 3))
// Output: 5
}
9.4. Mock与接口
// 使用接口便于测试
type UserService interface {
GetUser(id int) (*User, error)
}
// 生产实现
type DBUserService struct{}
func (s *DBUserService) GetUser(id int) (*User, error) {
// 数据库查询
}
// 测试Mock
type MockUserService struct {
users map[int]*User
}
func (s *MockUserService) GetUser(id int) (*User, error) {
return s.users[id], nil
}
9.5. 测试覆盖率
go test -cover go test -coverprofile=coverage.out go tool cover -html=coverage.out
10. 反射与unsafe
10.1. 反射基础
import "reflect"
func inspect(x interface{}) {
v := reflect.ValueOf(x)
t := reflect.TypeOf(x)
fmt.Println("Type:", t)
fmt.Println("Kind:", v.Kind())
// 获取值
if v.Kind() == reflect.Int {
fmt.Println("Value:", v.Int())
}
}
// 修改值
func modify(x interface{}) {
v := reflect.ValueOf(x)
if v.Kind() != reflect.Ptr {
return
}
v = v.Elem() // 解引用
if v.CanSet() {
v.SetInt(100)
}
}
i := 42
modify(&i) // i = 100
10.2. 结构体反射
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
func inspectStruct(x interface{}) {
t := reflect.TypeOf(x)
v := reflect.ValueOf(x)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
tag := field.Tag.Get("json")
fmt.Printf("%s (%s) = %v, tag: %s\n",
field.Name, field.Type, value.Interface(), tag)
}
}
10.3. Unsafe
import "unsafe"
// 指针转换(危险)
var i int64 = 42
ptr := unsafe.Pointer(&i)
floatPtr := (*float64)(ptr) // 类型不安全
// 获取结构体字段偏移
type Person struct {
Name string
Age int
}
offset := unsafe.Offsetof(Person{}.Age)
// Sizeof
size := unsafe.Sizeof(Person{})
警告: unsafe破坏类型安全和内存安全,仅用于性能关键场景。
11. 高级特性
11.1. 嵌入文件 (Go 1.16+)
import _ "embed"
//go:embed version.txt
var version string
//go:embed templates/*.html
var templates embed.FS
func loadTemplate() {
data, _ := templates.ReadFile("templates/index.html")
}
11.2. 构建标签
//go:build linux && amd64
// +build linux,amd64 // 旧语法
package platform
func init() {
// Linux AMD64专用代码
}
11.3. CGO
/*
#include <stdlib.h>
int add(int a, int b) {
return a + b;
}
*/
import "C"
func main() {
result := C.add(1, 2)
fmt.Println(result)
}
Tips: CGO牺牲跨平台和编译速度,尽量避免。
11.4. 汇编优化
// add_amd64.s
TEXT ·Add(SB), NOSPLIT, $0-24
MOVQ a+0(FP), AX
MOVQ b+8(FP), BX
ADDQ BX, AX
MOVQ AX, ret+16(FP)
RET
// add.go
//go:noescape
func Add(a, b int) int
12. 性能优化
12.1. Profiling
import (
"runtime/pprof"
_ "net/http/pprof"
)
// CPU profiling
f, _ := os.Create("cpu.prof")
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
// 内存profiling
f, _ := os.Create("mem.prof")
pprof.WriteHeapProfile(f)
// HTTP服务自动暴露
import _ "net/http/pprof"
go func() {
http.ListenAndServe("localhost:6060", nil)
}()
// 访问: http://localhost:6060/debug/pprof/
// 分析: go tool pprof http://localhost:6060/debug/pprof/profile
12.2. 性能优化技巧
// 1. 避免不必要的内存分配
// 差: 频繁分配
for i := 0; i < 1000; i++ {
s := make([]int, 100)
// ...
}
// 好: 复用
s := make([]int, 100)
for i := 0; i < 1000; i++ {
s = s[:0] // 重置长度,保留容量
// ...
}
// 2. 字符串拼接
// 差: + 操作符
var result string
for i := 0; i < 1000; i++ {
result += "a" // O(n²)
}
// 好: strings.Builder
var builder strings.Builder
builder.Grow(1000) // 预分配
for i := 0; i < 1000; i++ {
builder.WriteString("a")
}
result := builder.String()
// 3. 结构体对齐
// 差: 24字节(因填充)
type Bad struct {
a bool // 1 + 7填充
b int64 // 8
c bool // 1 + 7填充
}
// 好: 16字节
type Good struct {
b int64 // 8
a bool // 1
c bool // 1 + 6填充
}
// 4. 同步池
var bufferPool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
buf := bufferPool.Get().(*bytes.Buffer)
buf.Reset()
defer bufferPool.Put(buf)
// 5. 避免interface{}装箱
// 差: 装箱开销
func processInterface(x interface{}) {
// 类型断言开销
}
// 好: 泛型
func process[T any](x T) {
// 无装箱
}
12.3. Benchmark优化示例
// 优化前
func Concat(strs []string) string {
var result string
for _, s := range strs {
result += s
}
return result
}
// 优化后
func ConcatOptimized(strs []string) string {
var builder strings.Builder
totalLen := 0
for _, s := range strs {
totalLen += len(s)
}
builder.Grow(totalLen)
for _, s := range strs {
builder.WriteString(s)
}
return builder.String()
}
// Benchmark显示10x+性能提升
13. 标准库精选
13.1. I/O操作
import (
"bufio"
"io"
"os"
)
// 读文件
data, err := os.ReadFile("file.txt")
// 写文件
err := os.WriteFile("file.txt", data, 0644)
// 流式读取
f, _ := os.Open("large.txt")
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
// 处理每行
}
// 流式写入
f, _ := os.Create("output.txt")
defer f.Close()
writer := bufio.NewWriter(f)
writer.WriteString("Hello\n")
writer.Flush()
// io.Copy
src, _ := os.Open("src.txt")
dst, _ := os.Create("dst.txt")
io.Copy(dst, src)
// MultiReader
r1 := strings.NewReader("first ")
r2 := strings.NewReader("second")
r := io.MultiReader(r1, r2)
io.Copy(os.Stdout, r)
13.2. JSON处理
import "encoding/json"
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email,omitempty"`
Password string `json:"-"` // 忽略
CreatedAt time.Time `json:"created_at"`
}
// 序列化
user := User{ID: 1, Name: "Alice"}
data, err := json.Marshal(user)
// 格式化
data, err := json.MarshalIndent(user, "", " ")
// 反序列化
var user User
err := json.Unmarshal(data, &user)
// 流式处理
encoder := json.NewEncoder(os.Stdout)
encoder.Encode(user)
decoder := json.NewDecoder(os.Stdin)
decoder.Decode(&user)
// 自定义序列化
func (u User) MarshalJSON() ([]byte, error) {
type Alias User
return json.Marshal(&struct {
*Alias
Type string `json:"type"`
}{
Alias: (*Alias)(&u),
Type: "user",
})
}
13.3. HTTP服务
import (
"net/http"
"time"
)
// 简单服务器
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World"))
})
http.ListenAndServe(":8080", nil)
}
// 自定义路由
mux := http.NewServeMux()
mux.HandleFunc("/users", handleUsers)
mux.HandleFunc("/posts", handlePosts)
// 中间件
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
})
}
// 配置服务器
server := &http.Server{
Addr: ":8080",
Handler: loggingMiddleware(mux),
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
server.ListenAndServe()
// HTTP客户端
client := &http.Client{
Timeout: 30 * time.Second,
}
resp, err := client.Get("https://api.example.com/data")
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
// POST请求
data := []byte(`{"name":"Alice"}`)
resp, err := client.Post("https://api.example.com/users",
"application/json", bytes.NewBuffer(data))
13.4. 时间处理
import "time"
// 当前时间
now := time.Now()
// 解析时间
t, _ := time.Parse("2006-01-02 15:04:05", "2025-01-15 10:30:00")
t, _ := time.Parse(time.RFC3339, "2025-01-15T10:30:00Z")
// 格式化(使用参考时间: Mon Jan 2 15:04:05 MST 2006)
str := now.Format("2006-01-02 15:04:05")
str := now.Format(time.RFC3339)
// 时间运算
tomorrow := now.Add(24 * time.Hour)
nextWeek := now.AddDate(0, 0, 7)
duration := t2.Sub(t1)
// 定时器
timer := time.NewTimer(5 * time.Second)
<-timer.C // 5秒后触发
// Ticker
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for t := range ticker.C {
fmt.Println("Tick at", t)
}
// 超时控制
select {
case result := <-ch:
// 处理结果
case <-time.After(5 * time.Second):
// 超时
}
13.5. 正则表达式
import "regexp"
// 编译正则(缓存)
re := regexp.MustCompile(`\d{3}-\d{4}`)
// 匹配
matched := re.MatchString("123-4567")
// 查找
result := re.FindString("Call 123-4567 or 890-1234")
results := re.FindAllString("...", -1)
// 捕获组
re := regexp.MustCompile(`(\w+)@(\w+\.\w+)`)
matches := re.FindStringSubmatch("user@example.com")
// matches[0]: 完整匹配
// matches[1]: user
// matches[2]: example.com
// 替换
re := regexp.MustCompile(`\d+`)
result := re.ReplaceAllString("Age: 25", "XX") // "Age: XX"
13.6. 数据库操作
import "database/sql"
import _ "github.com/lib/pq" // PostgreSQL驱动
// 连接
db, err := sql.Open("postgres", "user=postgres dbname=mydb")
defer db.Close()
// 连接池配置
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(5)
db.SetConnMaxLifetime(5 * time.Minute)
// 查询单行
var name string
var age int
err := db.QueryRow("SELECT name, age FROM users WHERE id = $1", 1).Scan(&name, &age)
// 查询多行
rows, err := db.Query("SELECT id, name FROM users WHERE age > $1", 18)
defer rows.Close()
for rows.Next() {
var id int
var name string
rows.Scan(&id, &name)
fmt.Println(id, name)
}
// 插入
result, err := db.Exec("INSERT INTO users (name, age) VALUES ($1, $2)", "Alice", 25)
id, _ := result.LastInsertId()
affected, _ := result.RowsAffected()
// 事务
tx, err := db.Begin()
_, err = tx.Exec("UPDATE accounts SET balance = balance - 100 WHERE id = 1")
_, err = tx.Exec("UPDATE accounts SET balance = balance + 100 WHERE id = 2")
if err != nil {
tx.Rollback()
return err
}
tx.Commit()
// Prepared Statement
stmt, err := db.Prepare("SELECT name FROM users WHERE id = $1")
defer stmt.Close()
stmt.QueryRow(1).Scan(&name)
14. 项目结构
14.1. 标准布局
myproject/ ├── cmd/ # 主程序入口 │ ├── server/ │ │ └── main.go │ └── cli/ │ └── main.go ├── internal/ # 私有代码(不可被外部导入) │ ├── app/ │ ├── pkg/ │ └── config/ ├── pkg/ # 公开库代码(可被外部导入) │ ├── api/ │ └── util/ ├── api/ # API定义(OpenAPI/gRPC) ├── web/ # 静态资源 ├── configs/ # 配置文件 ├── scripts/ # 脚本 ├── test/ # 额外测试 ├── docs/ # 文档 ├── examples/ # 示例 ├── go.mod ├── go.sum ├── Makefile └── README.md
14.2. 分层架构示例
// internal/domain/user.go - 领域模型
package domain
type User struct {
ID int
Username string
Email string
CreatedAt time.Time
}
// internal/repository/user_repository.go - 数据访问
package repository
type UserRepository interface {
Create(user *domain.User) error
FindByID(id int) (*domain.User, error)
FindByEmail(email string) (*domain.User, error)
}
type userRepository struct {
db *sql.DB
}
func NewUserRepository(db *sql.DB) UserRepository {
return &userRepository{db: db}
}
// internal/service/user_service.go - 业务逻辑
package service
type UserService struct {
repo repository.UserRepository
}
func NewUserService(repo repository.UserRepository) *UserService {
return &UserService{repo: repo}
}
func (s *UserService) Register(username, email string) (*domain.User, error) {
// 业务逻辑
existing, _ := s.repo.FindByEmail(email)
if existing != nil {
return nil, errors.New("email already exists")
}
user := &domain.User{
Username: username,
Email: email,
CreatedAt: time.Now(),
}
if err := s.repo.Create(user); err != nil {
return nil, err
}
return user, nil
}
// internal/handler/user_handler.go - HTTP处理器
package handler
type UserHandler struct {
service *service.UserService
}
func NewUserHandler(service *service.UserService) *UserHandler {
return &UserHandler{service: service}
}
func (h *UserHandler) Register(w http.ResponseWriter, r *http.Request) {
var req struct {
Username string `json:"username"`
Email string `json:"email"`
}
json.NewDecoder(r.Body).Decode(&req)
user, err := h.service.Register(req.Username, req.Email)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
json.NewEncoder(w).Encode(user)
}
// cmd/server/main.go - 主程序
package main
func main() {
db := setupDatabase()
userRepo := repository.NewUserRepository(db)
userService := service.NewUserService(userRepo)
userHandler := handler.NewUserHandler(userService)
http.HandleFunc("/register", userHandler.Register)
http.ListenAndServe(":8080", nil)
}
15. 最佳实践
15.1. 编码规范
// 1. 命名
// 包名: 小写单数,简短有意义
package user
// 导出标识符: 大写开头
func ProcessData() {}
// 私有标识符: 小写开头
func helperFunction() {}
// 接口: -er结尾
type Reader interface {}
type Closer interface {}
// 2. 注释
// 包注释(package前)
// Package user provides user management functionality.
package user
// 导出标识符必须有注释(以名称开头)
// ProcessData processes the input data and returns the result.
func ProcessData() {}
// 3. 错误处理
// 提前返回
if err != nil {
return err
}
// 继续正常逻辑
// 不要忽略错误
result, err := doSomething()
if err != nil {
return nil, fmt.Errorf("failed to do something: %w", err)
}
// 4. 接口设计
// 小而精
type Reader interface {
Read(p []byte) (n int, err error)
}
// 消费者定义接口,生产者实现
// (在使用方定义需要的接口,而非提供方)
// 5. 并发
// 使用context传递取消信号
func doWork(ctx context.Context) error {
select {
case <-ctx.Done():
return ctx.Err()
case <-workDone:
return nil
}
}
// channel由发送方关闭
ch := make(chan int)
go func() {
defer close(ch)
for i := 0; i < 10; i++ {
ch <- i
}
}()
// 6. 初始化
// 使用构造函数
func NewServer(addr string) *Server {
return &Server{
addr: addr,
mux: http.NewServeMux(),
}
}
// 避免init(),除非必要(如注册驱动)
15.2. 常见陷阱
// 1. 循环变量捕获
for _, v := range values {
go func() {
fmt.Println(v) // 错误: v被共享
}()
}
// 正确:
for _, v := range values {
v := v // 创建副本
go func() {
fmt.Println(v)
}()
}
// 或传参:
for _, v := range values {
go func(val int) {
fmt.Println(val)
}(v)
}
// 2. Slice陷阱
s1 := []int{1, 2, 3, 4, 5}
s2 := s1[1:3] // [2, 3]
s2[0] = 999 // s1变为[1, 999, 3, 4, 5]
// 完全复制:
s2 := make([]int, len(s1))
copy(s2, s1)
// 3. Map并发读写
m := make(map[string]int)
go func() { m["a"] = 1 }()
go func() { m["b"] = 2 }() // 竞态条件!
// 使用sync.Map或加锁
// 4. Defer在循环中
for _, file := range files {
f, _ := os.Open(file)
defer f.Close() // 循环结束后才执行,可能耗尽文件描述符
}
// 正确: 使用函数封装
for _, file := range files {
func() {
f, _ := os.Open(file)
defer f.Close()
// 处理文件
}()
}
// 5. nil interface
var p *int = nil
var i interface{} = p
fmt.Println(i == nil) // false! interface包含类型信息
// 6. Goroutine泄漏
func leak() {
ch := make(chan int)
go func() {
val := <-ch // 永远阻塞,goroutine泄漏
fmt.Println(val)
}()
// 忘记发送数据
}
// 使用context取消
func noLeak(ctx context.Context) {
ch := make(chan int)
go func() {
select {
case val := <-ch:
fmt.Println(val)
case <-ctx.Done():
return
}
}()
}
16. 工具链使用
16.1. 常用命令
# 格式化 go fmt ./... goimports -w . # 静态分析 go vet ./... golangci-lint run # 构建 go build -o myapp ./cmd/myapp go build -ldflags="-s -w" -o myapp # 压缩二进制 # 交叉编译 GOOS=linux GOARCH=amd64 go build GOOS=windows GOARCH=amd64 go build # 运行 go run ./cmd/myapp # 测试 go test ./... go test -v -cover ./... go test -race ./... # 竞态检测 go test -bench=. -benchmem # 性能分析 go test -cpuprofile=cpu.prof -memprofile=mem.prof go tool pprof cpu.prof # 依赖管理 go mod init go mod tidy go mod download go mod verify go get -u ./... # 升级所有依赖 # 文档 go doc fmt.Println godoc -http=:6060 # 代码生成 go generate ./...
16.2. Makefile示例
.PHONY: build test clean APP_NAME=myapp BUILD_DIR=bin build: go build -o $(BUILD_DIR)/$(APP_NAME) ./cmd/$(APP_NAME) test: go test -v -cover ./... test-race: go test -race ./... bench: go test -bench=. -benchmem ./... lint: golangci-lint run fmt: gofmt -s -w . goimports -w . clean: rm -rf $(BUILD_DIR) run: go run ./cmd/$(APP_NAME) docker: docker build -t $(APP_NAME):latest . .DEFAULT_GOAL := build
16.3. Dockerfile最佳实践
# 多阶段构建 FROM golang:1.23-alpine AS builder WORKDIR /app # 缓存依赖 COPY go.mod go.sum ./ RUN go mod download # 构建 COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main ./cmd/server # 最小运行镜像 FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=builder /app/main . COPY --from=builder /app/configs ./configs EXPOSE 8080 CMD ["./main"]
17. 进阶主题
17.1. 内存模型
Go保证:
- 单goroutine内,读写顺序与代码顺序一致
- Init函数在main前执行
- Goroutine创建happens-before其执行
- Channel发送happens-before接收完成
- Mutex.Unlock() happens-before后续Lock()
17.2. 逃逸分析
// 栈分配
func stack() int {
x := 42
return x
}
// 堆分配(逃逸)
func heap() *int {
x := 42
return &x // x逃逸到堆
}
// 查看逃逸分析
// go build -gcflags="-m" main.go
优化技巧:
- 避免返回局部变量指针
- 减少interface{}使用
- 预分配slice容量
17.3. 编译器指令
//go:noinline // 禁止内联 //go:nosplit // 禁止栈分裂 //go:noescape // 参数不逃逸 //go:linkname // 链接到其他符号 //go:generate // 代码生成 //go:embed // 嵌入文件 //go:generate go run gen.go //go:generate mockgen -source=interface.go -destination=mock.go
18. 总结
18.1. 学习路径
- 基础语法 → 2. 并发编程 → 3. 标准库 → 4. 项目实战 → 5. 性能优化
18.2. 推荐资源
- 官方文档: https://go.dev/doc/
- Effective Go: https://go.dev/doc/effective_go
- Go by Example: https://gobyexample.com
- Go 101: https://go101.org
18.3. 关键要点
- 简洁至上: 选择最简单的解决方案
- 显式错误处理: 不要忽略错误
- 组合优于继承: 使用接口和嵌入
- 并发安全: 通过channel通信,不要共享内存
- 性能意识: 了解内存分配和逃逸分析
- 测试先行: 良好的测试覆盖率
- 标准工具: 使用go fmt, go vet等标准工具
Go的哲学是"少即是多",避免过度设计,写清晰简洁的代码。
Comments:
Email questions, comments, and corrections to hi@smartisan.dev.
Submissions may appear publicly on this website, unless requested otherwise in your email.