🐹 Go 语言快速入门
掌握 Go 语言核心特性:类型系统、并发模型、接口设计与标准库——这些是构建高性能服务器的关键。
1. 为什么选择 Go 做服务器开发?
🚀 性能优势
- 编译为原生机器码,接近 C 性能
- 内置高效 GC,停顿时间 <1ms
- goroutine 栈初始 2KB,可承载百万并发
- 内置 CPU profiler,易于调优
⚡ 开发效率
- 编译速度极快(秒级)
- 语法简洁,学习曲线平缓
- 强大的标准库,net/http 开箱即用
- 静态类型 + 类型推断,安全不冗余
2. 快速安装与工程结构
安装 Go
# macOS
brew install go
# 验证安装
go version # go version go1.22.x darwin/arm64
# 设置模块代理 (国内加速)
go env -w GOPROXY=https://goproxy.cn,direct
go env -w GONOSUMCHECK=*
标准工程结构
myservice/
├── cmd/
│ └── server/
│ └── main.go # 程序入口
├── internal/ # 私有包 (外部无法 import)
│ ├── handler/ # HTTP 处理器
│ ├── service/ # 业务逻辑层
│ ├── repository/ # 数据访问层
│ └── model/ # 数据模型
├── pkg/ # 可复用的公共包
│ ├── logger/
│ └── config/
├── api/ # OpenAPI/Protobuf 定义
├── config/ # 配置文件
├── migrations/ # 数据库迁移
├── go.mod # 模块依赖
├── go.sum # 依赖校验
└── Makefile # 构建脚本
# 初始化模块
mkdir myservice && cd myservice
go mod init github.com/yourname/myservice
# 添加依赖
go get github.com/gin-gonic/gin
go get gorm.io/gorm
# 整理依赖
go mod tidy
3. Go 类型系统
package main
import "fmt"
// 基础类型
var (
name string = "Alice"
age int = 30
score float64 = 98.5
active bool = true
data []byte = []byte{0x48, 0x65}
)
// 类型推断
host := "localhost"
port := 8080
// 结构体 (核心数据组织方式)
type User struct {
ID int64 `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Email string `json:"email" db:"email"`
Password string `json:"-"` // json 序列化时忽略
CreatedAt time.Time `json:"created_at"`
}
// 方法 (值接收者 vs 指针接收者)
func (u User) String() string {
return fmt.Sprintf("User{%d, %s}", u.ID, u.Name)
}
func (u *User) SetEmail(email string) {
u.Email = email // 修改原始数据需要指针接收者
}
// 接口
type Stringer interface {
String() string
}
// 枚举模拟
type Status int
const (
StatusPending Status = iota // 0
StatusActive // 1
StatusDisabled // 2
)
func (s Status) String() string {
return [...]string{"pending", "active", "disabled"}[s]
}
// map 与 slice
func main() {
// slice
users := []User{
{ID: 1, Name: "Alice"},
{ID: 2, Name: "Bob"},
}
users = append(users, User{ID: 3, Name: "Charlie"})
// map
cache := map[string]int{
"hits": 100,
"misses": 5,
}
// 安全访问 (comma-ok 惯用法)
val, ok := cache["hits"]
if ok {
fmt.Println("命中次数:", val)
}
// range 遍历
for i, u := range users {
fmt.Printf("[%d] %s\n", i, u.Name)
}
}
4. 错误处理
Go 的错误哲学
Go 将错误视为普通值,而非异常。函数通过多返回值返回错误,调用者必须显式处理——这使错误处理可见且可靠。
package main
import (
"errors"
"fmt"
)
// 自定义错误类型
type AppError struct {
Code int
Message string
Err error // 原始错误 (错误链)
}
func (e *AppError) Error() string {
if e.Err != nil {
return fmt.Sprintf("[%d] %s: %v", e.Code, e.Message, e.Err)
}
return fmt.Sprintf("[%d] %s", e.Code, e.Message)
}
func (e *AppError) Unwrap() error { return e.Err }
// 哨兵错误
var (
ErrNotFound = errors.New("resource not found")
ErrUnauthorized = errors.New("unauthorized")
ErrValidation = errors.New("validation failed")
)
func findUser(id int) (*User, error) {
if id <= 0 {
return nil, &AppError{Code: 400, Message: "invalid id", Err: ErrValidation}
}
if id > 100 {
return nil, fmt.Errorf("findUser: %w", ErrNotFound) // %w 包装错误
}
return &User{ID: int64(id), Name: "Alice"}, nil
}
func main() {
user, err := findUser(-1)
if err != nil {
// errors.Is: 检查错误链中是否有目标错误
if errors.Is(err, ErrValidation) {
fmt.Println("参数校验失败:", err)
}
// errors.As: 从错误链中提取特定类型
var appErr *AppError
if errors.As(err, &appErr) {
fmt.Printf("应用错误码: %d\n", appErr.Code)
}
return
}
fmt.Println(user)
}
5. Goroutine 并发模型
goroutine 是 Go 的核心竞争力。一个 goroutine 只需约 2KB 内存,Go 运行时用 M:N 调度模型将数百万 goroutine 映射到操作系统线程上。
G1 (运行中)
G2 (运行中)
G3 (等待IO)
G4 (就绪)
G5 (等待Channel)
G6 (就绪)
↑ Go 运行时调度器将 goroutine 分配到 OS 线程 (P/M) 上执行
package main
import (
"fmt"
"sync"
"time"
)
// ─── 基础 goroutine ───────────────────────────────────────────
func basicGoroutine() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) { // 注意:传入参数避免闭包变量捕获问题
defer wg.Done()
time.Sleep(time.Duration(id*100) * time.Millisecond)
fmt.Printf("goroutine %d 完成\n", id)
}(i)
}
wg.Wait()
fmt.Println("所有 goroutine 完成")
}
// ─── Channel 通信 ─────────────────────────────────────────────
func channelDemo() {
// 无缓冲 channel: 发送者阻塞直到接收者准备好
ch := make(chan int)
// 有缓冲 channel: 发送不超过容量时不阻塞
buffered := make(chan int, 10)
// 生产者-消费者模式
go func() {
for i := 1; i <= 5; i++ {
buffered <- i // 发送
}
close(buffered) // 关闭 channel,接收者会收到零值
}()
for v := range buffered { // range channel: 直到关闭
fmt.Printf("收到: %d\n", v)
}
_ = ch
}
// ─── Select 多路复用 ──────────────────────────────────────────
func selectDemo() {
ch1 := make(chan string, 1)
ch2 := make(chan string, 1)
timeout := time.After(2 * time.Second)
go func() {
time.Sleep(300 * time.Millisecond)
ch1 <- "来自 ch1"
}()
go func() {
time.Sleep(500 * time.Millisecond)
ch2 <- "来自 ch2"
}()
for i := 0; i < 2; i++ {
select {
case msg := <-ch1:
fmt.Println("ch1:", msg)
case msg := <-ch2:
fmt.Println("ch2:", msg)
case <-timeout:
fmt.Println("超时!")
return
}
}
}
// ─── Context 控制取消 ─────────────────────────────────────────
func contextDemo() {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
result := make(chan string, 1)
go func() {
// 模拟耗时操作
select {
case <-time.After(2 * time.Second): // 2秒后完成
result <- "操作完成"
case <-ctx.Done(): // 超时或取消
fmt.Println("操作被取消:", ctx.Err())
}
}()
select {
case r := <-result:
fmt.Println(r)
case <-ctx.Done():
fmt.Println("主协程超时")
}
}
func main() {
basicGoroutine()
channelDemo()
selectDemo()
}
6. 并发安全:Mutex 与 sync 包
package main
import (
"fmt"
"sync"
"sync/atomic"
)
// ─── 互斥锁 ───────────────────────────────────────────────────
type SafeCounter struct {
mu sync.RWMutex
v map[string]int
}
func (c *SafeCounter) Inc(key string) {
c.mu.Lock() // 写锁
defer c.mu.Unlock()
c.v[key]++
}
func (c *SafeCounter) Value(key string) int {
c.mu.RLock() // 读锁 (允许并发读)
defer c.mu.RUnlock()
return c.v[key]
}
// ─── atomic 原子操作 (比 Mutex 更快) ─────────────────────────
var requestCount atomic.Int64
func handleRequest() {
requestCount.Add(1)
}
// ─── sync.Once 单例初始化 ──────────────────────────────────────
var (
instance *Database
once sync.Once
)
func GetDB() *Database {
once.Do(func() { // 保证只执行一次,即使并发调用
instance = &Database{conn: "initialized"}
fmt.Println("数据库连接初始化")
})
return instance
}
// ─── sync.Pool 对象复用 ───────────────────────────────────────
var bufferPool = sync.Pool{
New: func() any {
return make([]byte, 0, 4096) // 初始 4KB buffer
},
}
func processRequest(data []byte) {
buf := bufferPool.Get().([]byte) // 从 pool 取
defer func() {
buf = buf[:0] // 清空但保留容量
bufferPool.Put(buf) // 还回 pool
}()
buf = append(buf, data...)
// 处理 buf...
}
type Database struct{ conn string }
func main() {
counter := &SafeCounter{v: make(map[string]int)}
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
counter.Inc("requests")
}()
}
wg.Wait()
fmt.Println("最终计数:", counter.Value("requests")) // 100
}
7. 接口与泛型
package main
import "fmt"
// ─── 接口:Go 的多态核心 ──────────────────────────────────────
type Cache interface {
Get(key string) (any, bool)
Set(key string, value any)
Delete(key string)
}
// 内存缓存实现
type MemCache struct {
data map[string]any
mu sync.RWMutex
}
func (m *MemCache) Get(key string) (any, bool) {
m.mu.RLock()
defer m.mu.RUnlock()
v, ok := m.data[key]
return v, ok
}
func (m *MemCache) Set(key string, value any) {
m.mu.Lock()
defer m.mu.Unlock()
m.data[key] = value
}
func (m *MemCache) Delete(key string) {
m.mu.Lock()
defer m.mu.Unlock()
delete(m.data, key)
}
// 使用接口,不依赖具体实现 (依赖倒置)
type UserService struct {
cache Cache // 可以换成 RedisCache, MemCache...
}
// ─── 泛型 (Go 1.18+) ──────────────────────────────────────────
// 泛型函数
func Map[T, U any](s []T, fn func(T) U) []U {
result := make([]U, len(s))
for i, v := range s {
result[i] = fn(v)
}
return result
}
func Filter[T any](s []T, fn func(T) bool) []T {
var result []T
for _, v := range s {
if fn(v) {
result = append(result, v)
}
}
return result
}
// 泛型类型约束
type Number interface {
~int | ~int64 | ~float32 | ~float64
}
func Sum[T Number](nums []T) T {
var total T
for _, n := range nums {
total += n
}
return total
}
// 泛型结构体:类型安全的结果类型
type Result[T any] struct {
Data T
Error error
}
func (r Result[T]) Unwrap() T {
if r.Error != nil {
panic(r.Error)
}
return r.Data
}
func main() {
nums := []int{1, 2, 3, 4, 5}
doubled := Map(nums, func(n int) int { return n * 2 })
evens := Filter(nums, func(n int) bool { return n%2 == 0 })
total := Sum(nums)
fmt.Println("doubled:", doubled) // [2 4 6 8 10]
fmt.Println("evens:", evens) // [2 4]
fmt.Println("sum:", total) // 15
}
8. 构建完整 HTTP 服务
整合以上所有知识,构建一个带有中间件、优雅关机、配置管理的完整 HTTP 服务。
package main
import (
"context"
"encoding/json"
"fmt"
"log/slog"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
// ─── 配置 ─────────────────────────────────────────────────────
type Config struct {
Port string
ReadTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
}
func defaultConfig() Config {
return Config{
Port: getEnv("PORT", "8080"),
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 30 * time.Second,
}
}
func getEnv(key, defaultVal string) string {
if v := os.Getenv(key); v != "" {
return v
}
return defaultVal
}
// ─── 响应助手 ─────────────────────────────────────────────────
type APIResponse struct {
Success bool `json:"success"`
Data any `json:"data,omitempty"`
Error string `json:"error,omitempty"`
}
func writeJSON(w http.ResponseWriter, status int, data any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(data)
}
func writeError(w http.ResponseWriter, status int, msg string) {
writeJSON(w, status, APIResponse{Success: false, Error: msg})
}
// ─── 中间件 ───────────────────────────────────────────────────
func loggingMiddleware(logger *slog.Logger) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
rw := &responseWriter{ResponseWriter: w, status: 200}
next.ServeHTTP(rw, r)
logger.Info("request",
"method", r.Method,
"path", r.URL.Path,
"status", rw.status,
"duration", time.Since(start).String(),
"ip", r.RemoteAddr,
)
})
}
}
type responseWriter struct {
http.ResponseWriter
status int
}
func (rw *responseWriter) WriteHeader(status int) {
rw.status = status
rw.ResponseWriter.WriteHeader(status)
}
func corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusNoContent)
return
}
next.ServeHTTP(w, r)
})
}
// ─── 处理器 ───────────────────────────────────────────────────
func healthHandler(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, map[string]any{
"status": "healthy",
"time": time.Now().Format(time.RFC3339),
})
}
// ─── 主函数:优雅关机 ─────────────────────────────────────────
func main() {
cfg := defaultConfig()
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
mux := http.NewServeMux()
mux.HandleFunc("GET /health", healthHandler)
mux.HandleFunc("GET /api/v1/users", func(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, APIResponse{
Success: true,
Data: []map[string]any{{"id": 1, "name": "Alice"}},
})
})
// 组合中间件
var handler http.Handler = mux
handler = loggingMiddleware(logger)(handler)
handler = corsMiddleware(handler)
server := &http.Server{
Addr: ":" + cfg.Port,
Handler: handler,
ReadTimeout: cfg.ReadTimeout,
WriteTimeout: cfg.WriteTimeout,
IdleTimeout: cfg.IdleTimeout,
}
// 在 goroutine 中启动服务
go func() {
logger.Info("服务器启动", "port", cfg.Port)
if err := server.ListenAndServe(); err != http.ErrServerClosed {
logger.Error("服务器错误", "err", err)
os.Exit(1)
}
}()
// 等待中断信号 (Ctrl+C 或 kill)
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
// 优雅关机:等待进行中的请求完成 (最多30秒)
logger.Info("正在关机...")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
logger.Error("强制关机", "err", err)
}
logger.Info("服务器已关闭")
}
Go 最佳实践速查
① 永远处理错误,不用 _ 丢弃 ② 接口应当小,单一职责 ③ 不要过早优化并发 ④ 用 context 传递超时/取消 ⑤ 日志用结构化(slog)