← 返回首页

社区库

Go 语言拥有活跃的社区和丰富的第三方库,涵盖 Web 开发、数据库、云原生等领域。

🌐

Web 框架

Gin、Echo、Fiber

🗄️

ORM

GORM、XORM

⚙️

配置

Viper

📝

日志

Zap、logrus

🧪

测试

Testify、gomock

☁️

云原生

gRPC、K8s 客户端

Go 语言拥有活跃的社区和丰富的第三方库。本章将介绍 Go 生态中最流行的几个库:Gin(Web 框架)、GORM(ORM)、Viper(配置)和 Zap(日志)。

Gin Web 框架

简介

Gin 是一个用 Go 语言编写的高性能 HTTP Web 框架,具有类似 Martini 的 API,但性能更好(高达 40 倍)。它提供了路由、中间件、JSON 验证等功能,是 Go 社区最流行的 Web 框架之一。

安装

go get -u github.com/gin-gonic/gin

快速开始

package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

func main() {
    // 创建默认路由
    r := gin.Default()

    // 定义路由
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "message": "pong",
        })
    })

    // 启动服务器
    r.Run(":8080")
}

路由

package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

func main() {
    r := gin.Default()

    // GET 请求
    r.GET("/users", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "users": []string{"Alice", "Bob"},
        })
    })

    // POST 请求
    r.POST("/users", func(c *gin.Context) {
        var user struct {
            Name  string `json:"name"`
            Email string `json:"email"`
        }
        
        if err := c.ShouldBindJSON(&user); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }
        
        c.JSON(http.StatusCreated, gin.H{
            "message": "User created",
            "user":    user,
        })
    })

    // 路径参数
    r.GET("/users/:id", func(c *gin.Context) {
        id := c.Param("id")
        c.JSON(http.StatusOK, gin.H{"id": id})
    })

    // 查询参数
    r.GET("/search", func(c *gin.Context) {
        query := c.Query("q")
        page := c.DefaultQuery("page", "1")
        c.JSON(http.StatusOK, gin.H{
            "query": query,
            "page":  page,
        })
    })

    r.Run(":8080")
}

中间件

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "net/http"
    "time"
)

// 自定义中间件
func Logger() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        
        c.Next()
        
        latency := time.Since(start)
        fmt.Printf("%s %s %v\n", c.Request.Method, c.Request.URL.Path, latency)
    }
}

// 认证中间件
func AuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.GetHeader("Authorization")
        
        if token == "" {
            c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
            c.Abort()
            return
        }
        
        c.Set("user_id", "123")
        c.Next()
    }
}

func main() {
    r := gin.Default()
    
    // 全局中间件
    r.Use(Logger())
    
    // 路由组
    api := r.Group("/api")
    api.Use(AuthMiddleware())
    
    api.GET("/profile", func(c *gin.Context) {
        userID := c.GetString("user_id")
        c.JSON(http.StatusOK, gin.H{"user_id": userID})
    })
    
    r.Run(":8080")
}

路由组

package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

func main() {
    r := gin.Default()
    
    // v1 路由组
    v1 := r.Group("/v1")
    {
        v1.GET("/users", func(c *gin.Context) {
            c.String(http.StatusOK, "v1 users")
        })
        v1.GET("/posts", func(c *gin.Context) {
            c.String(http.StatusOK, "v1 posts")
        })
    }
    
    // v2 路由组
    v2 := r.Group("/v2")
    {
        v2.GET("/users", func(c *gin.Context) {
            c.String(http.StatusOK, "v2 users")
        })
        v2.GET("/posts", func(c *gin.Context) {
            c.String(http.StatusOK, "v2 posts")
        })
    }
    
    r.Run(":8080")
}

错误处理

package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

func main() {
    r := gin.Default()
    
    // 自定义错误处理
    r.NoRoute(func(c *gin.Context) {
        c.JSON(http.StatusNotFound, gin.H{
            "error": "Route not found",
        })
    })
    
    r.NoMethod(func(c *gin.Context) {
        c.JSON(http.StatusMethodNotAllowed, gin.H{
            "error": "Method not allowed",
        })
    })
    
    // Panic 恢复
    r.Use(gin.Recovery())
    
    r.GET("/panic", func(c *gin.Context) {
        panic("Something went wrong")
    })
    
    r.Run(":8080")
}

GORM ORM

简介

GORM 是 Go 语言中最流行的 ORM(对象关系映射)库,提供了友好的 API,支持多种数据库(MySQL、PostgreSQL、SQLite、SQL Server 等),并包含自动迁移、关联、钩子等功能。

安装

go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql

快速开始

package main

import (
    "fmt"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

type User struct {
    ID       uint   `gorm:"primaryKey"`
    Name     string `gorm:"size:255;not null"`
    Email    string `gorm:"size:255;uniqueIndex;not null"`
    Age      int    `gorm:"default:0"`
    CreatedAt time.Time
    UpdatedAt time.Time
}

func main() {
    // 连接数据库
    dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        panic("Failed to connect database")
    }
    
    // 自动迁移
    db.AutoMigrate(&User{})
    
    // 创建记录
    user := User{Name: "Alice", Email: "alice@example.com", Age: 25}
    result := db.Create(&user)
    fmt.Println("Created user ID:", user.ID)
    fmt.Println("Rows affected:", result.RowsAffected)
}

创建记录

// 创建单条记录
user := User{Name: "Bob", Email: "bob@example.com", Age: 30}
db.Create(&user)

// 创建多条记录
users := []User{
    {Name: "Charlie", Email: "charlie@example.com", Age: 28},
    {Name: "David", Email: "david@example.com", Age: 32},
}
db.Create(&users)

// 使用 Select 指定字段
db.Select("Name", "Email").Create(&user)

// 使用 Omit 忽略字段
db.Omit("Age").Create(&user)

查询记录

// 查询第一条记录
var user User
db.First(&user)
// SELECT * FROM users ORDER BY id LIMIT 1;

// 根据主键查询
db.First(&user, 1)
// SELECT * FROM users WHERE id = 1;

// 查询所有记录
var users []User
db.Find(&users)
// SELECT * FROM users;

// 条件查询
db.Where("name = ?", "Alice").First(&user)
db.Where("age > ?", 25).Find(&users)
db.Where("name LIKE ?", "%A%").Find(&users)

// 多条件
db.Where("name = ? AND age > ?", "Alice", 20).First(&user)

// 使用 Map
db.Where(map[string]interface{}{"name": "Alice", "age": 25}).First(&user)

// 使用 Struct
db.Where(User{Name: "Alice", Age: 25}).First(&user)

// Or 条件
db.Where("name = ?", "Alice").Or("name = ?", "Bob").Find(&users)

// Not 条件
db.Not("name = ?", "Alice").Find(&users)

// 排序
db.Order("age desc").Find(&users)

// 限制和偏移
db.Limit(10).Offset(20).Find(&users)

// 统计
var count int64
db.Model(&User{}).Count(&count)

// 选择特定字段
db.Select("name", "email").Find(&users)

更新记录

// 更新单个字段
db.Model(&user).Update("age", 26)

// 更新多个字段
db.Model(&user).Updates(map[string]interface{}{
    "name": "Alice Updated",
    "age":  26,
})

// 使用 Struct 更新
db.Model(&user).Updates(User{Name: "Alice Updated", Age: 26})

// 只更新非零字段
db.Model(&user).Updates(User{Name: "Alice"})

// 更新所有字段(包括零值)
db.Model(&user).Select("*").Updates(User{Name: "", Age: 0})

// 条件更新
db.Model(&User{}).Where("age > ?", 20).Update("status", "active")

// 批量更新
db.Model(&User{}).Where("age > ?", 20).Updates(map[string]interface{}{"status": "active"})

删除记录

// 删除记录
db.Delete(&user)
// DELETE FROM users WHERE id = 1;

// 根据主键删除
db.Delete(&User{}, 1)

// 批量删除
db.Where("age < ?", 18).Delete(&User{})

// 软删除
type User struct {
    ID        uint
    Name      string
    DeletedAt gorm.DeletedAt `gorm:"index"`
}
// 软删除不会真正删除记录,而是设置 deleted_at 字段

// 永久删除
db.Unscoped().Delete(&user)

关联

type User struct {
    gorm.Model
    Name   string
    Posts  []Post
}

type Post struct {
    gorm.Model
    Title   string
    UserID  uint
    User    User
}

// 预加载关联
var users []User
db.Preload("Posts").Find(&users)

// 创建关联
user := User{Name: "Alice"}
db.Create(&user)
db.Model(&user).Association("Posts").Append([]Post{
    {Title: "Post 1"},
    {Title: "Post 2"},
})

// 查找关联
var posts []Post
db.Model(&user).Association("Posts").Find(&posts)

// 替换关联
db.Model(&user).Association("Posts").Replace([]Post{
    {Title: "New Post 1"},
})

// 删除关联
db.Model(&user).Association("Posts").Delete(posts)

// 清空关联
db.Model(&user).Association("Posts").Clear()

钩子

type User struct {
    gorm.Model
    Name  string
    Email string
}

// 创建前钩子
func (u *User) BeforeCreate(tx *gorm.DB) error {
    fmt.Println("Before create:", u.Name)
    return nil
}

// 创建后钩子
func (u *User) AfterCreate(tx *gorm.DB) error {
    fmt.Println("After create:", u.ID)
    return nil
}

// 更新前钩子
func (u *User) BeforeUpdate(tx *gorm.DB) error {
    fmt.Println("Before update:", u.Name)
    return nil
}

// 查询前钩子
func (u *User) AfterFind(tx *gorm.DB) error {
    fmt.Println("After find:", u.Name)
    return nil
}

事务

// 自动事务
err := db.Transaction(func(tx *gorm.DB) error {
    // 创建用户
    if err := tx.Create(&User{Name: "Alice"}).Error; err != nil {
        return err
    }
    
    // 创建订单
    if err := tx.Create(&Order{UserID: 1}).Error; err != nil {
        return err
    }
    
    return nil
})

// 手动事务
tx := db.Begin()

if err := tx.Create(&User{Name: "Bob"}).Error; err != nil {
    tx.Rollback()
}

if err := tx.Commit().Error; err != nil {
    tx.Rollback()
}

Viper 配置

简介

Viper 是 Go 语言的配置管理库,支持多种配置格式(JSON、TOML、YAML、HCL、envfile 等),可以从文件、环境变量、命令行参数等多种来源读取配置,并支持配置热重载。

安装

go get github.com/spf13/viper

快速开始

package main

import (
    "fmt"
    "github.com/spf13/viper"
)

func main() {
    v := viper.New()
    
    // 设置配置文件名和路径
    v.SetConfigName("config")
    v.SetConfigType("yaml")
    v.AddConfigPath("./")
    
    // 读取配置文件
    if err := v.ReadInConfig(); err != nil {
        panic(fmt.Errorf("Failed to read config file: %s", err))
    }
    
    // 获取配置值
    fmt.Println("Server Port:", v.GetString("server.port"))
    fmt.Println("Database Host:", v.GetString("database.host"))
}

配置文件示例

# config.yaml
server:
  port: 8080
  host: localhost

database:
  host: localhost
  port: 3306
  name: mydb
  user: root
  password: secret

logging:
  level: info
  format: json

读取配置

// 获取字符串
port := v.GetString("server.port")

// 获取整数
port := v.GetInt("server.port")

// 获取布尔值
debug := v.GetBool("debug")

// 获取浮点数
timeout := v.GetFloat64("timeout")

// 获取时间
duration := v.GetDuration("timeout")

// 获取字符串切片
hosts := v.GetStringSlice("servers.hosts")

// 获取所有配置
all := v.AllSettings()

// 检查键是否存在
if v.IsSet("server.port") {
    port := v.GetString("server.port")
}

// 获取子配置
server := v.Sub("server")
port := server.GetString("port")

环境变量

package main

import (
    "fmt"
    "github.com/spf13/viper"
)

func main() {
    v := viper.New()
    
    // 绑定环境变量
    v.BindEnv("server.port", "SERVER_PORT")
    v.BindEnv("database.host", "DB_HOST")
    
    // 自动绑定环境变量(使用前缀)
    v.SetEnvPrefix("APP")
    v.AutomaticEnv()
    // APP_SERVER_PORT -> server.port
    
    // 设置默认值
    v.SetDefault("server.port", 8080)
    v.SetDefault("debug", false)
    
    // 读取环境变量
    port := v.GetString("server.port")
    fmt.Println("Port:", port)
}

命令行参数

package main

import (
    "fmt"
    "github.com/spf13/cobra"
    "github.com/spf13/viper"
)

var rootCmd = &cobra.Command{
    Run: func(cmd *cobra.Command, args []string) {
        port := viper.GetInt("port")
        fmt.Println("Port:", port)
    },
}

func init() {
    rootCmd.PersistentFlags().Int("port", 8080, "Server port")
    viper.BindPFlag("port", rootCmd.PersistentFlags().Lookup("port"))
}

func main() {
    cobra.Execute()
}

配置热重载

package main

import (
    "fmt"
    "log"
    "github.com/fsnotify/fsnotify"
    "github.com/spf13/viper"
)

func main() {
    v := viper.New()
    v.SetConfigName("config")
    v.SetConfigType("yaml")
    v.AddConfigPath("./")
    
    if err := v.ReadInConfig(); err != nil {
        panic(err)
    }
    
    // 监听配置文件变化
    v.OnConfigChange(func(e fsnotify.Event) {
        fmt.Printf("Config file changed: %s\n", e.Name)
        log.Println("Reloading config...")
    })
    
    v.WatchConfig()
    
    // 主循环
    select {}
}

多配置源

package main

import (
    "fmt"
    "github.com/spf13/viper"
)

func main() {
    v := viper.New()
    
    // 1. 设置默认值
    v.SetDefault("server.port", 8080)
    
    // 2. 读取配置文件
    v.SetConfigName("config")
    v.SetConfigType("yaml")
    v.AddConfigPath("./")
    v.ReadInConfig()
    
    // 3. 读取环境变量
    v.SetEnvPrefix("APP")
    v.AutomaticEnv()
    
    // 4. 读取命令行参数
    // 使用 cobra 绑定
    
    // 优先级:命令行参数 > 环境变量 > 配置文件 > 默认值
    port := v.GetInt("server.port")
    fmt.Println("Port:", port)
}

Zap 日志

简介

Zap 是 Uber 开发的高性能、结构化日志库,专为 Go 语言设计。它提供了零内存分配的日志记录和结构化日志支持,性能远超标准库的 log 包和其他日志库。

安装

go get -u go.uber.org/zap
go get -u go.uber.org/zap/zapcore

快速开始

package main

import (
    "go.uber.org/zap"
)

func main() {
    // 创建开发环境 logger
    logger, _ := zap.NewDevelopment()
    defer logger.Sync()
    
    // 记录日志
    logger.Info("Hello, World!")
    logger.Debug("Debug message")
    logger.Warn("Warning message")
    logger.Error("Error message")
}

生产环境 Logger

package main

import (
    "go.uber.org/zap"
)

func main() {
    // 创建生产环境 logger
    logger, _ := zap.NewProduction()
    defer logger.Sync()
    
    // 记录日志
    logger.Info("Server started",
        zap.String("host", "localhost"),
        zap.Int("port", 8080),
    )
}

自定义 Logger

package main

import (
    "os"
    
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
)

func main() {
    // 自定义配置
    config := zap.Config{
        Level:            zap.NewAtomicLevelAt(zapcore.InfoLevel),
        Development:      false,
        Encoding:         "json",
        EncoderConfig: zapcore.EncoderConfig{
            TimeKey:        "timestamp",
            LevelKey:       "level",
            NameKey:        "logger",
            CallerKey:      "caller",
            MessageKey:     "msg",
            StacktraceKey:  "stacktrace",
            LineEnding:     zapcore.DefaultLineEnding,
            EncodeLevel:    zapcore.LowercaseLevelEncoder,
            EncodeTime:     zapcore.ISO8601TimeEncoder,
            EncodeDuration: zapcore.SecondsDurationEncoder,
            EncodeCaller:   zapcore.ShortCallerEncoder,
        },
        OutputPaths:      []string{"stdout"},
        ErrorOutputPaths: []string{"stderr"},
    }
    
    logger, _ := config.Build()
    defer logger.Sync()
    
    logger.Info("Custom logger initialized")
}

结构化日志

package main

import (
    "go.uber.org/zap"
)

func main() {
    logger, _ := zap.NewProduction()
    defer logger.Sync()
    
    // 添加字段
    logger.Info("User logged in",
        zap.String("user_id", "123"),
        zap.String("username", "alice"),
        zap.String("ip", "192.168.1.1"),
    )
    
    // 使用结构化对象
    type User struct {
        ID    string
        Name  string
        Email string
    }
    
    user := User{ID: "123", Name: "Alice", Email: "alice@example.com"}
    logger.Info("User created", zap.Any("user", user))
}

日志级别

package main

import (
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
)

func main() {
    // 设置日志级别
    config := zap.NewProductionConfig()
    config.Level = zap.NewAtomicLevelAt(zapcore.DebugLevel)
    
    logger, _ := config.Build(zap.AddCaller())
    defer logger.Sync()
    
    // 不同级别的日志
    logger.Debug("Debug information")
    logger.Info("Information")
    logger.Warn("Warning")
    logger.Error("Error occurred")
    
    // Fatal 会调用 os.Exit(1)
    // logger.Fatal("Fatal error")
    
    // Panic 会调用 panic()
    // logger.Panic("Panic situation")
}

输出到文件

package main

import (
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
    "gopkg.in/natefinch/lumberjack.v2"
)

func main() {
    // 日志轮转配置
    writer := &lumberjack.Logger{
        Filename:   "logs/app.log",
        MaxSize:    100, // MB
        MaxBackups: 3,
        MaxAge:     28,   // days
        Compress:   true,
    }
    
    // 创建 core
    core := zapcore.NewCore(
        zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
        zapcore.AddSync(writer),
        zapcore.InfoLevel,
    )
    
    logger := zap.New(core, zap.AddCaller())
    defer logger.Sync()
    
    logger.Info("Logging to file")
}

同时输出到多个地方

package main

import (
    "os"
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
    "gopkg.in/natefinch/lumberjack.v2"
)

func main() {
    // 文件输出
    fileWriter := &lumberjack.Logger{
        Filename:   "logs/app.log",
        MaxSize:    100,
        MaxBackups: 3,
        MaxAge:     28,
        Compress:   true,
    }
    
    // 控制台输出
    consoleWriter := zapcore.AddSync(os.Stdout)
    
    // 创建 encoder
    encoderConfig := zap.NewProductionEncoderConfig()
    encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
    encoder := zapcore.NewJSONEncoder(encoderConfig)
    
    // 创建 core
    core := zapcore.NewTee(
        zapcore.NewCore(encoder, zapcore.AddSync(fileWriter), zapcore.InfoLevel),
        zapcore.NewCore(encoder, consoleWriter, zapcore.DebugLevel),
    )
    
    logger := zap.New(core, zap.AddCaller())
    defer logger.Sync()
    
    logger.Info("Logging to both file and console")
}

Sugar Logger

package main

import (
    "go.uber.org/zap"
)

func main() {
    logger, _ := zap.NewProduction()
    defer logger.Sync()
    
    // Sugar logger 提供更方便的 API
    sugar := logger.Sugar()
    
    // Printf 风格
    sugar.Infof("User %s logged in from %s", "alice", "192.168.1.1")
    
    // 惯用风格
    sugar.Infow("User logged in",
        "user", "alice",
        "ip", "192.168.1.1",
    )
    
    // 简单风格
    sugar.Info("Simple log message", "arg1", "arg2")
}

错误处理

package main

import (
    "errors"
    "go.uber.org/zap"
)

func main() {
    logger, _ := zap.NewProduction()
    defer logger.Sync()
    
    // 记录错误
    err := errors.New("something went wrong")
    logger.Error("Operation failed",
        zap.Error(err),
        zap.String("operation", "create_user"),
    )
    
    // 记录堆栈
    logger.Error("Error with stack",
        zap.Error(err),
        zap.Stack("stack"),
    )
}

最佳实践

Gin 最佳实践

1. 项目结构

project/
├── cmd/
│   └── server/
│       └── main.go
├── internal/
│   ├── handler/
│   ├── service/
│   ├── repository/
│   └── model/
├── pkg/
├── configs/
└── go.mod

2. 使用依赖注入

type Server struct {
    router *gin.Engine
    userHandler *UserHandler
}

func NewServer(userHandler *UserHandler) *Server {
    s := &Server{
        router: gin.Default(),
        userHandler: userHandler,
    }
    s.setupRoutes()
    return s
}

3. 统一错误处理

type ErrorResponse struct {
    Error   string `json:"error"`
    Code    int    `json:"code"`
    Message string `json:"message"`
}

func HandleError(c *gin.Context, err error) {
    if errors.Is(err, ErrNotFound) {
        c.JSON(http.StatusNotFound, ErrorResponse{
            Error:   "not_found",
            Code:    404,
            Message: err.Error(),
        })
        return
    }
    c.JSON(http.StatusInternalServerError, ErrorResponse{
        Error:   "internal_error",
        Code:    500,
        Message: "Internal server error",
    })
}

4. 使用 Context 传递请求作用域数据

// 在中间件中设置
c.Set("user_id", userID)

// 在 handler 中获取
userID := c.GetString("user_id")

GORM 最佳实践

1. 使用模型层抽象

type Repository interface {
    Create(user *User) error
    FindByID(id uint) (*User, error)
    Update(user *User) error
    Delete(id uint) error
}

type UserRepository struct {
    db *gorm.DB
}

func (r *UserRepository) FindByID(id uint) (*User, error) {
    var user User
    err := r.db.First(&user, id).Error
    return &user, err
}

2. 使用事务管理

func (s *UserService) CreateUserWithOrder(user *User, order *Order) error {
    return s.db.Transaction(func(tx *gorm.DB) error {
        if err := tx.Create(user).Error; err != nil {
            return err
        }
        order.UserID = user.ID
        return tx.Create(order).Error
    })
}

3. 使用软删除

type BaseModel struct {
    ID        uint
    CreatedAt time.Time
    UpdatedAt time.Time
    DeletedAt gorm.DeletedAt `gorm:"index"`
}

type User struct {
    BaseModel
    Name  string
    Email string
}

4. 使用预加载避免 N+1 查询

// 错误:N+1 查询
var users []User
db.Find(&users)
for _, user := range users {
    db.Where("user_id = ?", user.ID).Find(&user.Posts)
}

// 正确:使用预加载
db.Preload("Posts").Find(&users)

Viper 最佳实践

1. 配置优先级

// 优先级从高到低
// 1. 命令行参数
// 2. 环境变量
// 3. 配置文件
// 4. 默认值

2. 使用配置验证

func ValidateConfig(v *viper.Viper) error {
    if !v.IsSet("server.port") {
        return errors.New("server.port is required")
    }
    if !v.IsSet("database.host") {
        return errors.New("database.host is required")
    }
    return nil
}

3. 敏感信息使用环境变量

// config.yaml
database:
  host: localhost
  port: 3306
  name: mydb
  user: root
  password: ${DB_PASSWORD}  // 从环境变量读取

Zap 最佳实践

1. 使用全局 Logger

var logger *zap.Logger

func InitLogger(config *Config) error {
    var err error
    logger, err = config.BuildLogger()
    return err
}

func GetLogger() *zap.Logger {
    return logger
}

2. 使用结构化日志

// 好的实践
logger.Info("User logged in",
    zap.String("user_id", userID),
    zap.String("ip", ip),
)

// 不好的实践
logger.Info(fmt.Sprintf("User %s logged in from %s", userID, ip))

3. 日志级别控制

// 开发环境
config.Level = zap.NewAtomicLevelAt(zapcore.DebugLevel)

// 生产环境
config.Level = zap.NewAtomicLevelAt(zapcore.InfoLevel)

4. 日志轮转

// 使用 lumberjack 进行日志轮转
writer := &lumberjack.Logger{
    Filename:   "logs/app.log",
    MaxSize:    100, // MB
    MaxBackups: 3,
    MaxAge:     28,   // days
    Compress:   true,
}

集成示例

package main

import (
    "github.com/gin-gonic/gin"
    "github.com/spf13/viper"
    "go.uber.org/zap"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

type App struct {
    Router *gin.Engine
    DB     *gorm.DB
    Config *viper.Viper
    Logger *zap.Logger
}

func NewApp() *App {
    // 初始化配置
    config := viper.New()
    config.SetConfigName("config")
    config.SetConfigType("yaml")
    config.AddConfigPath("./")
    config.ReadInConfig()
    
    // 初始化日志
    logger, _ := zap.NewProduction()
    
    // 初始化数据库
    dsn := config.GetString("database.dsn")
    db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    
    // 初始化路由
    router := gin.Default()
    
    return &App{
        Router: router,
        DB:     db,
        Config: config,
        Logger: logger,
    }
}

func (a *App) Run() {
    port := a.Config.GetString("server.port")
    a.Logger.Info("Starting server", zap.String("port", port))
    a.Router.Run(":" + port)
}

func main() {
    app := NewApp()
    app.Run()
}

Validator 参数校验

简介

Validator 是 Go 语言中最流行的参数校验库,基于结构体标签实现,支持多种校验规则,可以轻松集成到 Gin 等 Web 框架中。它支持自定义校验规则、国际化错误消息等功能。

安装

go get -u github.com/go-playground/validator/v10

快速开始

package main

import (
    "fmt"
    "github.com/go-playground/validator/v10"
)

type User struct {
    Name  string `validate:"required,min=3,max=32"`
    Email string `validate:"required,email"`
    Age   int    `validate:"required,gte=0,lte=130"`
}

func main() {
    validate := validator.New()
    
    user := User{
        Name:  "Alice",
        Email: "alice@example.com",
        Age:   25,
    }
    
    err := validate.Struct(user)
    if err != nil {
        fmt.Println("Validation failed:", err)
        return
    }
    
    fmt.Println("Validation passed!")
}

常用校验规则

type User struct {
    // 必填字段
    Name string `validate:"required"`
    
    // 字符串长度
    Username string `validate:"min=3,max=20"`
    
    // 邮箱格式
    Email string `validate:"email"`
    
    // URL 格式
    Website string `validate:"url"`
    
    // 数值范围
    Age int `validate:"gte=0,lte=130"`
    
    // 大于等于 (gte)、大于 (gt)、小于等于 (lte)、小于 (lt)
    Price float64 `validate:"gt=0"`
    
    // 正则表达式
    Phone string `validate:"required,^1[3-9]\\d{9}$"`
    
    // 枚举值
    Gender string `validate:"oneof=male female other"`
    
    // 字符串包含
    Password string `validate:"containsany=!@#$%"`
    
    // 唯一性(需要自定义校验)
    Username string `validate:"unique"`
    
    // 可选字段
    Nickname string `validate:"omitempty,min=2"`
    
    // 排除某些值
    Role string `validate:"nefield=Password"`
    
    // 字段相等
    ConfirmPassword string `validate:"eqfield=Password"`
    
    // 字段不等
    NewEmail string `validate:"nefield=OldEmail"`
}

错误处理

package main

import (
    "fmt"
    "github.com/go-playground/validator/v10"
)

func handleValidationError(err error) {
    // 类型断言
    validationErrors, ok := err.(validator.ValidationErrors)
    if !ok {
        fmt.Println("Unknown error:", err)
        return
    }
    
    // 遍历所有错误
    for, e := range validationErrors {
        fmt.Printf("Field: %s, Tag: %s, Param: %s\n", e.Field(), e.Tag(), e.Param())
    }
}

// 自定义错误消息
type ErrorResponse struct {
    Field   string `json:"field"`
    Message string `json:"message"`
}

func getValidationErrors(err error) []ErrorResponse {
    var errors []ErrorResponse
    
    validationErrors := err.(validator.ValidationErrors)
    for, e := range validationErrors {
        errors = append(errors, ErrorResponse{
            Field:   e.Field(),
            Message: getErrorMessage(e),
        })
    }
    
    return errors
}

func getErrorMessage(e validator.FieldError) string {
    switch e.Tag() {
    case "required":
        return fmt.Sprintf("%s is required", e.Field())
    case "email":
        return fmt.Sprintf("%s must be a valid email", e.Field())
    case "min":
        return fmt.Sprintf("%s must be at least %s characters", e.Field(), e.Param())
    case "max":
        return fmt.Sprintf("%s must be at most %s characters", e.Field(), e.Param())
    default:
        return fmt.Sprintf("%s is invalid", e.Field())
    }
}

自定义校验规则

package main

import (
    "fmt"
    "github.com/go-playground/validator/v10"
    "regexp"
)

// 自定义校验:手机号
func validateMobile(fl validator.FieldLevel) bool {
    mobile := fl.Field().String()
    matched, _ := regexp.MatchString(`^1[3-9]\d{9}$`, mobile)
    return matched
}

// 自定义校验:用户名唯一性
func validateUsernameUnique(fl validator.FieldLevel) bool {
    username := fl.Field().String()
    // 这里应该查询数据库检查用户名是否已存在
    // 简化示例:假设用户名不能是 "admin"
    return username != "admin"
}

func main() {
    validate := validator.New()
    
    // 注册自定义校验
    validate.RegisterValidation("mobile", validateMobile)
    validate.RegisterValidation("username_unique", validateUsernameUnique)
    
    type User struct {
        Username string `validate:"required,username_unique"`
        Mobile   string `validate:"required,mobile"`
    }
    
    user := User{
        Username: "admin",
        Mobile:   "13800138000",
    }
    
    err := validate.Struct(user)
    if err != nil {
        fmt.Println("Validation failed:", err)
    }
}

与 Gin 集成

package main

import (
    "github.com/gin-gonic/gin"
    "github.com/go-playground/validator/v10"
    "net/http"
)

type CreateUserRequest struct {
    Name     string `json:"name" validate:"required,min=3,max=32"`
    Email    string `json:"email" validate:"required,email"`
    Password string `json:"password" validate:"required,min=8"`
}

func main() {
    r := gin.Default()
    
    // 获取 Gin 的 validator
    if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
        // 注册自定义校验
        v.RegisterValidation("mobile", validateMobile)
    }
    
    r.POST("/users", func(c *gin.Context) {
        var req CreateUserRequest
        
        // 绑定和校验
        if err := c.ShouldBindJSON(&req); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{
                "error": getValidationErrors(err),
            })
            return
        }
        
        c.JSON(http.StatusCreated, gin.H{
            "message": "User created",
            "user":    req,
        })
    })
    
    r.Run(":8080")
}

结构体嵌套校验

type Address struct {
    Street  string `validate:"required"`
    City    string `validate:"required"`
    ZipCode string `validate:"required,len=6"`
}

type User struct {
    Name    string  `validate:"required"`
    Address Address  `validate:"required,dive"`  // dive 表示校验嵌套结构体
}

数组/切片校验

type Order struct {
    Items []Item `validate:"required,min=1,dive"`  // 校验数组中的每个元素
}

type Item struct {
    Name  string  `validate:"required"`
    Price float64 `validate:"required,gt=0"`
}

Cobra 命令行工具

简介

Cobra 是 Go 语言中最流行的命令行应用框架,被广泛用于许多知名项目(如 Docker、Kubernetes、Hugo 等)。它提供了强大的命令解析、子命令支持、自动生成帮助文档等功能。

安装

go get -u github.com/spf13/cobra
go get -u github.com/spf13/pflag

快速开始

package main

import (
    "fmt"
    "github.com/spf13/cobra"
)

func main() {
    var name string
    
    rootCmd := &cobra.Command{
        Use:   "myapp",
        Short: "My application",
        Long:  "A longer description of my application",
        Run: func(cmd *cobra.Command, args []string) {
            fmt.Printf("Hello, %s!\n", name)
        },
    }
    
    rootCmd.Flags().StringVarP(&name, "name", "n", "World", "Name to greet")
    
    rootCmd.Execute()
}

子命令

package main

import (
    "fmt"
    "github.com/spf13/cobra"
)

func main() {
    rootCmd := &cobra.Command{
        Use:   "myapp",
        Short: "My application",
    }
    
    // 添加子命令
    rootCmd.AddCommand(createCommand())
    rootCmd.AddCommand(listCommand())
    rootCmd.AddCommand(deleteCommand())
    
    rootCmd.Execute()
}

func createCommand() *cobra.Command {
    return &cobra.Command{
        Use:   "create",
        Short: "Create a new resource",
        Run: func(cmd *cobra.Command, args []string) {
            fmt.Println("Creating resource...")
        },
    }
}

func listCommand() *cobra.Command {
    return &cobra.Command{
        Use:   "list",
        Short: "List all resources",
        Run: func(cmd *cobra.Command, args []string) {
            fmt.Println("Listing resources...")
        },
    }
}

func deleteCommand() *cobra.Command {
    return &cobra.Command{
        Use:   "delete [id]",
        Short: "Delete a resource",
        Args:  cobra.ExactArgs(1),
        Run: func(cmd *cobra.Command, args []string) {
            fmt.Printf("Deleting resource: %s\n", args[0])
        },
    }
}

参数验证

func createCommand() *cobra.Command {
    var name string
    var age int
    
    cmd := &cobra.Command{
        Use:   "create",
        Short: "Create a new user",
        Run: func(cmd *cobra.Command, args []string) {
            // 验证参数
            if name == "" {
                fmt.Println("Error: name is required")
                return
            }
            if age <= 0 {
                fmt.Println("Error: age must be positive")
                return
            }
            
            fmt.Printf("Creating user: %s, age: %d\n", name, age)
        },
    }
    
    cmd.Flags().StringVarP(&name, "name", "n", "", "User name")
    cmd.Flags().IntVarP(&age, "age", "a", 0, "User age")
    
    // 标记为必填
    cmd.MarkFlagRequired("name")
    
    return cmd
}

持久化标志

func main() {
    var verbose bool
    var config string
    
    rootCmd := &cobra.Command{
        Use:   "myapp",
        Short: "My application",
        PersistentPreRun: func(cmd *cobra.Command, args []string) {
            if verbose {
                fmt.Println("Verbose mode enabled")
            }
            fmt.Printf("Using config: %s\n", config)
        },
    }
    
    // 持久化标志:所有子命令都可以使用
    rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Verbose output")
    rootCmd.PersistentFlags().StringVarP(&config, "config", "c", "config.yaml", "Config file")
    
    rootCmd.AddCommand(createCommand())
    rootCmd.Execute()
}

配置文件集成

package main

import (
    "fmt"
    "github.com/spf13/cobra"
    "github.com/spf13/viper"
)

func main() {
    rootCmd := &cobra.Command{
        Use:   "myapp",
        Short: "My application",
        PersistentPreRun: func(cmd *cobra.Command, args []string) {
            // 初始化 Viper
            viper.SetConfigName("config")
            viper.SetConfigType("yaml")
            viper.AddConfigPath("./")
            
            if err := viper.ReadInConfig(); err != nil {
                fmt.Println("Using default config")
            }
            
            // 绑定命令行标志
            viper.BindPFlag("port", cmd.PersistentFlags().Lookup("port"))
        },
    }
    
    rootCmd.PersistentFlags().IntP("port", "p", 8080, "Server port")
    
    rootCmd.AddCommand(serveCommand())
    rootCmd.Execute()
}

func serveCommand() *cobra.Command {
    return &cobra.Command{
        Use:   "serve",
        Short: "Start the server",
        Run: func(cmd *cobra.Command, args []string) {
            port := viper.GetInt("port")
            fmt.Printf("Starting server on port %d\n", port)
        },
    }
}

自动生成文档

package main

import (
    "fmt"
    "github.com/spf13/cobra"
    "github.com/spf13/cobra/doc"
)

func main() {
    rootCmd := &cobra.Command{
        Use:   "myapp",
        Short: "My application",
        Run: func(cmd *cobra.Command, args []string) {
            fmt.Println("Hello, World!")
        },
    }
    
    rootCmd.AddCommand(&cobra.Command{
        Use:   "version",
        Short: "Print version",
        Run: func(cmd *cobra.Command, args []string) {
            fmt.Println("Version 1.0.0")
        },
    })
    
    // 生成 Markdown 文档
    doc.GenMarkdownTree(rootCmd, "./docs")
    
    // 生成 Man 页面
    doc.GenManTree(rootCmd, &doc.GenManHeader{
        Section: "1",
    }, "./man")
    
    // 生成 ReStructuredText
    doc.GenReSTTree(rootCmd, "./rst")
}

Shell 自动补全

func main() {
    rootCmd := &cobra.Command{
        Use:   "myapp",
        Short: "My application",
    }
    
    // 生成 Bash 补全
    rootCmd.GenBashCompletionFile("/etc/bash_completion.d/myapp")
    
    // 生成 Zsh 补全
    rootCmd.GenZshCompletionFile("/usr/local/share/zsh/site-functions/_myapp")
    
    // 生成 Fish 补全
    rootCmd.GenFishCompletionFile("/usr/share/fish/vendor_completions.d/myapp.fish")
    
    rootCmd.Execute()
}

Go-Micro 微服务框架

简介

Go-Micro 是一个用于构建微服务的 Go 语言框架,提供服务发现、负载均衡、消息编码、RPC 通信等功能。它支持多种传输协议、编码格式和服务发现机制,是构建分布式系统的理想选择。

安装

go get -u github.com/micro/go-micro/v2
go get -u github.com/micro/go-micro/v2/api

快速开始

package main

import (
    "context"
    "github.com/micro/go-micro/v2"
    proto "github.com/micro/go-micro/v2/examples/greeter/proto"
)

type Greeter struct {}

func (g *Greeter) Hello(ctx context.Context, req *proto.HelloRequest, rsp *proto.HelloResponse) error {
    rsp.Greeting = "Hello " + req.Name
    return nil
}

func main() {
    // 创建服务
    service := micro.NewService(
        micro.Name("greeter"),
        micro.Version("latest"),
    )
    
    // 初始化服务
    service.Init()
    
    // 注册处理器
    proto.RegisterGreeterHandler(service.Server(), &Greeter{})
    
    // 运行服务
    if err := service.Run(); err != nil {
        fmt.Println(err)
    }
}

服务发现

package main

import (
    "context"
    "fmt"
    "time"
    
    "github.com/micro/go-micro/v2"
    "github.com/micro/go-micro/v2/client"
    "github.com/micro/go-micro/v2/registry"
    "github.com/micro/go-micro/v2/registry/etcd"
)

func main() {
    // 使用 Etcd 作为注册中心
    reg := etcd.NewRegistry(registry.Addrs("127.0.0.1:2379"))
    
    // 创建服务
    service := micro.NewService(
        micro.Registry(reg),
        micro.Name("greeter"),
    )
    
    service.Init()
    
    // 创建客户端
    cli := service.Client()
    
    // 调用服务
    rsp := &HelloResponse{}
    err := cli.Call(
        context.Background(),
        client.NewRequest("greeter", "Greeter.Hello", &HelloRequest{Name: "World"}),
        rsp,
        client.WithRetries(3),
        client.WithRequestTimeout(time.Second * 5),
    )
    
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    
    fmt.Println("Response:", rsp.Greeting)
}

服务间通信

package main

import (
    "context"
    "github.com/micro/go-micro/v2"
)

// 定义服务接口
type UserService interface {
    GetUser(ctx context.Context, req *GetUserRequest, rsp *GetUserResponse) error
}

// 服务 A:用户服务
type UserHandler struct {}

func (h *UserHandler) GetUser(ctx context.Context, req *GetUserRequest, rsp *GetUserResponse) error {
    // 获取用户信息
    rsp.User = &User{ID: req.Id, Name: "Alice"}
    return nil
}

// 服务 B:订单服务
type OrderHandler struct {
    userService UserService
}

func (h *OrderHandler) CreateOrder(ctx context.Context, req *CreateOrderRequest, rsp *CreateOrderResponse) error {
    // 调用用户服务
    userReq := &GetUserRequest{Id: req.UserId}
    userRsp := &GetUserResponse{}
    err := h.userService.GetUser(ctx, userReq, userRsp)
    if err != nil {
        return err
    }
    
    // 创建订单
    rsp.Order = &Order{ID: "1", User: userRsp.User}
    return nil
}

func main() {
    // 创建服务
    service := micro.NewService(
        micro.Name("order"),
    )
    
    service.Init()
    
    // 创建用户服务客户端
    userService := micro.NewService(
        micro.Name("order"),  // 使用当前服务的客户端
    )
    
    // 注册订单服务
    micro.RegisterHandler(service.Server(), &OrderHandler{
        userService: userService.Client(),
    })
    
    service.Run()
}

消息发布订阅

package main

import (
    "context"
    "fmt"
    "time"
    
    "github.com/micro/go-micro/v2"
    "github.com/micro/go-micro/v2/broker"
    "github.com/micro/go-micro/v2/broker/nats"
)

// 发布者
func publisher() {
    // 创建 Broker
    b := nats.NewBroker(broker.Addrs("127.0.0.1:4222"))
    
    if err := b.Connect(); err != nil {
        fmt.Println("Broker connect error:", err)
        return
    }
    
    // 发布消息
    msg := &broker.Message{
        Header: map[string]string{
            "Content-Type": "application/json",
        },
        Body: []byte(`{"event":"user.created","user_id":"123"}`),
    }
    
    if err := b.Publish("user.created", msg); err != nil {
        fmt.Println("Publish error:", err)
    }
}

// 订阅者
func subscriber() {
    // 创建服务
    service := micro.NewService(
        micro.Broker(nats.NewBroker(broker.Addrs("127.0.0.1:4222"))),
    )
    
    service.Init()
    
    // 订阅消息
    micro.RegisterSubscriber("user.created", service.Server(), func(ctx context.Context, msg *broker.Message) error {
        fmt.Printf("Received message: %s\n", string(msg.Body))
        return nil
    })
    
    service.Run()
}

中间件

package main

import (
    "context"
    "fmt"
    "time"
    
    "github.com/micro/go-micro/v2"
    "github.com/micro/go-micro/v2/server"
)

// 日志中间件
func logWrapper(fn server.HandlerFunc) server.HandlerFunc {
    return func(ctx context.Context, req server.Request, rsp interface{}) error {
        fmt.Printf("[Request] %s %s\n", req.Method(), req.Endpoint())
        err := fn(ctx, req, rsp)
        fmt.Printf("[Response] %s\n", err)
        return err
    }
}

// 认证中间件
func authWrapper(fn server.HandlerFunc) server.HandlerFunc {
    return func(ctx context.Context, req server.Request, rsp interface{}) error {
        // 检查认证令牌
        token := req.Header().Get("Authorization")
        if token == "" {
            return fmt.Errorf("Unauthorized")
        }
        
        return fn(ctx, req, rsp)
    }
}

func main() {
    // 创建服务
    service := micro.NewService(
        micro.Name("greeter"),
        micro.WrapHandler(logWrapper),
        micro.WrapHandler(authWrapper),
    )
    
    service.Init()
    service.Run()
}

Etcd 分布式键值存储

简介

Etcd 是一个分布式、可靠的键值存储系统,用于配置管理和服务发现。它使用 Raft 算法保证数据一致性,支持强一致性读写、事务、租约等功能,是 Kubernetes 等项目的核心组件。

安装

go get -u go.etcd.io/etcd/client/v3

快速开始

package main

import (
    "context"
    "fmt"
    "time"
    
    "go.etcd.io/etcd/client/v3"
)

func main() {
    // 创建客户端
    cli, err := clientv3.New(clientv3.Config{
        Endpoints:   []string{"127.0.0.1:2379"},
        DialTimeout: 5 * time.Second,
    })
    if err != nil {
        panic(err)
    }
    defer cli.Close()
    
    // 设置键值
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    resp, err := cli.Put(ctx, "key", "value")
    cancel()
    if err != nil {
        panic(err)
    }
    fmt.Println("Revision:", resp.Header.Revision)
    
    // 获取键值
    ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
    resp, err = cli.Get(ctx, "key")
    cancel()
    if err != nil {
        panic(err)
    }
    
    for _, ev := range resp.Kvs {
        fmt.Printf("%s : %s\n", ev.Key, ev.Value)
    }
}

键值操作

// 设置键值
resp, err := cli.Put(ctx, "key", "value")

// 获取单个键
resp, err := cli.Get(ctx, "key")

// 获取多个键(前缀匹配)
resp, err := cli.Get(ctx, "prefix", clientv3.WithPrefix())

// 获取所有键
resp, err := cli.Get(ctx, "", clientv3.WithFromKey(), clientv3.WithLimit(100))

// 删除键
resp, err := cli.Delete(ctx, "key")

// 删除多个键(前缀匹配)
resp, err := cli.Delete(ctx, "prefix", clientv3.WithPrefix())

// 删除所有键
resp, err := cli.Delete(ctx, "", clientv3.WithPrefix())

租约(Lease)

// 创建租约(10秒过期)
lease, err := cli.Grant(ctx, 10)
if err != nil {
    panic(err)
}

// 绑定租约到键
resp, err := cli.Put(ctx, "key", "value", clientv3.WithLease(lease.ID))

// 续租
keepAlive, err := cli.KeepAlive(ctx, lease.ID)
for ka := range keepAlive {
    fmt.Printf("TTL: %d\n", ka.TTL)
}

// 撤销租约
_, err = cli.Revoke(ctx, lease.ID)

事务

// 事务操作
txn := cli.Txn(ctx)

// If 条件
txn.If(
    clientv3.Compare(clientv3.Value("key"), "=", "value"),
).
Then(
    clientv3.OpPut("key", "new_value"),
).
Else(
    clientv3.OpPut("key", "default_value"),
).
Commit()

// 事务示例:分布式锁
func acquireLock(cli *clientv3.Client, key string, ttl int64) (bool, error) {
    // 创建租约
    lease, err := cli.Grant(context.Background(), ttl)
    if err != nil {
        return false, err
    }
    
    // 事务:如果键不存在,则设置
    txn := cli.Txn(context.Background())
    txn.If(
        clientv3.Compare(clientv3.CreateRevision(key), "=", 0),
    ).
    Then(
        clientv3.OpPut(key, "locked", clientv3.WithLease(lease.ID)),
    ).
    Commit()
    
    return txn.Succeeded(), nil
}

监听变化

// 监听单个键
watchChan := cli.Watch(ctx, "key")
for watchResp := range watchChan {
    for _, event := range watchResp.Events {
        fmt.Printf("Type: %s, Key: %s, Value: %s\n", 
            event.Type, event.Kv.Key, event.Kv.Value)
    }
}

// 监听前缀
watchChan = cli.Watch(ctx, "prefix", clientv3.WithPrefix())
for watchResp := range watchChan {
    for _, event := range watchResp.Events {
        fmt.Printf("Type: %s, Key: %s, Value: %s\n", 
            event.Type, event.Kv.Key, event.Kv.Value)
    }
}

// 从指定版本开始监听
watchChan = cli.Watch(ctx, "key", clientv3.WithRev(100))

服务发现

// 注册服务
func registerService(cli *clientv3.Client, serviceID, address string, ttl int64) (string, error) {
    // 创建租约
    lease, err := cli.Grant(context.Background(), ttl)
    if err != nil {
        return "", err
    }
    
    // 注册服务
    key := fmt.Sprintf("/services/%s", serviceID)
    _, err = cli.Put(context.Background(), key, address, clientv3.WithLease(lease.ID))
    if err != nil {
        return "", err
    }
    
    // 续租
    keepAlive, err := cli.KeepAlive(context.Background(), lease.ID)
    go func() {
        for range keepAlive {
            // 保持续租
        }
    }()
    
    return fmt.Sprintf("%d", lease.ID), nil
}

// 发现服务
func discoverService(cli *clientv3.Client, serviceID string) (string, error) {
    key := fmt.Sprintf("/services/%s", serviceID)
    resp, err := cli.Get(context.Background(), key)
    if err != nil {
        return "", err
    }
    
    if len(resp.Kvs) == 0 {
        return "", fmt.Errorf("service not found")
    }
    
    return string(resp.Kvs[0].Value), nil
}

配置管理

// 监听配置变化
func watchConfig(cli *clientv3.Client, key string) {
    watchChan := cli.Watch(context.Background(), key)
    for watchResp := range watchChan {
        for _, event := range watchResp.Events {
            switch event.Type {
            case clientv3.EventTypePut:
                fmt.Printf("Config updated: %s\n", event.Kv.Value)
                // 重新加载配置
            case clientv3.EventTypeDelete:
                fmt.Println("Config deleted")
            }
        }
    }
}

// 获取配置
func getConfig(cli *clientv3.Client, key string) (string, error) {
    resp, err := cli.Get(context.Background(), key)
    if err != nil {
        return "", err
    }
    
    if len(resp.Kvs) == 0 {
        return "", fmt.Errorf("config not found")
    }
    
    return string(resp.Kvs[0].Value), nil
}

最佳实践

Validator 最佳实践

1. 统一错误处理

type ValidationError struct {
    Field   string `json:"field"`
    Message string `json:"message"`
}

func HandleValidationError(err error) []ValidationError {
    var errors []ValidationError
    validationErrors := err.(validator.ValidationErrors)
    for, e := range validationErrors {
        errors = append(errors, ValidationError{
            Field:   e.Field(),
            Message: getErrorMessage(e),
        })
    }
    return errors
}

2. 自定义校验规则

// 业务相关的自定义校验
func validateBusinessRule(fl validator.FieldLevel) bool {
    // 实现业务规则校验
    return true
}

3. 分层校验

// Controller 层:格式校验
if err := c.ShouldBindJSON(&req); err != nil {
    return err
}

// Service 层:业务校验
if err := s.ValidateBusiness(&req); err != nil {
    return err
}

Cobra 最佳实践

1. 结构化命令

// cmd/root.go
package cmd

var rootCmd = &cobra.Command{
    Use:   "myapp",
    Short: "My application",
}

func Execute() {
    if err := rootCmd.Execute(); err != nil {
        os.Exit(1)
    }
}

// cmd/create.go
package cmd

var createCmd = &cobra.Command{
    Use:   "create",
    Short: "Create resource",
}

func init() {
    rootCmd.AddCommand(createCmd)
}

2. 配置管理

// 使用 Viper 管理配置
var cfgFile string

func init() {
    cobra.OnInitialize(initConfig)
    rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file")
}

func initConfig() {
    if cfgFile != "" {
        viper.SetConfigFile(cfgFile)
    } else {
        home, _ := os.UserHomeDir()
        viper.AddConfigPath(home)
        viper.SetConfigName(".myapp")
    }
    
    viper.ReadInConfig()
}

3. 生成文档

// 自动生成文档
go run main.go docs

Go-Micro 最佳实践

1. 服务拆分

// 按业务领域拆分服务
// - user-service
// - order-service
// - payment-service

2. 接口设计

// 使用 Protobuf 定义接口
syntax = "proto3";

package user;

service UserService {
  rpc GetUser(GetUserRequest) returns (GetUserResponse);
  rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
}

3. 错误处理

// 统一错误处理
type ErrorResponse struct {
    Code    string `json:"code"`
    Message string `json:"message"`
}

func HandleError(err error) *ErrorResponse {
    return &ErrorResponse{
        Code:    "ERROR",
        Message: err.Error(),
    }
}

Etcd 最佳实践

1. 键命名规范

// 使用层次化的键名
// /services/user-service/instances/192.168.1.1:8080
// /config/database/host
// /locks/resource-123

2. 租约管理

// 自动续租
keepAlive, err := cli.KeepAlive(ctx, lease.ID)
defer cli.Revoke(ctx, lease.ID)

3. 事务使用

// 使用事务实现原子操作
txn := cli.Txn(ctx)
// ...
txn.Commit()

4. 监听机制

// 使用监听实现配置热更新
watchChan := cli.Watch(ctx, "/config", clientv3.WithPrefix())

微服务架构最佳实践

1. 服务注册与发现

// 使用 Etcd 作为注册中心
registry := etcd.NewRegistry(registry.Addrs("127.0.0.1:2379"))
service := micro.NewService(micro.Registry(registry))

2. 配置中心

// 使用 Etcd 存储配置
cli.Put(ctx, "/config/database/host", "localhost")

3. 分布式锁

// 使用 Etcd 实现分布式锁
func acquireLock(cli *clientv3.Client, key string) (bool, error) {
    // ...
}

4. 消息队列

// 使用 Go-Micro Broker
micro.RegisterSubscriber("topic", service.Server(), handler)

Protobuf 数据序列化

Protocol Buffers (Protobuf) 是 Google 开发的一种语言无关、平台无关的序列化结构数据的方法。

快速开始

// 安装 protoc 编译器
// macOS: brew install protobuf
// Ubuntu: apt-get install protobuf-compiler

// 安装 Go 插件
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

定义 .proto 文件

// user.proto
syntax = "proto3";

package user;

option go_package = "./user";

message User {
    int32 id = 1;
    string name = 2;
    string email = 3;
    repeated string tags = 4;
    enum Status {
        UNKNOWN = 0;
        ACTIVE = 1;
        INACTIVE = 2;
    }
    Status status = 5;
}

message GetUserRequest {
    int32 id = 1;
}

message GetUserResponse {
    User user = 1;
}

生成 Go 代码

// 生成代码
protoc --go_out=. --go_opt=paths=source_relative \
       --go-grpc_out=. --go-grpc_opt=paths=source_relative \
       user.proto

序列化和反序列化

package main

import (
    "fmt"
    "google.golang.org/protobuf/proto"
    "path/to/user"
)

func main() {
    // 创建消息
    u := &user.User{
        Id:    1,
        Name:  "张三",
        Email: "zhangsan@example.com",
        Tags:  []string{"admin", "user"},
        Status: user.User_ACTIVE,
    }
    
    // 序列化
    data, err := proto.Marshal(u)
    if err != nil {
        panic(err)
    }
    fmt.Printf("序列化结果: %x\n", data)
    
    // 反序列化
    var u2 user.User
    err = proto.Unmarshal(data, &u2)
    if err != nil {
        panic(err)
    }
    fmt.Printf("反序列化结果: %+v\n", u2)
}

Protobuf 最佳实践

1. 字段编号规范

// 1-15: 单字节编码,常用字段
// 16-2047: 两字节编码,不常用字段
// 保留字段编号:19000-19999

message User {
    string name = 1;   // 常用字段
    string email = 2;  // 常用字段
    string phone = 16; // 不常用字段
}

2. 使用 oneof

message Result {
    oneof result {
        string error = 1;
        string data = 2;
    }
}

3. 使用 map

message User {
    map<string, string> metadata = 10;
}

4. 版本兼容性

// 新增字段时使用 optional
message User {
    string name = 1;
    optional string nickname = 10; // 新增字段
}

// 保留废弃字段
message User {
    int32 old_id = 1 [deprecated = true];
    string new_id = 2;
}

Sonic JSON

Sonic 是字节跳动开源的高性能 JSON 库,基于 SIMD 指令集优化,在 Go 语言中提供了比标准库 `encoding/json` 更快的 JSON 序列化和反序列化性能。它完全兼容标准库 API,可以作为标准库的替代品使用。

核心特性

性能对比

与标准库和其他 JSON 库的性能对比(数据来源:Sonic 官方基准测试):

序列化 反序列化
encoding/json 1x (基准) 1x (基准)
json-iterator/go 1.5x 1.8x
easyjson 2.5x 3.0x
sonic 3.5x 4.2x

安装

go get -u github.com/bytedance/sonic

快速开始

package main

import (
    "fmt"
    "github.com/bytedance/sonic"
)

type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

func main() {
    // 序列化
    user := User{ID: 1, Name: "Alice", Email: "alice@example.com"}
    data, err := sonic.Marshal(user)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(data))
    // 输出: {"id":1,"name":"Alice","email":"alice@example.com"}
    
    // 反序列化
    var decoded User
    err = sonic.Unmarshal(data, &decoded)
    if err != nil {
        panic(err)
    }
    fmt.Printf("%+v\n", decoded)
    // 输出: {ID:1 Name:Alice Email:alice@example.com}
}

高级用法

1. 使用配置选项

package main

import (
    "fmt"
    "github.com/bytedance/sonic"
    "github.com/bytedance/sonic/option"
)

func main() {
    // 创建自定义配置
    cfg := sonic.Config{
        EscapeHTML: false,
        SortKeys:   true,
    }
    
    api := sonic.ConfigStd(cfg)
    
    user := map[string]interface{}{
        "name":  "Bob",
        "email": "bob@example.com",
    }
    
    data, _ := api.Marshal(user)
    fmt.Println(string(data))
}

2. 流式处理

package main

import (
    "bytes"
    "fmt"
    "github.com/bytedance/sonic"
)

func main() {
    // 使用 Encoder 进行流式序列化
    var buf bytes.Buffer
    encoder := sonic.ConfigDefault().NewEncoder(&buf)
    
    users := []string{"Alice", "Bob", "Charlie"}
    for _, name := range users {
        encoder.Encode(name)
    }
    
    fmt.Println(buf.String())
}

3. 使用 Node API 处理动态 JSON

package main

import (
    "fmt"
    "github.com/bytedance/sonic"
    "github.com/bytedance/sonic/ast"
)

func main() {
    jsonStr := `{"name":"Alice","age":30,"email":"alice@example.com"}`
    
    // 解析为 Node
    node, err := ast.NewNode(jsonStr)
    if err != nil {
        panic(err)
    }
    
    // 访问字段
    name, _ := node.GetByPath("name").String()
    age, _ := node.GetByPath("age").Int64()
    
    fmt.Printf("Name: %s, Age: %d\n", name, age)
    
    // 修改字段
    node.SetByPath("age", 31)
    
    // 转换回 JSON
    modified, _ := node.MarshalJSON()
    fmt.Println(string(modified))
}

4. 与 HTTP 服务器集成

package main

import (
    "encoding/json"
    "net/http"
    "github.com/bytedance/sonic"
)

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

func main() {
    http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {
        if r.Method == "POST" {
            var user User
            // 使用 Sonic 解析 JSON
            err := sonic.Unmarshal([]byte(r.FormValue("data")), &user)
            if err != nil {
                http.Error(w, err.Error(), http.StatusBadRequest)
                return
            }
            
            // 使用 Sonic 序列化响应
            data, _ := sonic.Marshal(user)
            w.Header().Set("Content-Type", "application/json")
            w.Write(data)
        }
    })
    
    http.ListenAndServe(":8080", nil)
}

最佳实践

1. 性能优化

package main

import (
    "github.com/bytedance/sonic"
    "github.com/bytedance/sonic/option"
)

func main() {
    // 使用预编译的 API 提升性能
    api := sonic.ConfigFastest()
    
    // 对于频繁使用的类型,使用预编译
    type User struct {
        Name string `json:"name"`
        Age  int    `json:"age"`
    }
    
    // 预编译类型的序列化器
    marshaler, _ := api.NewMarshaler(User{})
    
    user := User{Name: "Alice", Age: 30}
    data, _ := marshaler.Marshal(user)
}

2. 错误处理

package main

import (
    "fmt"
    "github.com/bytedance/sonic"
)

func safeUnmarshal(data []byte, v interface{}) error {
    if err := sonic.Unmarshal(data, v); err != nil {
        return fmt.Errorf("JSON 解析失败: %w", err)
    }
    return nil
}

func safeMarshal(v interface{}) ([]byte, error) {
    data, err := sonic.Marshal(v)
    if err != nil {
        return nil, fmt.Errorf("JSON 序列化失败: %w", err)
    }
    return data, nil
}

3. 兼容性处理

package main

import (
    "encoding/json"
    "github.com/bytedance/sonic"
)

type JSONMarshaler interface {
    MarshalJSON() ([]byte, error)
}

type JSONUnmarshaler interface {
    UnmarshalJSON([]byte) error
}

// Sonic 完全支持标准库的 MarshalJSON 和 UnmarshalJSON 接口
type CustomType struct {
    Value string
}

func (c *CustomType) MarshalJSON() ([]byte, error) {
    return json.Marshal(c.Value)
}

func (c *CustomType) UnmarshalJSON(data []byte) error {
    return json.Unmarshal(data, &c.Value)
}

使用场景

注意事项

GoCarina/gocsv

gocsv 是一个功能强大且易于使用的 Go 语言 CSV 文件读写库,由 GoCarina 开发。它提供了简洁的 API,支持将 CSV 文件与 Go 结构体进行自动映射,大大简化了 CSV 文件的处理工作。

核心特性

安装

go get github.com/gocarina/gocsv

快速开始

package main

import (
    "encoding/csv"
    "fmt"
    "os"
    "github.com/gocarina/gocsv"
)

type Client struct {
    Id      string `csv:"id"`
    Name    `csv:"name"`
    Age     int    `csv:"age"`
    Country string `csv:"country"`
}

func main() {
    clients := []Client{
        {Id: "1", Name: "Alice", Age: 30, Country: "USA"},
        {Id: "2", Name: "Bob", Age: 25, Country: "UK"},
        {Id: "3", Name: "Charlie", Age: 35, Country: "Canada"},
    }
    
    // 写入 CSV 文件
    file, _ := os.OpenFile("clients.csv", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
    defer file.Close()
    
    gocsv.MarshalFile(&clients, file)
    
    // 读取 CSV 文件
    clientsFile, _ := os.OpenFile("clients.csv", os.O_RDWR|os.O_CREATE, 0755)
    defer clientsFile.Close()
    
    var clientsRead []Client
    gocsv.UnmarshalFile(clientsFile, &clientsRead)
    
    fmt.Printf("%+v\n", clientsRead)
}

高级用法

1. 自定义分隔符和配置

package main

import (
    "github.com/gocarina/gocsv"
)

func main() {
    // 使用分号作为分隔符
    gocsv.SetCSVReader(gocsv.CustomReader{
        Separator: ';',
    })
    
    // 使用分号作为分隔符写入
    gocsv.SetCSVWriter(gocsv.CustomWriter{
        Separator: ';',
    })
}

2. 处理带标题的 CSV

package main

import (
    "fmt"
    "os"
    "github.com/gocarina/gocsv"
)

type Product struct {
    SKU      string  `csv:"SKU"`
    Name     string  `csv:"Product Name"`
    Price    float64 `csv:"Price"`
    Quantity int     `csv:"Quantity"`
}

func main() {
    file, _ := os.Open("products.csv")
    defer file.Close()
    
    var products []Product
    gocsv.UnmarshalFile(file, &products)
    
    fmt.Printf("读取到 %d 个产品\n", len(products))
}

3. 流式读取大型 CSV 文件

package main

import (
    "fmt"
    "os"
    "github.com/gocarina/gocsv"
)

type LogEntry struct {
    Timestamp string `csv:"timestamp"`
    Level     string `csv:"level"`
    Message   string `csv:"message"`
}

func main() {
    file, _ := os.Open("large_logs.csv")
    defer file.Close()
    
    // 流式读取,逐行处理
    reader := gocsv.NewDecoder(file)
    
    for {
        var entry LogEntry
        err := reader.Decode(&entry)
        if err == io.EOF {
            break
        }
        if err != nil {
            fmt.Printf("解析错误: %v\n", err)
            continue
        }
        
        // 处理每行数据
        fmt.Printf("[%s] %s: %s\n", entry.Timestamp, entry.Level, entry.Message)
    }
}

4. 处理嵌套结构

package main

import (
    "fmt"
    "github.com/gocarina/gocsv"
)

type Address struct {
    Street  string `csv:"street"`
    City    string `csv:"city"`
    Country string `csv:"country"`
}

type Person struct {
    Name    string  `csv:"name"`
    Age     int     `csv:"age"`
    Address Address  `csv:"address"`
}

func main() {
    // gocsv 支持嵌套结构
    csvData := `name,age,street,city,country
Alice,30,123 Main St,New York,USA
Bob,25,456 Oak Ave,London,UK`
    
    var people []Person
    gocsv.Unmarshal([]byte(csvData), &people)
    
    fmt.Printf("%+v\n", people)
}

5. 自定义类型转换

package main

import (
    "fmt"
    "time"
    "github.com/gocarina/gocsv"
)

type Transaction struct {
    ID        string    `csv:"id"`
    Amount    float64   `csv:"amount"`
    Date      string    `csv:"date"`
    DateParsed time.Time  `csv:"-"` // 不从 CSV 读取
}

func (t *Transaction) UnmarshalCSV(csv string) error {
    // 自定义解析逻辑
    return nil
}

func (t *Transaction) MarshalCSV() (string, error) {
    // 自定义序列化逻辑
    return "", nil
}

func main() {
    // 使用自定义类型转换
    transactions := []Transaction{
        {ID: "1", Amount: 100.50, Date: "2024-01-15"},
    }
    
    csvData, _ := gocsv.MarshalString(&transactions)
    fmt.Println(csvData)
}

最佳实践

1. 错误处理和验证

package main

import (
    "fmt"
    "os"
    "github.com/gocarina/gocsv"
)

type ValidatedRecord struct {
    Email string `csv:"email" validate:"required,email"`
    Phone string `csv:"phone" validate:"required"`
}

func validateRecord(record ValidatedRecord) error {
    if record.Email == "" {
        return fmt.Errorf("邮箱不能为空")
    }
    // 更多验证逻辑...
    return nil
}

func main() {
    file, _ := os.Open("data.csv")
    defer file.Close()
    
    var records []ValidatedRecord
    if err := gocsv.UnmarshalFile(file, &records); err != nil {
        fmt.Printf("CSV 解析失败: %v\n", err)
        return
    }
    
    // 验证每条记录
    for i, record := range records {
        if err := validateRecord(record); err != nil {
            fmt.Printf("第 %d 行验证失败: %v\n", i+1, err)
        }
    }
}

2. 性能优化

package main

import (
    "bufio"
    "os"
    "github.com/gocarina/gocsv"
)

func processLargeCSV(filePath string) error {
    // 使用缓冲读取提升性能
    file, err := os.Open(filePath)
    if err != nil {
        return err
    }
    defer file.Close()
    
    // 使用 bufio 包装文件
    bufferedFile := bufio.NewReader(file)
    
    // 流式处理,避免内存溢出
    reader := gocsv.NewDecoder(bufferedFile)
    
    for {
        var record interface{}
        err := reader.Decode(&record)
        if err == io.EOF {
            break
        }
        if err != nil {
            continue
        }
        
        // 处理记录
    }
    
    return nil
}

3. 处理特殊字符和转义

package main

import (
    "github.com/gocarina/gocsv"
)

func main() {
    // 配置处理包含特殊字符的字段
    gocsv.SetCSVReader(gocsv.CustomReader{
        LazyQuotes:    true,  // 宽松的引号处理
        TrimLeadingSpace: true,  // 去除前导空格
        ReuseRecord:    true,  // 重用记录以减少内存分配
    })
    
    // 处理包含逗号、引号、换行符的字段
    data := struct {
        Name    string `csv:"name"`
        Comment string `csv:"comment"`
    }{
        Name:    "Alice, Bob",
        Comment: "This is a \"quoted\" string",
    }
}

4. 批量处理和并发

package main

import (
    "sync"
    "github.com/gocarina/gocsv"
)

func processBatch(records []interface{}) {
    // 使用 worker pool 并发处理
    var wg sync.WaitGroup
    workerCount := 10
    
    recordChan := make(chan interface{}, workerCount*2)
    
    // 启动 workers
    for i := 0; i < workerCount; i++ {
        wg.Add(1)
        go worker(recordChan, &wg)
    }
    
    // 发送记录
    for _, record := range records {
        recordChan <- record
    }
    
    close(recordChan)
    wg.Wait()
}

func worker(recordChan chan interface{}, wg *sync.WaitGroup) {
    defer wg.Done()
    for record := range recordChan {
        // 处理记录
        _ = record
    }
}

5. 与数据库集成

package main

import (
    "database/sql"
    "fmt"
    "os"
    "_ github.com/go-sql-driver/mysql"
    "github.com/gocarina/gocsv"
)

type Customer struct {
    ID   int    `csv:"id" db:"id"`
    Name string `csv:"name" db:"name"`
    Email string `csv:"email" db:"email"`
}

func importCSVToDatabase(csvPath string, db *sql.DB) error {
    // 从 CSV 读取数据
    file, err := os.Open(csvPath)
    if err != nil {
        return err
    }
    defer file.Close()
    
    var customers []Customer
    if err := gocsv.UnmarshalFile(file, &customers); err != nil {
        return err
    }
    
    // 批量插入数据库
    stmt, err := db.Prepare("INSERT INTO customers (name, email) VALUES (?, ?)")
    if err != nil {
        return err
    }
    defer stmt.Close()
    
    for _, customer := range customers {
        _, err := stmt.Exec(customer.Name, customer.Email)
        if err != nil {
            fmt.Printf("插入失败: %v\n", err)
        }
    }
    
    return nil
}

func exportDatabaseToCSV(db *sql.DB, csvPath string) error {
    // 从数据库查询数据
    rows, err := db.Query("SELECT id, name, email FROM customers")
    if err != nil {
        return err
    }
    defer rows.Close()
    
    var customers []Customer
    for rows.Next() {
        var customer Customer
        err := rows.Scan(&customer.ID, &customer.Name, &customer.Email)
        if err != nil {
            continue
        }
        customers = append(customers, customer)
    }
    
    // 写入 CSV 文件
    file, err := os.Create(csvPath)
    if err != nil {
        return err
    }
    defer file.Close()
    
    return gocsv.MarshalFile(&customers, file)
}

使用场景

注意事项

Excelize Excel

Excelize 是一个用于处理 Microsoft Excel™(.xlsx)文件的 Go 语言库。它提供了丰富的功能来创建、读取、修改和写入 Excel 文件,支持复杂的格式设置、图表、图片、数据透视表等功能,是 Go 生态中最强大的 Excel 处理库之一。

核心特性

安装

go get github.com/xuri/excelize/v2

快速开始

package main

import (
    "fmt"
    "github.com/xuri/excelize/v2"
)

func main() {
    // 创建新的 Excel 文件
    f := excelize.NewFile()
    defer func() {
        if err := f.Close(); err != nil {
            fmt.Println(err)
        }
    }()
    
    // 设置单元格值
    f.SetCellValue("Sheet1", "A1", "姓名")
    f.SetCellValue("Sheet1", "B1", "年龄")
    f.SetCellValue("Sheet1", "A2", "Alice")
    f.SetCellValue("Sheet1", "B2", 30)
    
    // 保存文件
    if err := f.SaveAs("Book1.xlsx"); err != nil {
        fmt.Println(err)
    }
}

高级用法

1. 读取 Excel 文件

package main

import (
    "fmt"
    "github.com/xuri/excelize/v2"
)

func main() {
    // 打开现有文件
    f, err := excelize.OpenFile("Book1.xlsx")
    if err != nil {
        fmt.Println(err)
        return
    }
    defer func() {
        if err := f.Close(); err != nil {
            fmt.Println(err)
        }
    }()
    
    // 获取单元格值
    cell, _ := f.GetCellValue("Sheet1", "A2")
    fmt.Println("A2 的值:", cell)
    
    // 获取所有行
    rows, _ := f.GetRows("Sheet1")
    for _, row := range rows {
        fmt.Println(row)
    }
}

2. 单元格格式设置

package main

import (
    "github.com/xuri/excelize/v2"
)

func main() {
    f := excelize.NewFile()
    defer f.Close()
    
    // 设置字体样式
    style, _ := f.NewStyle(&excelize.Style{
        Font: &excelize.Font{
            Bold:   true,
            Italic: true,
            Family: "Arial",
            Size:   14,
            Color:  "#FF0000",
        },
    })
    f.SetCellStyle("Sheet1", "A1", "A1", style)
    
    // 设置边框
    borderStyle, _ := f.NewStyle(&excelize.Style{
        Border: []excelize.Border{
            {Type: "left", Color: "#000000", Style: 1},
            {Type: "top", Color: "#000000", Style: 1},
            {Type: "bottom", Color: "#000000", Style: 1},
            {Type: "right", Color: "#000000", Style: 1},
        },
    })
    f.SetCellStyle("Sheet1", "A1", "C10", borderStyle)
    
    // 设置背景颜色
    fillStyle, _ := f.NewStyle(&excelize.Style{
        Fill: excelize.Fill{
            Type:    "pattern",
            Color:   []string{"#E0E0E0"},
            Pattern: 1,
        },
    })
    f.SetCellStyle("Sheet1", "A1", "A1", fillStyle)
    
    f.SaveAs("styled.xlsx")
}

3. 创建图表

package main

import (
    "github.com/xuri/excelize/v2"
)

func main() {
    f := excelize.NewFile()
    defer f.Close()
    
    // 添加数据
    for i, row := range [][]interface{}{
        {"产品", "销售量"},
        {"A", 100},
        {"B", 200},
        {"C", 150},
    } {
        for j, cell := range row {
            f.SetCellValue("Sheet1", 
                fmt.Sprintf("%c%d", 'A'+j, i+1), cell)
        }
    }
    
    // 创建柱状图
    if err := f.AddChart("Sheet1", "E1", &excelize.Chart{
        Type: excelize.Col,
        Series: []excelize.ChartSeries{
            {
                Name:       "Sheet1!$B$1",
                Categories: "Sheet1!$A$2:$A$4",
                Values:     "Sheet1!$B$2:$B$4",
            },
        },
        Format: excelize.GraphicOptions{
            ScaleX: 1,
            ScaleY: 1,
        },
        Title: []excelize.ChartTitle{
            {Text: "产品销售量柱状图"},
        },
    }); err != nil {
        fmt.Println(err)
    }
    
    f.SaveAs("chart.xlsx")
}

4. 插入图片

package main

import (
    "fmt"
    "github.com/xuri/excelize/v2"
    _ "image/jpeg"
    _ "image/png"
)

func main() {
    f := excelize.NewFile()
    defer f.Close()
    
    // 插入图片
    if err := f.AddPicture("Sheet1", "A1", "image.png", 
        &excelize.Picture{
            Format:     excelize.PictureType("image/png"),
            Width:      200,
            Height:     150,
            PrintObj:   true,
            LockAspect: true,
        }); err != nil {
        fmt.Println(err)
    }
    
    f.SaveAs("image.xlsx")
}

5. 流式读取大型 Excel 文件

package main

import (
    "fmt"
    "github.com/xuri/excelize/v2"
)

func main() {
    f, err := excelize.OpenFile("large_file.xlsx")
    if err != nil {
        fmt.Println(err)
        return
    }
    defer f.Close()
    
    // 使用流式读取器
    rows, err := f.Rows("Sheet1")
    if err != nil {
        fmt.Println(err)
        return
    }
    defer rows.Close()
    
    for rows.Next() {
        row, err := rows.Columns()
        if err != nil {
            fmt.Println(err)
            continue
        }
        fmt.Println(row)
    }
}

6. 使用公式和函数

package main

import (
    "github.com/xuri/excelize/v2"
)

func main() {
    f := excelize.NewFile()
    defer f.Close()
    
    // 设置数据
    f.SetCellValue("Sheet1", "A1", 10)
    f.SetCellValue("Sheet1", "A2", 20)
    f.SetCellValue("Sheet1", "A3", 30)
    
    // 使用公式
    f.SetCellValue("Sheet1", "A4", "=SUM(A1:A3)")
    f.SetCellValue("Sheet1", "A5", "=AVERAGE(A1:A3)")
    f.SetCellValue("Sheet1", "A6", "=MAX(A1:A3)")
    f.SetCellValue("Sheet1", "A7", "=MIN(A1:A3)")
    
    // 计算公式结果
    result, _ := f.GetCellValue("Sheet1", "A4")
    fmt.Println("SUM 结果:", result)
    
    f.SaveAs("formula.xlsx")
}

最佳实践

1. 内存管理

package main

import (
    "github.com/xuri/excelize/v2"
)

func processLargeExcel(filePath string) error {
    // 使用流式读取避免内存溢出
    f, err := excelize.OpenFile(filePath, excelize.Options{
        Password: "",
    })
    if err != nil {
        return err
    }
    defer f.Close()
    
    // 使用 Rows 流式读取
    rows, err := f.Rows("Sheet1")
    if err != nil {
        return err
    }
    defer rows.Close()
    
    for rows.Next() {
        row, _ := rows.Columns()
        // 处理每一行
        _ = row
    }
    
    return nil
}

2. 错误处理和验证

package main

import (
    "fmt"
    "github.com/xuri/excelize/v2"
)

func safeSetCellValue(f *excelize.File, sheet, cell string, value interface{}) error {
    if err := f.SetCellValue(sheet, cell, value); err != nil {
        return fmt.Errorf("设置单元格 %s 失败: %v", cell, err)
    }
    return nil
}

func validateCellValue(value interface{}) error {
    switch v := value.(type) {
    case string:
        if len(v) > 32767 {
            return fmt.Errorf("字符串长度超过 Excel 限制")
        }
    case int, int64:
        if v < -9007199254740992 || v > 9007199254740992 {
            return fmt.Errorf("数值超出 Excel 范围")
        }
    }
    return nil
}

3. 性能优化

package main

import (
    "github.com/xuri/excelize/v2"
)

func batchWriteCells(f *excelize.File, sheet string, data [][]interface{}) error {
    // 批量写入单元格,减少 I/O 操作
    for i, row := range data {
        for j, cell := range row {
            cellName, _ := excelize.CoordinatesToCellName(j+1, i+1)
            if err := f.SetCellValue(sheet, cellName, cell); err != nil {
                return err
            }
        }
    }
    return nil
}

func optimizeStyle(f *excelize.File) {
    // 复用样式对象,减少内存分配
    headerStyle, _ := f.NewStyle(&excelize.Style{
        Font: &excelize.Font{Bold: true},
    })
    
    // 应用样式到多个单元格
    f.SetCellStyle("Sheet1", "A1", "Z1", headerStyle)
}

4. 模板处理

package main

import (
    "github.com/xuri/excelize/v2"
)

func generateReportFromTemplate(templatePath, outputPath string, data map[string]interface{}) error {
    // 从模板创建新文件
    f, err := excelize.OpenFile(templatePath)
    if err != nil {
        return err
    }
    defer f.Close()
    
    // 替换模板中的占位符
    for key, value := range data {
        f.SetCellValue("Sheet1", key, value)
    }
    
    // 保存为新文件
    return f.SaveAs(outputPath)
}

5. 并发处理

package main

import (
    "sync"
    "github.com/xuri/excelize/v2"
)

func processMultipleSheets(filePath string) error {
    f, err := excelize.OpenFile(filePath)
    if err != nil {
        return err
    }
    defer f.Close()
    
    sheets := f.GetSheetList()
    var wg sync.WaitGroup
    
    // 并发处理多个工作表
    for _, sheet := range sheets {
        wg.Add(1)
        go func(s string) {
            defer wg.Done()
            // 处理工作表
            _ = s
        }(sheet)
    }
    
    wg.Wait()
    return nil
}

使用场景

注意事项

Imaging 图片处理

Imaging 是一个简单而强大的 Go 语言图片处理库,提供了丰富的功能来处理图片,包括缩放、裁剪、旋转、滤镜、颜色调整等。它基于标准库 `image` 和 `image/draw`,易于使用且性能优秀。

核心特性

安装

go get github.com/disintegration/imaging

快速开始

package main

import (
    "log"
    "github.com/disintegration/imaging"
    "image"
    _ "image/jpeg"
    _ "image/png"
)

func main() {
    // 打开图片
    src, err := imaging.Open("input.jpg")
    if err != nil {
        log.Fatal(err)
    }
    defer src.Close()
    
    // 缩放图片
    dst := imaging.Resize(src, 800, 0, imaging.Lanczos)
    
    // 保存图片
    err = imaging.Save(dst, "output.jpg")
    if err != nil {
        log.Fatal(err)
    }
}

常用功能

1. 缩放和裁剪

package main

import (
    "github.com/disintegration/imaging"
)

func main() {
    src, _ := imaging.Open("input.jpg")
    defer src.Close()
    
    // 按宽度缩放,保持宽高比
    resized1 := imaging.Resize(src, 800, 0, imaging.Lanczos)
    
    // 按高度缩放,保持宽高比
    resized2 := imaging.Resize(src, 0, 600, imaging.Lanczos)
    
    // 缩放到指定尺寸(可能变形)
    resized3 := imaging.Resize(src, 800, 600, imaging.Lanczos)
    
    // 裁剪图片
    cropped := imaging.Crop(src, image.Rect(100, 100, 500, 500))
    
    // 缩略图生成
    thumbnail := imaging.Thumbnail(src, 100, 100, imaging.Lanczos)
    
    imaging.Save(resized1, "resized1.jpg")
    imaging.Save(cropped, "cropped.jpg")
    imaging.Save(thumbnail, "thumbnail.jpg")
}

2. 旋转和翻转

package main

import (
    "github.com/disintegration/imaging"
)

func main() {
    src, _ := imaging.Open("input.jpg")
    defer src.Close()
    
    // 旋转 90 度
    rotated90 := imaging.Rotate90(src)
    
    // 旋转 180 度
    rotated180 := imaging.Rotate180(src)
    
    // 旋转 270 度
    rotated270 := imaging.Rotate270(src)
    
    // 旋转任意角度
    rotated45 := imaging.Rotate(src, 45, nil)
    
    // 水平翻转
    flippedH := imaging.FlipH(src)
    
    // 垂直翻转
    flippedV := imaging.FlipV(src)
    
    imaging.Save(rotated90, "rotated90.jpg")
    imaging.Save(flippedH, "flipped_h.jpg")
}

3. 滤镜效果

package main

import (
    "github.com/disintegration/imaging"
)

func main() {
    src, _ := imaging.Open("input.jpg")
    defer src.Close()
    
    // 模糊效果
    blurred := imaging.Blur(src, 5.0)
    
    // 锐化效果
    sharpened := imaging.Sharpen(src, 2.0)
    
    // 调整亮度
    brighter := imaging.AdjustBrightness(src, 20)
    darker := imaging.AdjustBrightness(src, -20)
    
    // 调整对比度
    contrast := imaging.AdjustContrast(src, 20)
    
    // 调整饱和度
    saturated := imaging.AdjustSaturation(src, 50)
    
    // 灰度化
    grayscale := imaging.Grayscale(src)
    
    // 反色
    inverted := imaging.Invert(src)
    
    imaging.Save(blurred, "blurred.jpg")
    imaging.Save(grayscale, "grayscale.jpg")
    imaging.Save(inverted, "inverted.jpg")
}

4. 图片合成

package main

import (
    "github.com/disintegration/imaging"
    "image"
)

func main() {
    // 加载两张图片
    bg, _ := imaging.Open("background.jpg")
    defer bg.Close()
    
    overlay, _ := imaging.Open("overlay.png")
    defer overlay.Close()
    
    // 调整overlay图片大小
    overlay = imaging.Resize(overlay, 200, 0, imaging.Lanczos)
    
    // 在背景图上绘制overlay
    dst := imaging.Overlay(bg, overlay, image.Pt(100, 100), 1.0)
    
    // 创建水印效果(半透明)
    watermark := imaging.Overlay(bg, overlay, image.Pt(100, 100), 0.5)
    
    imaging.Save(dst, "overlay.jpg")
    imaging.Save(watermark, "watermark.jpg")
}

5. 批量处理

package main

import (
    "os"
    "path/filepath"
    "github.com/disintegration/imaging"
)

func batchProcessImages(inputDir, outputDir string) error {
    // 遍历输入目录
    err := filepath.Walk(inputDir, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }
        
        // 跳过目录
        if info.IsDir() {
            return nil
        }
        
        // 只处理图片文件
        ext := filepath.Ext(path)
        if ext != ".jpg" && ext != ".png" {
            return nil
        }
        
        // 打开图片
        src, err := imaging.Open(path)
        if err != nil {
            return err
        }
        defer src.Close()
        
        // 调整大小
        dst := imaging.Resize(src, 800, 0, imaging.Lanczos)
        
        // 保存到输出目录
        outputPath := filepath.Join(outputDir, filepath.Base(path))
        return imaging.Save(dst, outputPath)
    })
    
    return err
}

最佳实践

1. 内存管理

package main

import (
    "github.com/disintegration/imaging"
)

func processLargeImage(filePath string) error {
    // 使用 io.Reader 逐步读取大图片
    src, err := imaging.Open(filePath)
    if err != nil {
        return err
    }
    defer src.Close()
    
    // 处理完后立即释放资源
    dst := imaging.Resize(src, 800, 0, imaging.Lanczos)
    
    // 保存后关闭
    err = imaging.Save(dst, "output.jpg")
    if err != nil {
        return err
    }
    
    // 显式释放内存
    dst = nil
    return nil
}

2. 错误处理

package main

import (
    "fmt"
    "github.com/disintegration/imaging"
)

func safeProcessImage(inputPath, outputPath string) error {
    // 验证输入文件
    src, err := imaging.Open(inputPath)
    if err != nil {
        return fmt.Errorf("无法打开图片 %s: %v", inputPath, err)
    }
    defer src.Close()
    
    // 验证输出路径
    if outputPath == "" {
        return fmt.Errorf("输出路径不能为空")
    }
    
    // 处理图片
    dst := imaging.Resize(src, 800, 0, imaging.Lanczos)
    
    // 保存并处理错误
    if err := imaging.Save(dst, outputPath); err != nil {
        return fmt.Errorf("保存图片失败: %v", err)
    }
    
    return nil
}

3. 性能优化

package main

import (
    "github.com/disintegration/imaging"
    "image"
)

func optimizeProcessing(src image.Image) image.Image {
    // 使用合适的插值算法
    // Lanczos: 高质量但慢
    // CatmullRom: 平衡质量和速度
    // Linear: 快速但质量较低
    
    // 根据图片大小选择算法
    bounds := src.Bounds()
    if bounds.Dx() > 2000 || bounds.Dy() > 2000 {
        // 大图片使用快速算法
        return imaging.Resize(src, 800, 0, imaging.Linear)
    } else {
        // 小图片使用高质量算法
        return imaging.Resize(src, 800, 0, imaging.Lanczos)
    }
}

4. 格式转换

package main

import (
    "path/filepath"
    "github.com/disintegration/imaging"
)

func convertFormat(inputPath, outputFormat string) error {
    src, err := imaging.Open(inputPath)
    if err != nil {
        return err
    }
    defer src.Close()
    
    // 构建输出路径
    outputPath := filepath.Join(
        filepath.Dir(inputPath),
        filepath.Base(inputPath[:len(inputPath)-len(filepath.Ext(inputPath))])+"."+outputFormat,
    )
    
    // 根据格式调整质量
    if outputFormat == "jpg" {
        // JPEG 格式,使用默认质量
        return imaging.Save(src, outputPath)
    } else if outputFormat == "png" {
        // PNG 格式,无损压缩
        return imaging.Save(src, outputPath)
    }
    
    return nil
}

使用场景

注意事项

Testify 测试框架

Testify 是一个 Go 语言流行的测试工具包,提供了断言、模拟(Mock)、套件(Suite)等功能,让编写测试更加简单和强大。它提供了丰富的断言方法、模拟对象和测试套件功能,是 Go 测试生态中不可或缺的工具。

核心特性

安装

go get github.com/stretchr/testify

快速开始

package math

import (
    "testing"
    "github.com/stretchr/testify/assert"
)

func TestAdd(t *testing.T) {
    result := Add(2, 3)
    assert.Equal(t, 5, result, "2 + 3 应该等于 5")
}

常用功能

1. 基本断言

package main

import (
    "testing"
    "github.com/stretchr/testify/assert"
)

func TestAssertions(t *testing.T) {
    // 相等断言
    assert.Equal(t, "hello", "hello")
    assert.NotEqual(t, "hello", "world")
    
    // 布尔断言
    assert.True(t, true)
    assert.False(t, false)
    
    // 数值断言
    assert.Equal(t, 42, 42)
    assert.NotEqual(t, 42, 43)
    
    // nil 断言
    var ptr *int
    assert.Nil(t, ptr)
    assert.NotNil(t, &ptr)
    
    // 错误断言
    err := someFunction()
    assert.NoError(t, err)
    assert.Error(t, err)
}

2. 集合断言

package main

import (
    "testing"
    "github.com/stretchr/testify/assert"
)

func TestCollectionAssertions(t *testing.T) {
    // 切片断言
    slice := []int{1, 2, 3}
    assert.Contains(t, slice, 2)
    assert.NotContains(t, slice, 4)
    assert.Len(t, slice, 3)
    assert.Empty(t, []int{})
    assert.NotEmpty(t, slice)
    
    // Map 断言
    m := map[string]int{"a": 1, "b": 2}
    assert.ContainsKey(t, m, "a")
    assert.NotContainsKey(t, m, "c")
    assert.Len(t, m, 2)
}

3. 对象断言

package main

import (
    "testing"
    "github.com/stretchr/testify/assert"
)

type User struct {
    Name string
    Age  int
}

func TestObjectAssertions(t *testing.T) {
    user1 := User{Name: "Alice", Age: 30}
    user2 := User{Name: "Alice", Age: 30}
    user3 := User{Name: "Bob", Age: 25}
    
    // 对象相等断言
    assert.Equal(t, user1, user2)
    assert.NotEqual(t, user1, user3)
    
    // 指针断言
    ptr1 := &user1
    ptr2 := &user2
    assert.Same(t, ptr1, ptr1)
    assert.NotSame(t, ptr1, ptr2)
    
    // 类型断言
    var interface{} = user1
    assert.IsType(t, User{}, interface{})
    assert.Implements(t, (*interface{})(nil), user1)
}

4. Mock 对象

package main

import (
    "testing"
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/mock"
)

// 定义接口
type Database interface {
    GetUser(id int) (*User, error)
    SaveUser(user *User) error
}

// 创建 Mock
type MockDatabase struct {
    mock.Mock
}

func (m *MockDatabase) GetUser(id int) (*User, error) {
    args := m.Called(id)
    if args.Get(0) == nil {
        return nil, args.Error(1)
    }
    return args.Get(0).(*User), args.Error(1)
}

func (m *MockDatabase) SaveUser(user *User) error {
    args := m.Called(user)
    return args.Error(0)
}

func TestUserService(t *testing.T) {
    // 创建 Mock 对象
    mockDB := new(MockDatabase)
    
    // 设置期望
    mockDB.On("GetUser", 1).Return(&User{Name: "Alice"}, nil)
    mockDB.On("SaveUser", mock.Anything).Return(nil)
    
    // 使用 Mock 进行测试
    user, err := mockDB.GetUser(1)
    assert.NoError(t, err)
    assert.Equal(t, "Alice", user.Name)
    
    err = mockDB.SaveUser(&User{Name: "Bob"})
    assert.NoError(t, err)
    
    // 验证期望
    mockDB.AssertExpectations(t)
}

5. 测试套件

package main

import (
    "testing"
    "github.com/stretchr/testify/suite"
)

type CalculatorTestSuite struct {
    suite.Suite
    calc *Calculator
}

func (suite *CalculatorTestSuite) SetupTest() {
    // 每个测试前执行
    suite.calc = NewCalculator()
}

func (suite *CalculatorTestSuite) TearDownTest() {
    // 每个测试后执行
    suite.calc = nil
}

func (suite *CalculatorTestSuite) SetupSuite() {
    // 套件开始前执行
}

func (suite *CalculatorTestSuite) TearDownSuite() {
    // 套件结束后执行
}

func (suite *CalculatorTestSuite) TestAdd() {
    result := suite.calc.Add(2, 3)
    suite.Equal(5, result)
}

func (suite *CalculatorTestSuite) TestSubtract() {
    result := suite.calc.Subtract(5, 3)
    suite.Equal(2, result)
}

func TestCalculatorTestSuite(t *testing.T) {
    suite.Run(t, new(CalculatorTestSuite))
}

6. HTTP 测试

package main

import (
    "net/http"
    "net/http/httptest"
    "testing"
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/http"
)

func TestHTTPHandler(t *testing.T) {
    // 创建测试服务器
    router := SetupRouter()
    w := httptest.NewRecorder()
    req, _ := http.NewRequest("GET", "/users/1", nil)
    
    // 执行请求
    router.ServeHTTP(w, req)
    
    // 断言响应
    assert.Equal(t, http.StatusOK, w.Code)
    assert.JSONEq(t, `{"id":1,"name":"Alice"}`, w.Body.String())
    
    // 使用 httpassert
    http.AssertSuccess(t, w.Code)
    http.AssertJSONPath(t, w, "$.name", "Alice")
}

最佳实践

1. 测试组织

package main

import (
    "testing"
    "github.com/stretchr/testify/assert"
)

// 使用子测试组织测试
func TestCalculator(t *testing.T) {
    t.Run("Add", func(t *testing.T) {
        calc := NewCalculator()
        result := calc.Add(2, 3)
        assert.Equal(t, 5, result)
    })
    
    t.Run("Subtract", func(t *testing.T) {
        calc := NewCalculator()
        result := calc.Subtract(5, 3)
        assert.Equal(t, 2, result)
    })
}

2. 表驱动测试

package main

import (
    "testing"
    "github.com/stretchr/testify/assert"
)

func TestAdd(t *testing.T) {
    tests := []struct {
        name     string
        a, b     int
        expected int
    }{
        {"positive", 2, 3, 5},
        {"negative", -2, -3, -5},
        {"mixed", 2, -3, -1},
        {"zero", 0, 0, 0},
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result := Add(tt.a, tt.b)
            assert.Equal(t, tt.expected, result, "Add(%d, %d) = %d; want %d", tt.a, tt.b, result, tt.expected)
        })
    }
}

3. 测试覆盖率

package main

import (
    "testing"
    "github.com/stretchr/testify/assert"
)

// 测试所有分支
func TestDivide(t *testing.T) {
    // 正常情况
    result, err := Divide(10, 2)
    assert.Equal(t, 5, result)
    assert.NoError(t, err)
    
    // 除零错误
    result, err = Divide(10, 0)
    assert.Zero(t, result)
    assert.Error(t, err)
    
    // 负数除法
    result, err = Divide(-10, 2)
    assert.Equal(t, -5, result)
    assert.NoError(t, err)
}

4. Mock 最佳实践

package main

import (
    "testing"
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/mock"
)

func TestWithProperMocking(t *testing.T) {
    mockDB := new(MockDatabase)
    
    // 明确设置期望
    mockDB.On("GetUser", 1).Return(&User{Name: "Alice"}, nil).Once()
    
    // 执行测试
    user, err := mockDB.GetUser(1)
    assert.NoError(t, err)
    assert.Equal(t, "Alice", user.Name)
    
    // 验证所有期望都被满足
    mockDB.AssertExpectations(t)
    
    // 测试错误情况
    mockDB.On("GetUser", 999).Return(nil, assert.AnError).Once()
    
    user, err = mockDB.GetUser(999)
    assert.Nil(t, user)
    assert.Error(t, err)
    
    mockDB.AssertExpectations(t)
}

5. 测试辅助函数

package main

import (
    "testing"
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/require"
)

// 辅助函数创建测试数据
func createTestUser(name string, age int) *User {
    return &User{
        Name: name,
        Age:  age,
        ID:   generateTestID(),
    }
}

func generateTestID() int {
    return 42
}

func TestWithHelpers(t *testing.T) {
    // 使用 require 立即失败
    user := createTestUser("Alice", 30)
    require.NotNil(t, user)
    
    // 使用 assert 继续执行
    assert.Equal(t, "Alice", user.Name)
    assert.Equal(t, 30, user.Age)
}

使用场景

注意事项

GoDS 数据结构

GoDS(Go Data Structures)是一个纯 Go 语言实现的、高性能的开源数据结构和算法库。它提供了丰富的数据结构,如链表、栈、队列、集合、树、堆、哈希表等,以及多种算法实现。GoDS 的设计目标是提供与 Go 标准库兼容的 API,同时提供更好的性能和更丰富的功能。

核心特性

安装

go get github.com/emirpasic/gods/v2

快速开始

package main

import (
    "fmt"
    "github.com/emirpasic/gods/v2/list"
)

func main() {
    // 创建链表
    l := list.New()
    l.PushBack(1)
    l.PushBack(2)
    l.PushBack(3)
    
    // 遍历链表
    it := l.Front()
    for it.Next() {
        fmt.Println(it.Value())
    }
}

常用数据结构

1. 链表(List)

package main

import (
    "fmt"
    "github.com/emirpasic/gods/v2/list"
)

func main() {
    // 创建链表
    l := list.New()
    
    // 添加元素
    l.PushBack(1)
    l.PushFront(0)
    l.PushBack(2)
    
    // 访问元素
    fmt.Println("Front:", l.Front().Value())
    fmt.Println("Back:", l.Back().Value())
    
    // 移除元素
    l.Remove(l.Front())
    
    // 遍历
    for it := l.Front(); it != nil; it = it.Next() {
        fmt.Println(it.Value())
    }
}

2. 栈(Stack)

package main

import (
    "fmt"
    "github.com/emirpasic/gods/v2/stack"
)

func main() {
    // 创建栈
    s := stack.New()
    
    // 入栈
    s.Push(1)
    s.Push(2)
    s.Push(3)
    
    // 查看栈顶
    if top, ok := s.Peek(); ok {
        fmt.Println("Top:", top)
    }
    
    // 出栈
    if value, ok := s.Pop(); ok {
        fmt.Println("Popped:", value)
    }
    
    // 检查栈是否为空
    fmt.Println("Empty:", s.Empty())
}

3. 队列(Queue)

package main

import (
    "fmt"
    "github.com/emirpasic/gods/v2/queue"
)

func main() {
    // 创建队列
    q := queue.New()
    
    // 入队
    q.Enqueue(1)
    q.Enqueue(2)
    q.Enqueue(3)
    
    // 查看队首
    if front, ok := q.Peek(); ok {
        fmt.Println("Front:", front)
    }
    
    // 出队
    if value, ok := q.Dequeue(); ok {
        fmt.Println("Dequeued:", value)
    }
    
    // 检查队列大小
    fmt.Println("Size:", q.Size())
}

4. 集合(Set)

package main

import (
    "fmt"
    "github.com/emirpasic/gods/v2/hashset"
)

func main {
    // 创建集合
    set := hashset.New()
    
    // 添加元素
    set.Add(1)
    set.Add(2)
    set.Add(3)
    
    // 检查元素是否存在
    fmt.Println("Contains 2:", set.Contains(2))
    
    // 移除元素
    set.Remove(2)
    
    // 遍历集合
    set.Each(func(value interface{}) bool {
        fmt.Println(value)
        return true
    })
}

5. 树(Tree)

pre>package main import ( "fmt" "github.com/emirpasic/gods/v2/treemap" ) func main() { // 创建树形映射 tree := treemap.NewWithStringComparator() // 添加键值对 tree.Put("a", 1) tree.Put("b", 2) tree.Put("c", 3) // 获取值 if value, ok := tree.Get("b"); ok { fmt.Println("Value for 'b':", value) } // 遍历树 it := tree.Iterator() for it.Begin(); it.End(); it.Next()() { key, value := it.Key(), it.Value() fmt.Printf("%s: %v\n", key, value) } }

6. 堆(Heap)

package main

import (
    "container/heap"
    "github.com/emirpasic/gods/v2/treemap"
)

type IntHeap []int

func (h IntHeap) Len() int           { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h IntHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }
func (h IntHeap) Push(x int)      { heap.Push(h, x) }
func (h *IntHeap) Pop() int     { return heap.Pop(h) }

func main() {
    h := &IntHeap{3, 1, 4, 1, 5, 9}
    heap.Init(h)
    
    // 弹出最小值
    for h.Len() > 0 {
        fmt.Println(heap.Pop(h))
    }
}

高级功能

1. 并发安全的数据结构

package main

import (
    "fmt"
    "sync"
    "github.com/emirpasic/gods/v2/list"
)

func main() {
    // 创建并发安全的链表
    l := list.New()
    var mu sync.Mutex
    
    // 并发操作
    for i := 0; i < 10; i++ {
        go func(n int) {
            mu.Lock()
            defer mu.Unlock()
            l.PushBack(n)
        }(i)
    }
}

2. 自定义比较器

package main

import (
    "github.com/emirpasic/gods/v2/treemap"
    "github.com/emirpasic/gods/v2/utils"
)

type User struct {
    Name string
    Age  int
}

// 自定义比较器
type ByAge struct{ users []*User }

func (a ByAge) Len() int return len(a.users) }
func (a ByAge) Less(i, j int) bool return a.users[i].Age < a.users[j].Age }
func (a ByAge) Swap(i, j int)      { a.users[i], a.users[j] = a.users[j], a.users[i] }

func main() {
    // 使用自定义比较器创建树
    tree := treemap.NewWith(utils.Comparator(func(a, b interface{}) int {
        return a.(int) - b.(int)
    }))
    
    tree.Put(10, "ten")
    tree.Put(5, "five")
}

3. 序列化

package main

import (
    "encoding/json"
    "fmt"
    "github.com/emirpasic/gods/v2/list"
)

func main() {
    l := list.New()
    l.PushBack(1)
    l.PushBack(2)
    l.PushBack(3)
    
    // 转换为切片
    values := l.Values()
    
    // JSON 序列化
    data, _ := json.Marshal(values)
    fmt.Println(String(data))
}

最佳实践

1. 选择合适的数据结构

package main

import (
    "github.com/emirpasic/gods/v2/list"
    "github.com/emirpasic/gods/v2/hashmap"
    "github.com/emirpasic/gods/v2/hashset"
)

func chooseDataStructure() {
    // 频繁的插入和删除 - 使用链表
    l := list.New()
    
    // 需要快速查找 - 使用哈希表
    m := hashmap.New()
    
    // 需要唯一元素 - 使用集合
    s := hashset.New()
    
    // 需要排序 - 使用树
}

2. 内存管理

package main

import (
    "github.com/emirpasic/gods/v2/list"
)

func manageMemory() {
    l := list.New()
    
    // 及时清理不需要的元素
    for l.Size() > 1000 {
        l.Remove(l.Front())
    }
    
    // 使用迭代器避免创建不必要的切片
    for it := l.Front(); it != nil; it = it.Next() {
        _ = it.Value()
    }
}

3. 性能优化

预计算容量:
package main

import (
    "github.com/emirpasic/gods/v2/hashmap"
)

func optimizePerformance() {
    // 预分配容量
    m := hashmap.New()
    m.Resize(1000)
    
    // 批量操作
    for i := 0; i < 1000; i++ {
        m.Put(i, i*2)
    }
}

4. 迭代器使用

package main

import (
    "github.com/emirpasic/gods/v2/list"
)

func useIterator() {
    l := list.New()
    l.PushBack(1)
    l.PushBack(2)
    l.PushBack(3)
    
    // 正向迭代
    for it := l.Front(); it != nil(); it = it.Next()() {
        _ = it.Value()
    }
    
    // 反向迭代
    for it := l.Back(); it != nil(); it = it.Prev()() {
        _ = it.Value()
    }
}

5. 与标准库对比

package main

import (
    "container/list"
    "github.com/emirpasic/gods/v2/list"
)

func compareWithStdLib() {
    // 标准库链表
    stdList := list.New()
    stdList.PushBack(1)
    
    // GoDS 链表
    godsList := list.New()
    godsList.PushBack(1)
    
    // GoDS 提供了更丰富的功能
    // 如:Values()、Each()、Select() 等
}

使用场景

注意事项

  • 根据使用场景选择合适的数据结构
  • 注意内存使用,特别是对于大型数据集
  • 并发环境中使用适当的同步机制
  • GoDS 的 API 与标准库不同,需要适应
  • 定期检查性能,确保选择的数据结构合适
  • 注意迭代器的使用,避免在迭代时修改数据结构

gg 图形库

gg 是一个纯 Go 语言实现的高性能 2D 图形库,提供了丰富的绘图功能,包括路径绘制、图像处理、文本渲染、变换、渐变、透明度等。gg 的设计目标是提供简单易用、性能优秀的 2D 图形处理能力,适用于数据可视化、游戏开发、图像处理等场景。

核心特性

  • 纯 Go 实现:不依赖任何外部 C 库,完全用 Go 语言实现
  • 高性能:优化的渲染引擎,支持硬件加速
  • 丰富的绘图功能:支持路径、形状、文本、图像等
  • 变换支持:支持平移、旋转、缩放等变换
  • 渐变和透明度:支持线性渐变、径向渐变、透明度
  • 多种输出格式:支持 PNG、JPEG、GIF、TIFF 等格式
  • 字体支持:支持 TrueType 和 OpenType 字体

安装

go get github.com/fogleman/gg

快速开始

package main

import (
    "github.com/fogleman/gg"
)

func main() {
    // 创建 800x600 的画布
    dc := gg.NewContext(800, 600)
    
    // 设置背景色
    dc.SetRGB(1, 1, 1)
    dc.Clear()
    
    // 绘制红色矩形
    dc.SetRGB255(255, 0, 0)
    dc.DrawRectangle(100, 100, 200, 150)
    dc.Fill()
    
    // 保存为 PNG
    dc.SavePNG("output.png")
}

常用功能

1. 基本形状绘制

package main

import (
    "github.com/fogleman/gg"
)

func main() {
    dc := gg.NewContext(800, 600)
    dc.SetRGB(1, 1, 1)
    dc.Clear()
    
    // 绘制矩形
    dc.SetRGB255(255, 0, 0)
    dc.DrawRectangle(50, 50, 200, 150)
    dc.Fill()
    
    // 绘制圆形
    dc.SetRGB255(0, 255, 0)
    dc.DrawCircle(400, 300, 100)
    dc.Fill()
    
    // 绘制椭圆
    dc.SetRGB255(0, 0, 255)
    dc.DrawEllipse(600, 200, 100, 50)
    dc.Fill()
    
    // 绘制线条
    dc.SetRGB255(0, 0, 0)
    dc.SetLineWidth(5)
    dc.DrawLine(50, 400, 750, 400)
    dc.Stroke()
    
    dc.SavePNG("shapes.png")
}

2. 路径绘制

package main

import (
    "github.com/fogleman/gg"
)

func main() {
    dc := gg.NewContext(800, 600)
    dc.SetRGB(1, 1, 1)
    dc.Clear()
    
    // 绘制多边形路径
    dc.SetRGB255(255, 0, 0)
    dc.MoveTo(100, 100)
    dc.LineTo(200, 50)
    dc.LineTo(300, 100)
    dc.LineTo(300, 200)
    dc.LineTo(200, 250)
    dc.LineTo(100, 200)
    dc.ClosePath()
    dc.Fill()
    
    // 绘制贝塞尔曲线
    dc.SetRGB255(0, 0, 255)
    dc.SetLineWidth(3)
    dc.MoveTo(400, 300)
    dc.QuadCurveTo(500, 200, 600, 300)
    dc.CubicCurveTo(650, 350, 700, 250, 750, 300)
    dc.Stroke()
    
    dc.SavePNG("paths.png")
}

3. 文本渲染

package main

import (
    "github.com/fogleman/gg"
)

func main() {
    dc := gg.NewContext(800, 600)
    dc.SetRGB(1, 1, 1)
    dc.Clear()
    
    // 加载字体
    if err := dc.LoadFontFace("arial.ttf", 48); err != nil {
        panic(err)
    }
    
    // 绘制文本
    dc.SetRGB255(0, 0, 0)
    dc.DrawString("Hello, gg!", 100, 100)
    
    // 绘制带阴影的文本
    dc.SetRGB255(200, 200, 200)
    dc.DrawString("Shadow Text", 102, 202)
    dc.SetRGB255(0, 0, 0)
    dc.DrawString("Shadow Text", 100, 200)
    
    // 绘制旋转的文本
    dc.Push()
    dc.RotateAbout(gg.Radians(45), 400, 400)
    dc.SetRGB255(255, 0, 0)
    dc.DrawString("Rotated Text", 350, 400)
    dc.Pop()
    
    dc.SavePNG("text.png")
}

4. 图像处理

package main

import (
    "github.com/fogleman/gg"
)

func main() {
    dc := gg.NewContext(800, 600)
    dc.SetRGB(1, 1, 1)
    dc.Clear()
    
    // 加载图像
    img, err := gg.LoadImage("input.png")
    if err != nil {
        panic(err)
    }
    
    // 绘制图像(原始大小)
    dc.DrawImage(img, 50, 50)
    
    // 绘制缩放后的图像
    dc.DrawImageAnchored(img, 400, 300, 0.5, 0.5)
    
    // 绘制旋转后的图像
    dc.Push()
    dc.RotateAbout(gg.Radians(30), 600, 400)
    dc.DrawImageAnchored(img, 600, 400, 0.5, 0.5)
    dc.Pop()
    
    dc.SavePNG("image.png")
}

5. 渐变和透明度

package main

import (
    "github.com/fogleman/gg"
)

func main() {
    dc := gg.NewContext(800, 600)
    dc.SetRGB(1, 1, 1)
    dc.Clear()
    
    // 线性渐变
    gradient := gg.NewLinearGradient(0, 0, 400, 400)
    gradient.AddColorStop(0, gg.NewRGBA(255, 0, 0, 255))
    gradient.AddColorStop(1, gg.NewRGBA(0, 0, 255, 255))
    dc.SetFillStyle(gradient)
    dc.DrawRectangle(50, 50, 300, 300)
    dc.Fill()
    
    // 径向渐变
    radial := gg.NewRadialGradient(600, 300, 0, 600, 300, 150)
    radial.AddColorStop(0, gg.NewRGBA(255, 255, 0, 255))
    radial.AddColorStop(1, gg.NewRGBA(255, 0, 0, 255))
    dc.SetFillStyle(radial)
    dc.DrawCircle(600, 300, 150)
    dc.Fill()
    
    // 透明度
    dc.SetRGBA255(0, 255, 0, 128)
    dc.DrawRectangle(100, 400, 200, 150)
    dc.Fill()
    
    dc.SavePNG("gradient.png")
}

6. 变换操作

package main

import (
    "github.com/fogleman/gg"
)

func main() {
    dc := gg.NewContext(800, 600)
    dc.SetRGB(1, 1, 1)
    dc.Clear()
    
    // 平移
    dc.Push()
    dc.Translate(100, 100)
    dc.SetRGB255(255, 0, 0)
    dc.DrawRectangle(0, 0, 100, 100)
    dc.Fill()
    dc.Pop()
    
    // 旋转
    dc.Push()
    dc.Translate(300, 300)
    dc.Rotate(gg.Radians(45))
    dc.SetRGB255(0, 255, 0)
    dc.DrawRectangle(-50, -50, 100, 100)
    dc.Fill()
    dc.Pop()
    
    // 缩放
    dc.Push()
    dc.Translate(500, 400)
    dc.Scale(2, 0.5)
    dc.SetRGB255(0, 0, 255)
    dc.DrawCircle(0, 0, 50)
    dc.Fill()
    dc.Pop()
    
    dc.SavePNG("transform.png")
}

高级功能

1. 裁剪

package main

import (
    "github.com/fogleman/gg"
)

func main() {
    dc := gg.NewContext(800, 600)
    dc.SetRGB(1, 1, 1)
    dc.Clear()
    
    // 绘制圆形裁剪区域
    dc.DrawCircle(400, 300, 200)
    dc.Clip()
    
    // 在裁剪区域内绘制
    for i := 0; i < 10; i++ {
        dc.SetRGB255(byte(i*25), 0, byte(255-i*25))
        dc.DrawRectangle(float64(i*80), 0, 80, 600)
        dc.Fill()
    }
    
    dc.SavePNG("clip.png")
}

2. 阴影

package main

import (
    "github.com/fogleman/gg"
)

func main() {
    dc := gg.NewContext(800, 600)
    dc.SetRGB(1, 1, 1)
    dc.Clear()
    
    // 绘制阴影
    dc.SetRGBA255(0, 0, 0, 100)
    dc.DrawRoundedRectangle(110, 110, 200, 150, 20)
    dc.Fill()
    
    // 绘制主体
    dc.SetRGB255(255, 0, 0)
    dc.DrawRoundedRectangle(100, 100, 200, 150, 20)
    dc.Fill()
    
    dc.SavePNG("shadow.png")
}

3. 混合模式

package main

import (
    "github.com/fogleman/gg"
)

func main() {
    dc := gg.NewContext(800, 600)
    dc.SetRGB(1, 1, 1)
    dc.Clear()
    
    // 绘制红色圆形
    dc.SetRGB255(255, 0, 0)
    dc.DrawCircle(300, 300, 150)
    dc.Fill()
    
    // 使用混合模式绘制蓝色圆形
    dc.SetCompositionOp(gg.CompositionOperatorXor)
    dc.SetRGB255(0, 0, 255)
    dc.DrawCircle(500, 300, 150)
    dc.Fill()
    
    dc.SavePNG("blend.png")
}

最佳实践

1. 性能优化

package main

import (
    "github.com/fogleman/gg"
)

func optimizePerformance() {
    dc := gg.NewContext(800, 600)
    
    // 批量绘制
    for i := 0; i < 1000; i++ {
        dc.MoveTo(float64(i), float64(i%600))
        dc.LineTo(float64(i+1), float64((i+1)%600))
    }
    dc.Stroke()
    
    // 避免频繁的状态切换
    dc.SetRGB255(255, 0, 0)
    for i := 0; i < 100; i++ {
        dc.DrawCircle(float64(i*8), 300, 5)
    }
    dc.Fill()
}

2. 资源管理

package main

import (
    "github.com/fogleman/gg"
)

func manageResources() {
    // 重用 Context
    dc := gg.NewContext(800, 600)
    
    // 加载图像一次,多次使用
    img, _ := gg.LoadImage("sprite.png")
    
    for i := 0; i < 10; i++ {
        dc.Clear()
        dc.DrawImage(img, float64(i*80), 100)
        dc.SavePNG(Sprintf("frame%d.png", i))
    }
}

3. 错误处理

package main

import (
    "log"
    "github.com/fogleman/gg"
)

func handleErrors() {
    dc := gg.NewContext(800, 600)
    
    // 加载字体
    if err := dc.LoadFontFace("font.ttf", 24); err != nil {
        log.Fatal("Failed to load font:", err)
    }
    
    // 保存图像
    if err := dc.SavePNG("output.png"); err != nil {
        log.Fatal("Failed to save image:", err)
    }
}

4. 使用模板

package main

import (
    "github.com/fogleman/gg"
)

func useTemplates() {
    // 创建模板
    templateDC := gg.NewContext(800, 600)
    templateDC.SetRGB(1, 1, 1)
    templateDC.Clear()
    templateDC.DrawRectangle(10, 10, 780, 580)
    templateDC.SetLineWidth(5)
    templateDC.Stroke()
    
    // 使用模板创建新图像
    dc := gg.NewContext(800, 600)
    dc.DrawImage(templateDC.Image(), 0, 0)
    
    // 添加自定义内容
    dc.SetRGB255(255, 0, 0)
    dc.DrawCircle(400, 300, 100)
    dc.Fill()
}

使用场景

  • 数据可视化:创建图表、图形、仪表盘
  • 图像处理:图像编辑、滤镜、合成
  • 游戏开发:2D 游戏渲染、精灵绘制
  • 报告生成:生成 PDF、图片报告
  • Web 图形:生成网站图形、缩略图
  • 艺术创作:生成艺术、创意设计

注意事项

  • 字体文件需要单独提供,确保字体文件存在
  • 大尺寸图像可能消耗较多内存
  • 频繁的保存操作会影响性能
  • 注意坐标系统的方向(左上角为原点)
  • 变换操作会影响后续所有绘制,使用 Push/Pop 管理状态
  • 路径操作需要正确关闭路径
  • 透明度和混合模式可能影响性能

gRPC RPC框架

gRPC 是 Google 开发的高性能、开源的通用 RPC 框架,基于 HTTP/2 和 Protocol Buffers。它支持多种语言,提供了简单、高效的远程调用方式,广泛应用于微服务架构中。

核心特性

  • 基于 HTTP/2:支持双向流、多路复用、头部压缩
  • 使用 Protobuf:高效的二进制序列化格式
  • 多语言支持:支持 Go、Java、Python、C++ 等多种语言
  • 四种服务方法:一元调用、服务端流、客户端流、双向流
  • 拦截器:支持请求拦截和响应处理

安装

go get -u google.golang.org/grpc
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

定义服务

syntax = "proto3";

package example;

option go_package = "./example";

service Greeter {
    rpc SayHello (HelloRequest) returns (HelloReply) {}
    rpc SayHelloStream (HelloRequest) returns (stream HelloReply) {}
}

message HelloRequest {
    string name = 1;
}

message HelloReply {
    string message = 1;
}

实现服务端

package main

import (
    "context"
    "log"
    "net"
    "google.golang.org/grpc"
    pb "path/to/your/proto"
)

type server struct {
    pb.UnimplementedGreeterServer
}

func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    log.Printf("Received: %v", in.GetName())
    return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}

func (s *server) SayHelloStream(in *pb.HelloRequest, stream pb.Greeter_SayHelloStreamServer) error {
    for i := 0; i < 5; i++ {
        stream.Send(&pb.HelloReply{
            Message: "Hello " + in.GetName() + " " + string(rune('0'+i)),
        })
    }
    return nil
}

func main() {
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    
    s := grpc.NewServer()
    pb.RegisterGreeterServer(s, &server{})
    
    log.Printf("server listening at %v", lis.Addr())
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

实现客户端

package main

import (
    "context"
    "io"
    "log"
    "time"
    "google.golang.org/grpc"
    pb "path/to/your/proto"
)

func main() {
    conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil {
        log.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()
    
    c := pb.NewGreeterClient(conn)
    
    // 一元调用
    ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    defer cancel()
    
    r, err := c.SayHello(ctx, &pb.HelloRequest{Name: "World"})
    if err != nil {
        log.Fatalf("could not greet: %v", err)
    }
    log.Printf("Greeting: %s", r.GetMessage())
    
    // 流式调用
    stream, err := c.SayHelloStream(ctx, &pb.HelloRequest{Name: "Stream"})
    if err != nil {
        log.Fatalf("could not stream: %v", err)
    }
    
    for {
        reply, err := stream.Recv()
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Fatalf("failed to recv: %v", err)
        }
        log.Printf("Stream reply: %s", reply.GetMessage())
    }
}

Gateway API网关

gRPC-Gateway 是 Google 提供的一个插件,用于将 gRPC 服务转换为 RESTful API。它允许客户端通过 HTTP/JSON 调用 gRPC 服务,同时保持 gRPC 的高性能和类型安全。

核心特性

  • 自动生成:从 Protobuf 定义自动生成 RESTful API
  • 双向转换:支持 HTTP/JSON 到 gRPC 的双向转换
  • OpenAPI 支持:自动生成 OpenAPI (Swagger) 文档
  • 反向代理:作为反向代理转发请求到 gRPC 服务
  • 中间件支持:支持认证、日志、CORS 等中间件

安装

go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest
go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@latest

定义服务

syntax = "proto3";

package example;

option go_package = "./example";

import "google/api/annotations.proto";

service Greeter {
    rpc SayHello (HelloRequest) returns (HelloReply) {
        option (google.api.http) = {
            get: "/v1/hello/{name}"
        };
    }
    
    rpc CreateUser (CreateUserRequest) returns (User) {
        option (google.api.http) = {
            post: "/v1/users"
            body: "*"
        };
    }
}

message HelloRequest {
    string name = 1;
}

message HelloReply {
    string message = 1;
}

message CreateUserRequest {
    string name = 1;
    string email = 2;
}

message User {
    string id = 1;
    string name = 2;
    string email = 3;
}

生成代码

protoc -I. \
    --go_out=. --go_opt=paths=source_relative \
    --go-grpc_out=. --go-grpc_opt=paths=source_relative \
    --grpc-gateway_out=. --grpc-gateway_opt=paths=source_relative \
    --openapiv2_out=. --openapiv2_opt=paths=source_relative \
    service.proto

实现 Gateway 服务

package main

import (
    "context"
    "log"
    "net/http"
    "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
    gw "path/to/your/proto"
)

func main() {
    // 创建 gRPC 连接
    conn, err := grpc.DialContext(
        context.Background(),
        "localhost:50051",
        grpc.WithTransportCredentials(insecure.NewCredentials()),
    )
    if err != nil {
        log.Fatalf("failed to dial server: %v", err)
    }
    
    // 创建 Gateway mux
    mux := runtime.NewServeMux()
    
    // 注册服务
    err = gw.RegisterGreeterHandler(context.Background(), mux, conn)
    if err != nil {
        log.Fatalf("failed to register handler: %v", err)
    }
    
    // 启动 HTTP 服务
    addr := ":8080"
    log.Printf("Gateway server listening on %s", addr)
    
    if err := http.ListenAndServe(addr, mux); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

添加中间件

package main

import (
    "log"
    "net/http"
)

// CORS 中间件
func corsMiddleware(h 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, PATCH, DELETE, OPTIONS")
        w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
        
        if r.Method == "OPTIONS" {
            w.WriteHeader(http.StatusOK)
            return
        }
        
        h.ServeHTTP(w, r)
    })
}

// 日志中间件
func loggingMiddleware(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Printf("Request: %s %s", r.Method, r.URL.Path)
        h.ServeHTTP(w, r)
    })
}

// 使用中间件
func main() {
    mux := runtime.NewServeMux()
    
    // 使用中间件包装
    handler := corsMiddleware(loggingMiddleware(mux))
    
    http.ListenAndServe(":8080", handler)
}

测试 API

# GET 请求
curl http://localhost:8080/v1/hello/World

# POST 请求
curl -X POST http://localhost:8080/v1/users \
  -H "Content-Type: application/json" \
  -d '{"name": "Alice", "email": "alice@example.com"}'

# 查看 OpenAPI 文档
curl http://localhost:8080/swagger.json

Redis 客户端

Redis 是一个高性能的键值存储数据库,Go 中常用 go-redis 库进行操作。

快速开始

go get github.com/redis/go-redis/v9

基本操作

package main

import (
    "context"
    "fmt"
    "time"
    "github.com/redis/go-redis/v9"
)

func main() {
    // 创建客户端
    rdb := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "",
        DB:       0,
    })
    
    ctx := context.Background()
    
    // 设置键值
    err := rdb.Set(ctx, "key", "value", 0).Err()
    if err != nil {
        panic(err)
    }
    
    // 获取键值
    val, err := rdb.Get(ctx, "key").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("key:", val)
    
    // 设置过期时间
    rdb.Set(ctx, "key", "value", 10*time.Minute)
    
    // 删除键
    rdb.Del(ctx, "key")
}

数据结构操作

// 1. Hash 操作
rdb.HSet(ctx, "user:1", "name", "张三")
rdb.HSet(ctx, "user:1", "email", "zhangsan@example.com")
name, _ := rdb.HGet(ctx, "user:1", "name").Result()

// 2. List 操作
rdb.LPush(ctx, "list", "a", "b", "c")
val, _ := rdb.RPop(ctx, "list").Result()

// 3. Set 操作
rdb.SAdd(ctx, "set", "a", "b", "c")
members, _ := rdb.SMembers(ctx, "set").Result()

// 4. Sorted Set 操作
rdb.ZAdd(ctx, "zset", redis.Z{Score: 1, Member: "a"})
rdb.ZAdd(ctx, "zset", redis.Z{Score: 2, Member: "b"})
result, _ := rdb.ZRangeWithScores(ctx, "zset", 0, -1).Result()

发布订阅

// 发布消息
err := rdb.Publish(ctx, "channel", "message").Err()

// 订阅消息
pubsub := rdb.Subscribe(ctx, "channel")
defer pubsub.Close()

for {
    msg, err := pubsub.ReceiveMessage(ctx)
    if err != nil {
        panic(err)
    }
    fmt.Println("收到消息:", msg.Payload)
}

事务

// 使用事务
func transfer(from, to string, amount int64) error {
    tx := rdb.TxPipeline()
    
    err := tx.DecrBy(ctx, from, amount).Err()
    if err != nil {
        return err
    }
    
    tx.IncrBy(ctx, to, amount)
    
    _, err = tx.Exec(ctx)
    return err
}

Redis 最佳实践

1. 连接池配置

rdb := redis.NewClient(&redis.Options{
    Addr:         "localhost:6379",
    Password:     "",
    DB:           0,
    PoolSize:     10,   // 连接池大小
    MinIdleConns: 5,    // 最小空闲连接
    MaxRetries:   3,    // 最大重试次数
})

2. 使用 Pipeline

// 批量操作使用 Pipeline
pipe := rdb.Pipeline()
pipe.Set(ctx, "key1", "value1", 0)
pipe.Set(ctx, "key2", "value2", 0)
pipe.Set(ctx, "key3", "value3", 0)
cmds, err := pipe.Exec(ctx)

3. 键命名规范

// 使用冒号分隔的层次结构
"user:1:name"
"user:1:email"
"session:abc123"
"cache:product:123"

4. 缓存策略

// Cache-Aside 模式
func getUser(id int64) (*User, error) {
    // 1. 先查缓存
    key := fmt.Sprintf("user:%d", id)
    val, err := rdb.Get(ctx, key).Result()
    if err == nil {
        return unmarshalUser(val)
    }
    
    // 2. 查数据库
    user, err := db.GetUser(id)
    if err != nil {
        return nil, err
    }
    
    // 3. 写入缓存
    data, _ := json.Marshal(user)
    rdb.Set(ctx, key, data, 10*time.Minute)
    
    return user, nil
}

MySQL 客户端

Go 中常用 GORM 或 database/sql 操作 MySQL 数据库。

快速开始

go get gorm.io/gorm
go get gorm.io/driver/mysql

连接数据库

package main

import (
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

func main() {
    dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        panic(err)
    }
}

定义模型

type User struct {
    ID        uint           `gorm:"primaryKey"`
    Name      string         `gorm:"size:255;not null"`
    Email     string         `gorm:"size:255;uniqueIndex"`
    Age       int            `gorm:"default:0"`
    CreatedAt time.Time
    UpdatedAt time.Time
    DeletedAt gorm.DeletedAt `gorm:"index"`
}

// 自动迁移
db.AutoMigrate(&User{})

CRUD 操作

// 创建
user := User{Name: "张三", Email: "zhangsan@example.com"}
result := db.Create(&user)

// 查询
var user User
db.First(&user, 1)  // 根据 ID 查询
db.Where("name = ?", "张三").First(&user)
db.Find(&users)  // 查询所有

// 更新
db.Model(&user).Update("name", "李四")
db.Model(&user).Updates(User{Name: "李四", Age: 30})

// 删除
db.Delete(&user)  // 软删除
db.Unscoped().Delete(&user)  // 永久删除

复杂查询

// 条件查询
db.Where("age > ?", 18).Find(&users)
db.Where("name LIKE ?", "%张%").Find(&users)

// Or 查询
db.Where("name = ?", "张三").Or("name = ?", "李四").Find(&users)

// 排序和分页
db.Order("age desc").Offset(0).Limit(10).Find(&users)

// 统计
var count int64
db.Model(&User{}).Count(&count)

// 原生 SQL
db.Raw("SELECT * FROM users WHERE age > ?", 18).Scan(&users)

事务

// 自动事务
err := db.Transaction(func(tx *gorm.DB) error {
    if err := tx.Create(&user1).Error; err != nil {
        return err
    }
    if err := tx.Create(&user2).Error; err != nil {
        return err
    }
    return nil
})

// 手动事务
tx := db.Begin()
defer func() {
    if r := recover(); r != nil {
        tx.Rollback()
    }
}()

if err := tx.Create(&user).Error; err != nil {
    tx.Rollback()
    return err
}

tx.Commit()

MySQL 最佳实践

1. 连接池配置

sqlDB, _ := db.DB()
sqlDB.SetMaxIdleConns(10)
sqlDB.SetMaxOpenConns(100)
sqlDB.SetConnMaxLifetime(time.Hour)

2. 使用索引

type User struct {
    Email string `gorm:"uniqueIndex"`
    Age   int    `gorm:"index"`
}

3. 批量操作

// 使用 CreateInBatches 批量插入
users := []User{{Name: "a"}, {Name: "b"}, {Name: "c"}}
db.CreateInBatches(users, 100)

4. 预加载避免 N+1

// 使用 Preload 预加载关联
db.Preload("Orders").Find(&users)

5. 使用软删除

type User struct {
    DeletedAt gorm.DeletedAt `gorm:"index"`
}

集成最佳实践

完整的数据访问层

type UserRepository interface {
    Create(user *User) error
    GetByID(id uint) (*User, error)
    GetByEmail(email string) (*User, error)
    Update(user *User) error
    Delete(id uint) error
}

type userRepository struct {
    db *gorm.DB
}

func NewUserRepository(db *gorm.DB) UserRepository {
    return &userRepository{db: db}
}

func (r *userRepository) GetByID(id uint) (*User, error) {
    var user User
    err := r.db.First(&user, id).Error
    if err != nil {
        return nil, err
    }
    return &user, nil
}

总结

本章介绍了 Go 语言生态中更多重要的社区库:

  • Validator:强大的参数校验库,支持多种校验规则和自定义规则
  • Cobra:功能丰富的命令行框架,适合构建 CLI 应用
  • Go-Micro:微服务框架,提供服务发现、RPC 通信等功能
  • Etcd:分布式键值存储,用于配置管理和服务发现
  • YAML:配置文件处理库,支持 YAML 格式的读取、写入和序列化
  • Sonyflake:分布式唯一 ID 生成器,基于 Snowflake 算法优化
  • jinzhu/now:轻量级时间工具库,提供丰富的时间处理功能

这些库在构建企业级应用时非常有用,掌握它们可以帮助您构建高性能、可扩展的微服务架构。