Go语言微服务架构实战:从零构建云原生电商系统

在现代软件开发中,微服务架构已成为构建大型、复杂应用系统的首选方案。相比传统的单体架构,微服务具有以下显著优势:

  • 技术多样性 - 不同服务可以使用最适合的技术栈
  • 独立部署 - 服务可以独立开发、测试和部署
  • 弹性扩展 - 根据负载需求灵活扩缩容
  • 故障隔离 - 单个服务故障不会影响整个系统
  • 团队自治 - 不同团队可以独立维护各自的服务

Go语言凭借其出色的并发性能、简洁的语法和丰富的生态系统,成为微服务开发的理想选择。

🏗️ 系统架构设计

整体架构图

scss 复制代码
┌─────────────────────────────────────────────────────────────┐
│                        API Gateway                          │
│                     (Gin + 负载均衡)                         │
└─────────────────────┬───────────────────────────────────────┘
                      │
        ┌─────────────┴─────────────┐
        │                           │
┌───────▼────────┐         ┌────────▼────────┐
│   User Service │         │  Order Service  │
│   (用户服务)    │         │   (订单服务)     │
│                │         │                 │
│ ┌─────────────┐│         │ ┌─────────────┐ │
│ │    TORM     ││         │ │    TORM     │ │
│ │   MySQL     ││         │ │   MySQL     │ │
│ └─────────────┘│         │ └─────────────┘ │
└────────────────┘         └─────────────────┘
        │                           │
        └─────────────┬─────────────┘
                      │
        ┌─────────────▼─────────────┐
        │      Payment Service      │
        │       (支付服务)           │
        │                           │
        │ ┌─────────────────────────┐│
        │ │      TORM + Redis       ││
        │ │       PostgreSQL        ││
        │ └─────────────────────────┘│
        └───────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│                   基础设施层                                  │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐       │
│  │  Consul  │ │  Redis   │ │ Prometh- │ │  Jaeger  │       │
│  │服务发现   │ │   缓存    │ │   eus   │ │  链路追踪 │       │
│  └──────────┘ └──────────┘ └──────────┘ └──────────┘       │
└─────────────────────────────────────────────────────────────┘

技术栈选择

  • Web框架: Gin (HTTP API)
  • RPC框架: gRPC (服务间通信)
  • ORM框架: TORM (数据库操作)
  • 数据库: MySQL, PostgreSQL
  • 缓存: Redis
  • 服务发现: Consul
  • 配置管理: Viper
  • 监控: Prometheus + Grafana
  • 链路追踪: Jaeger
  • 容器化: Docker + Kubernetes

🚀 项目初始化

项目结构

bash 复制代码
ecommerce-microservices/
├── api-gateway/                 # API网关
├── services/
│   ├── user-service/           # 用户服务
│   ├── order-service/          # 订单服务
│   └── payment-service/        # 支付服务
├── shared/                     # 共享库
│   ├── config/                 # 配置管理
│   ├── middleware/             # 中间件
│   ├── utils/                  # 工具函数
│   └── proto/                  # Protobuf定义
├── docker/                     # Docker配置
├── k8s/                        # Kubernetes配置
├── monitoring/                 # 监控配置
└── docs/                       # 文档

共享配置和工具

首先创建共享的配置管理模块:

go 复制代码
// shared/config/config.go
package config

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

type DatabaseConfig struct {
    Driver   string `mapstructure:"driver"`
    Host     string `mapstructure:"host"`
    Port     int    `mapstructure:"port"`
    Username string `mapstructure:"username"`
    Password string `mapstructure:"password"`
    Database string `mapstructure:"database"`
}

type RedisConfig struct {
    Host     string `mapstructure:"host"`
    Port     int    `mapstructure:"port"`
    Password string `mapstructure:"password"`
    DB       int    `mapstructure:"db"`
}

type ConsulConfig struct {
    Host string `mapstructure:"host"`
    Port int    `mapstructure:"port"`
}

type ServiceConfig struct {
    Name     string          `mapstructure:"name"`
    Port     int             `mapstructure:"port"`
    Database DatabaseConfig  `mapstructure:"database"`
    Redis    RedisConfig     `mapstructure:"redis"`
    Consul   ConsulConfig    `mapstructure:"consul"`
}

func LoadConfig(path string) (*ServiceConfig, error) {
    viper.AddConfigPath(path)
    viper.SetConfigName("config")
    viper.SetConfigType("yaml")
    
    viper.AutomaticEnv()
    
    if err := viper.ReadInConfig(); err != nil {
        return nil, fmt.Errorf("读取配置文件失败: %w", err)
    }
    
    var config ServiceConfig
    if err := viper.Unmarshal(&config); err != nil {
        return nil, fmt.Errorf("解析配置文件失败: %w", err)
    }
    
    return &config, nil
}

数据库连接管理

go 复制代码
// shared/database/database.go
package database

import (
    "fmt"
    "github.com/zhoudm1743/torm/db"
    "ecommerce/shared/config"
)

func InitDatabase(cfg config.DatabaseConfig, connectionName string) error {
    conf := &db.Config{
        Driver:   cfg.Driver,
        Host:     cfg.Host,
        Port:     cfg.Port,
        Username: cfg.Username,
        Password: cfg.Password,
        Database: cfg.Database,
    }
    
    return db.AddConnection(connectionName, conf)
}

func GetQuery(connectionName string) (*db.QueryBuilder, error) {
    return db.Query(connectionName)
}

👤 用户服务实现

用户模型定义

go 复制代码
// services/user-service/internal/models/user.go
package models

import (
    "time"
    "golang.org/x/crypto/bcrypt"
)

type User struct {
    ID        int       `json:"id" torm:"id,primary_key,auto_increment"`
    Username  string    `json:"username" torm:"username,unique,not_null"`
    Email     string    `json:"email" torm:"email,unique,not_null"`
    Password  string    `json:"-" torm:"password,not_null"`
    Phone     string    `json:"phone" torm:"phone"`
    Avatar    string    `json:"avatar" torm:"avatar"`
    Status    int       `json:"status" torm:"status,default:1"`
    CreatedAt time.Time `json:"created_at" torm:"created_at"`
    UpdatedAt time.Time `json:"updated_at" torm:"updated_at"`
}

func (u *User) HashPassword() error {
    hashedBytes, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost)
    if err != nil {
        return err
    }
    u.Password = string(hashedBytes)
    return nil
}

func (u *User) CheckPassword(password string) bool {
    err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
    return err == nil
}

type UserProfile struct {
    UserID    int    `json:"user_id" torm:"user_id,primary_key"`
    RealName  string `json:"real_name" torm:"real_name"`
    Gender    int    `json:"gender" torm:"gender"`
    Birthday  string `json:"birthday" torm:"birthday"`
    Address   string `json:"address" torm:"address"`
    Bio       string `json:"bio" torm:"bio"`
}

用户仓储层

TORM使用说明:

TORM提供了两种操作方式:

  1. 直接执行 - 使用.Exec()直接执行数据库操作(推荐)
  2. 生成SQL - 使用.ToSQL()生成SQL语句后手动执行(调试和学习时使用)
go 复制代码
// 直接执行(推荐的生产环境用法)
_, err := query.Table("users").Where("id", "=", id).Delete().Exec()

// 生成SQL(用于调试、日志记录或自定义执行)
sql, args, err := query.Table("users").Where("id", "=", id).Delete().ToSQL()
if err == nil {
    fmt.Printf("Generated SQL: %s, Args: %v\n", sql, args) // 用于调试
    _, err = query.Exec(sql, args...)
}
go 复制代码
// services/user-service/internal/repository/user_repository.go
package repository

import (
    "fmt"
    "ecommerce/services/user-service/internal/models"
    "ecommerce/shared/database"
)

type UserRepository interface {
    Create(user *models.User) error
    GetByID(id int) (*models.User, error)
    GetByEmail(email string) (*models.User, error)
    GetByUsername(username string) (*models.User, error)
    Update(user *models.User) error
    Delete(id int) error
    List(offset, limit int) ([]models.User, error)
}

type userRepository struct {
    connectionName string
}

func NewUserRepository(connectionName string) UserRepository {
    return &userRepository{
        connectionName: connectionName,
    }
}

func (r *userRepository) Create(user *models.User) error {
    query, err := database.GetQuery(r.connectionName)
    if err != nil {
        return fmt.Errorf("获取数据库连接失败: %w", err)
    }
    
    // 直接使用TORM执行插入操作
    result, err := query.Table("users").Insert(map[string]interface{}{
        "username":   user.Username,
        "email":      user.Email,
        "password":   user.Password,
        "phone":      user.Phone,
        "avatar":     user.Avatar,
        "status":     user.Status,
        "created_at": user.CreatedAt,
        "updated_at": user.UpdatedAt,
    }).Exec()
    
    if err != nil {
        return fmt.Errorf("执行插入失败: %w", err)
    }
    
    id, err := result.LastInsertId()
    if err != nil {
        return fmt.Errorf("获取插入ID失败: %w", err)
    }
    
    user.ID = int(id)
    return nil
}

func (r *userRepository) GetByID(id int) (*models.User, error) {
    query, err := database.GetQuery(r.connectionName)
    if err != nil {
        return nil, fmt.Errorf("获取数据库连接失败: %w", err)
    }
    
    rows, err := query.From("users").Where("id", "=", id).First()
    if err != nil {
        return nil, fmt.Errorf("查询用户失败: %w", err)
    }
    
    if len(rows) == 0 {
        return nil, fmt.Errorf("用户不存在")
    }
    
    row := rows[0]
    user := &models.User{
        ID:        int(row["id"].(int64)),
        Username:  row["username"].(string),
        Email:     row["email"].(string),
        Password:  row["password"].(string),
        Phone:     getStringFromInterface(row["phone"]),
        Avatar:    getStringFromInterface(row["avatar"]),
        Status:    int(row["status"].(int64)),
        CreatedAt: row["created_at"].(time.Time),
        UpdatedAt: row["updated_at"].(time.Time),
    }
    
    return user, nil
}

func (r *userRepository) GetByEmail(email string) (*models.User, error) {
    query, err := database.GetQuery(r.connectionName)
    if err != nil {
        return nil, fmt.Errorf("获取数据库连接失败: %w", err)
    }
    
    rows, err := query.From("users").Where("email", "=", email).Find()
    if err != nil {
        return nil, fmt.Errorf("查询用户失败: %w", err)
    }
    
    if len(rows) == 0 {
        return nil, fmt.Errorf("用户不存在")
    }
    
    return r.mapRowToUser(rows[0])
}

func (r *userRepository) GetByUsername(username string) (*models.User, error) {
    query, err := database.GetQuery(r.connectionName)
    if err != nil {
        return nil, fmt.Errorf("获取数据库连接失败: %w", err)
    }
    
    rows, err := query.From("users").Where("username", "=", username).Find()
    if err != nil {
        return nil, fmt.Errorf("查询用户失败: %w", err)
    }
    
    if len(rows) == 0 {
        return nil, fmt.Errorf("用户不存在")
    }
    
    return r.mapRowToUser(rows[0])
}

func (r *userRepository) Update(user *models.User) error {
    query, err := database.GetQuery(r.connectionName)
    if err != nil {
        return fmt.Errorf("获取数据库连接失败: %w", err)
    }
    
    // 直接执行更新操作
    _, err = query.Table("users").Where("id", "=", user.ID).Update(map[string]interface{}{
        "username":   user.Username,
        "email":      user.Email,
        "phone":      user.Phone,
        "avatar":     user.Avatar,
        "status":     user.Status,
        "updated_at": user.UpdatedAt,
    }).Exec()
    
    if err != nil {
        return fmt.Errorf("执行更新失败: %w", err)
    }
    
    return nil
}

func (r *userRepository) Delete(id int) error {
    query, err := database.GetQuery(r.connectionName)
    if err != nil {
        return fmt.Errorf("获取数据库连接失败: %w", err)
    }
    
    // 直接执行删除操作
    _, err = query.Table("users").Where("id", "=", id).Delete().Exec()
    if err != nil {
        return fmt.Errorf("执行删除失败: %w", err)
    }
    
    return nil
}

func (r *userRepository) List(offset, limit int) ([]models.User, error) {
    query, err := database.GetQuery(r.connectionName)
    if err != nil {
        return nil, fmt.Errorf("获取数据库连接失败: %w", err)
    }
    
    rows, err := query.From("users").
        OrderBy("id", "DESC").
        Offset(offset).
        Limit(limit).
        Get()
    
    if err != nil {
        return nil, fmt.Errorf("查询用户列表失败: %w", err)
    }
    
    var users []models.User
    for _, row := range rows {
        user, err := r.mapRowToUser(row)
        if err != nil {
            continue // 跳过错误数据
        }
        users = append(users, *user)
    }
    
    return users, nil
}

// 辅助方法
func (r *userRepository) mapRowToUser(row map[string]interface{}) (*models.User, error) {
    user := &models.User{
        ID:        int(row["id"].(int64)),
        Username:  row["username"].(string),
        Email:     row["email"].(string),
        Password:  row["password"].(string),
        Phone:     getStringFromInterface(row["phone"]),
        Avatar:    getStringFromInterface(row["avatar"]),
        Status:    int(row["status"].(int64)),
        CreatedAt: row["created_at"].(time.Time),
        UpdatedAt: row["updated_at"].(time.Time),
    }
    return user, nil
}

func getStringFromInterface(v interface{}) string {
    if v == nil {
        return ""
    }
    if str, ok := v.(string); ok {
        return str
    }
    return ""
}

用户服务层

go 复制代码
// services/user-service/internal/service/user_service.go
package service

import (
    "fmt"
    "time"
    "ecommerce/services/user-service/internal/models"
    "ecommerce/services/user-service/internal/repository"
)

type UserService interface {
    Register(username, email, password string) (*models.User, error)
    Login(email, password string) (*models.User, error)
    GetProfile(userID int) (*models.User, error)
    UpdateProfile(user *models.User) error
    DeleteUser(userID int) error
    ListUsers(page, pageSize int) ([]models.User, error)
}

type userService struct {
    userRepo repository.UserRepository
}

func NewUserService(userRepo repository.UserRepository) UserService {
    return &userService{
        userRepo: userRepo,
    }
}

func (s *userService) Register(username, email, password string) (*models.User, error) {
    // 检查用户名是否已存在
    if existingUser, _ := s.userRepo.GetByUsername(username); existingUser != nil {
        return nil, fmt.Errorf("用户名已存在")
    }
    
    // 检查邮箱是否已存在
    if existingUser, _ := s.userRepo.GetByEmail(email); existingUser != nil {
        return nil, fmt.Errorf("邮箱已被注册")
    }
    
    // 创建新用户
    user := &models.User{
        Username:  username,
        Email:     email,
        Password:  password,
        Status:    1,
        CreatedAt: time.Now(),
        UpdatedAt: time.Now(),
    }
    
    // 加密密码
    if err := user.HashPassword(); err != nil {
        return nil, fmt.Errorf("密码加密失败: %w", err)
    }
    
    // 保存到数据库
    if err := s.userRepo.Create(user); err != nil {
        return nil, fmt.Errorf("创建用户失败: %w", err)
    }
    
    return user, nil
}

func (s *userService) Login(email, password string) (*models.User, error) {
    user, err := s.userRepo.GetByEmail(email)
    if err != nil {
        return nil, fmt.Errorf("用户不存在")
    }
    
    if !user.CheckPassword(password) {
        return nil, fmt.Errorf("密码错误")
    }
    
    if user.Status != 1 {
        return nil, fmt.Errorf("用户账户已被禁用")
    }
    
    return user, nil
}

func (s *userService) GetProfile(userID int) (*models.User, error) {
    return s.userRepo.GetByID(userID)
}

func (s *userService) UpdateProfile(user *models.User) error {
    user.UpdatedAt = time.Now()
    return s.userRepo.Update(user)
}

func (s *userService) DeleteUser(userID int) error {
    return s.userRepo.Delete(userID)
}

func (s *userService) ListUsers(page, pageSize int) ([]models.User, error) {
    offset := (page - 1) * pageSize
    return s.userRepo.List(offset, pageSize)
}

用户控制器

go 复制代码
// services/user-service/internal/handler/user_handler.go
package handler

import (
    "net/http"
    "strconv"
    "github.com/gin-gonic/gin"
    "ecommerce/services/user-service/internal/service"
    "ecommerce/services/user-service/internal/models"
)

type UserHandler struct {
    userService service.UserService
}

func NewUserHandler(userService service.UserService) *UserHandler {
    return &UserHandler{
        userService: userService,
    }
}

type RegisterRequest struct {
    Username string `json:"username" binding:"required,min=3,max=20"`
    Email    string `json:"email" binding:"required,email"`
    Password string `json:"password" binding:"required,min=6"`
}

type LoginRequest struct {
    Email    string `json:"email" binding:"required,email"`
    Password string `json:"password" binding:"required"`
}

type Response struct {
    Code    int         `json:"code"`
    Message string      `json:"message"`
    Data    interface{} `json:"data,omitempty"`
}

func (h *UserHandler) Register(c *gin.Context) {
    var req RegisterRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(http.StatusBadRequest, Response{
            Code:    400,
            Message: "请求参数错误: " + err.Error(),
        })
        return
    }
    
    user, err := h.userService.Register(req.Username, req.Email, req.Password)
    if err != nil {
        c.JSON(http.StatusBadRequest, Response{
            Code:    400,
            Message: err.Error(),
        })
        return
    }
    
    // 清除密码字段
    user.Password = ""
    
    c.JSON(http.StatusCreated, Response{
        Code:    201,
        Message: "注册成功",
        Data:    user,
    })
}

func (h *UserHandler) Login(c *gin.Context) {
    var req LoginRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(http.StatusBadRequest, Response{
            Code:    400,
            Message: "请求参数错误: " + err.Error(),
        })
        return
    }
    
    user, err := h.userService.Login(req.Email, req.Password)
    if err != nil {
        c.JSON(http.StatusUnauthorized, Response{
            Code:    401,
            Message: err.Error(),
        })
        return
    }
    
    // 清除密码字段
    user.Password = ""
    
    // 这里应该生成JWT token
    // token := generateJWTToken(user)
    
    c.JSON(http.StatusOK, Response{
        Code:    200,
        Message: "登录成功",
        Data: gin.H{
            "user": user,
            // "token": token,
        },
    })
}

func (h *UserHandler) GetProfile(c *gin.Context) {
    userIDStr := c.Param("id")
    userID, err := strconv.Atoi(userIDStr)
    if err != nil {
        c.JSON(http.StatusBadRequest, Response{
            Code:    400,
            Message: "用户ID格式错误",
        })
        return
    }
    
    user, err := h.userService.GetProfile(userID)
    if err != nil {
        c.JSON(http.StatusNotFound, Response{
            Code:    404,
            Message: err.Error(),
        })
        return
    }
    
    // 清除密码字段
    user.Password = ""
    
    c.JSON(http.StatusOK, Response{
        Code:    200,
        Message: "获取成功",
        Data:    user,
    })
}

func (h *UserHandler) UpdateProfile(c *gin.Context) {
    userIDStr := c.Param("id")
    userID, err := strconv.Atoi(userIDStr)
    if err != nil {
        c.JSON(http.StatusBadRequest, Response{
            Code:    400,
            Message: "用户ID格式错误",
        })
        return
    }
    
    var user models.User
    if err := c.ShouldBindJSON(&user); err != nil {
        c.JSON(http.StatusBadRequest, Response{
            Code:    400,
            Message: "请求参数错误: " + err.Error(),
        })
        return
    }
    
    user.ID = userID
    if err := h.userService.UpdateProfile(&user); err != nil {
        c.JSON(http.StatusBadRequest, Response{
            Code:    400,
            Message: err.Error(),
        })
        return
    }
    
    c.JSON(http.StatusOK, Response{
        Code:    200,
        Message: "更新成功",
    })
}

func (h *UserHandler) ListUsers(c *gin.Context) {
    pageStr := c.DefaultQuery("page", "1")
    pageSizeStr := c.DefaultQuery("page_size", "10")
    
    page, err := strconv.Atoi(pageStr)
    if err != nil || page < 1 {
        page = 1
    }
    
    pageSize, err := strconv.Atoi(pageSizeStr)
    if err != nil || pageSize < 1 || pageSize > 100 {
        pageSize = 10
    }
    
    users, err := h.userService.ListUsers(page, pageSize)
    if err != nil {
        c.JSON(http.StatusInternalServerError, Response{
            Code:    500,
            Message: err.Error(),
        })
        return
    }
    
    // 清除所有用户的密码字段
    for i := range users {
        users[i].Password = ""
    }
    
    c.JSON(http.StatusOK, Response{
        Code:    200,
        Message: "获取成功",
        Data: gin.H{
            "users":     users,
            "page":      page,
            "page_size": pageSize,
            "total":     len(users),
        },
    })
}

func (h *UserHandler) DeleteUser(c *gin.Context) {
    userIDStr := c.Param("id")
    userID, err := strconv.Atoi(userIDStr)
    if err != nil {
        c.JSON(http.StatusBadRequest, Response{
            Code:    400,
            Message: "用户ID格式错误",
        })
        return
    }
    
    if err := h.userService.DeleteUser(userID); err != nil {
        c.JSON(http.StatusBadRequest, Response{
            Code:    400,
            Message: err.Error(),
        })
        return
    }
    
    c.JSON(http.StatusOK, Response{
        Code:    200,
        Message: "删除成功",
    })
}

用户服务主程序

go 复制代码
// services/user-service/cmd/main.go
package main

import (
    "fmt"
    "log"
    
    "github.com/gin-gonic/gin"
    "ecommerce/services/user-service/internal/handler"
    "ecommerce/services/user-service/internal/repository"
    "ecommerce/services/user-service/internal/service"
    "ecommerce/shared/config"
    "ecommerce/shared/database"
)

func main() {
    // 加载配置
    cfg, err := config.LoadConfig("./configs")
    if err != nil {
        log.Fatalf("加载配置失败: %v", err)
    }
    
    // 初始化数据库
    if err := database.InitDatabase(cfg.Database, "user_service"); err != nil {
        log.Fatalf("初始化数据库失败: %v", err)
    }
    
    // 初始化仓储层
    userRepo := repository.NewUserRepository("user_service")
    
    // 初始化服务层
    userSvc := service.NewUserService(userRepo)
    
    // 初始化处理器
    userHandler := handler.NewUserHandler(userSvc)
    
    // 设置路由
    router := setupRoutes(userHandler)
    
    // 启动服务
    port := fmt.Sprintf(":%d", cfg.Port)
    log.Printf("用户服务启动在端口 %s", port)
    if err := router.Run(port); err != nil {
        log.Fatalf("启动服务失败: %v", err)
    }
}

func setupRoutes(userHandler *handler.UserHandler) *gin.Engine {
    router := gin.Default()
    
    // 健康检查
    router.GET("/health", func(c *gin.Context) {
        c.JSON(200, gin.H{"status": "ok"})
    })
    
    // API路由组
    api := router.Group("/api/v1")
    {
        users := api.Group("/users")
        {
            users.POST("/register", userHandler.Register)
            users.POST("/login", userHandler.Login)
            users.GET("/:id", userHandler.GetProfile)
            users.PUT("/:id", userHandler.UpdateProfile)
            users.DELETE("/:id", userHandler.DeleteUser)
            users.GET("", userHandler.ListUsers)
        }
    }
    
    return router
}

🛒 订单服务实现

订单模型

go 复制代码
// services/order-service/internal/models/order.go
package models

import (
    "time"
)

type Order struct {
    ID         int           `json:"id" torm:"id,primary_key,auto_increment"`
    OrderNo    string        `json:"order_no" torm:"order_no,unique,not_null"`
    UserID     int           `json:"user_id" torm:"user_id,not_null"`
    TotalPrice float64       `json:"total_price" torm:"total_price,not_null"`
    Status     OrderStatus   `json:"status" torm:"status,not_null"`
    Items      []OrderItem   `json:"items" torm:"-"`
    CreatedAt  time.Time     `json:"created_at" torm:"created_at"`
    UpdatedAt  time.Time     `json:"updated_at" torm:"updated_at"`
}

type OrderItem struct {
    ID        int     `json:"id" torm:"id,primary_key,auto_increment"`
    OrderID   int     `json:"order_id" torm:"order_id,not_null"`
    ProductID int     `json:"product_id" torm:"product_id,not_null"`
    Quantity  int     `json:"quantity" torm:"quantity,not_null"`
    Price     float64 `json:"price" torm:"price,not_null"`
    Total     float64 `json:"total" torm:"total,not_null"`
}

type OrderStatus int

const (
    OrderStatusPending OrderStatus = iota + 1
    OrderStatusPaid
    OrderStatusShipped
    OrderStatusDelivered
    OrderStatusCancelled
)

func (s OrderStatus) String() string {
    switch s {
    case OrderStatusPending:
        return "pending"
    case OrderStatusPaid:
        return "paid"
    case OrderStatusShipped:
        return "shipped"
    case OrderStatusDelivered:
        return "delivered"
    case OrderStatusCancelled:
        return "cancelled"
    default:
        return "unknown"
    }
}

func GenerateOrderNo() string {
    return fmt.Sprintf("ORD%d", time.Now().UnixNano())
}

订单服务

go 复制代码
// services/order-service/internal/service/order_service.go
package service

import (
    "context"
    "fmt"
    "time"
    "ecommerce/services/order-service/internal/models"
    "ecommerce/services/order-service/internal/repository"
)

type CreateOrderRequest struct {
    UserID int                    `json:"user_id"`
    Items  []CreateOrderItemRequest `json:"items"`
}

type CreateOrderItemRequest struct {
    ProductID int     `json:"product_id"`
    Quantity  int     `json:"quantity"`
    Price     float64 `json:"price"`
}

type OrderService interface {
    CreateOrder(ctx context.Context, req *CreateOrderRequest) (*models.Order, error)
    GetOrder(ctx context.Context, orderID int) (*models.Order, error)
    GetUserOrders(ctx context.Context, userID int, page, pageSize int) ([]models.Order, error)
    UpdateOrderStatus(ctx context.Context, orderID int, status models.OrderStatus) error
    CancelOrder(ctx context.Context, orderID int) error
}

type orderService struct {
    orderRepo repository.OrderRepository
    itemRepo  repository.OrderItemRepository
}

func NewOrderService(orderRepo repository.OrderRepository, itemRepo repository.OrderItemRepository) OrderService {
    return &orderService{
        orderRepo: orderRepo,
        itemRepo:  itemRepo,
    }
}

func (s *orderService) CreateOrder(ctx context.Context, req *CreateOrderRequest) (*models.Order, error) {
    // 计算订单总价
    var totalPrice float64
    var orderItems []models.OrderItem
    
    for _, item := range req.Items {
        total := item.Price * float64(item.Quantity)
        orderItems = append(orderItems, models.OrderItem{
            ProductID: item.ProductID,
            Quantity:  item.Quantity,
            Price:     item.Price,
            Total:     total,
        })
        totalPrice += total
    }
    
    // 创建订单
    order := &models.Order{
        OrderNo:    models.GenerateOrderNo(),
        UserID:     req.UserID,
        TotalPrice: totalPrice,
        Status:     models.OrderStatusPending,
        CreatedAt:  time.Now(),
        UpdatedAt:  time.Now(),
    }
    
    // 保存订单
    if err := s.orderRepo.Create(order); err != nil {
        return nil, fmt.Errorf("创建订单失败: %w", err)
    }
    
    // 保存订单项
    for i := range orderItems {
        orderItems[i].OrderID = order.ID
        if err := s.itemRepo.Create(&orderItems[i]); err != nil {
            return nil, fmt.Errorf("创建订单项失败: %w", err)
        }
    }
    
    order.Items = orderItems
    return order, nil
}

func (s *orderService) GetOrder(ctx context.Context, orderID int) (*models.Order, error) {
    order, err := s.orderRepo.GetByID(orderID)
    if err != nil {
        return nil, err
    }
    
    items, err := s.itemRepo.GetByOrderID(orderID)
    if err != nil {
        return nil, err
    }
    
    order.Items = items
    return order, nil
}

func (s *orderService) GetUserOrders(ctx context.Context, userID int, page, pageSize int) ([]models.Order, error) {
    offset := (page - 1) * pageSize
    orders, err := s.orderRepo.GetByUserID(userID, offset, pageSize)
    if err != nil {
        return nil, err
    }
    
    // 为每个订单加载订单项
    for i := range orders {
        items, err := s.itemRepo.GetByOrderID(orders[i].ID)
        if err != nil {
            continue // 跳过加载失败的订单项
        }
        orders[i].Items = items
    }
    
    return orders, nil
}

func (s *orderService) UpdateOrderStatus(ctx context.Context, orderID int, status models.OrderStatus) error {
    return s.orderRepo.UpdateStatus(orderID, status)
}

func (s *orderService) CancelOrder(ctx context.Context, orderID int) error {
    order, err := s.orderRepo.GetByID(orderID)
    if err != nil {
        return err
    }
    
    if order.Status != models.OrderStatusPending {
        return fmt.Errorf("只能取消待支付的订单")
    }
    
    return s.orderRepo.UpdateStatus(orderID, models.OrderStatusCancelled)
}

💳 支付服务实现

支付模型

go 复制代码
// services/payment-service/internal/models/payment.go
package models

import (
    "time"
)

type Payment struct {
    ID            int           `json:"id" torm:"id,primary_key,auto_increment"`
    PaymentNo     string        `json:"payment_no" torm:"payment_no,unique,not_null"`
    OrderID       int           `json:"order_id" torm:"order_id,not_null"`
    UserID        int           `json:"user_id" torm:"user_id,not_null"`
    Amount        float64       `json:"amount" torm:"amount,not_null"`
    PaymentMethod PaymentMethod `json:"payment_method" torm:"payment_method,not_null"`
    Status        PaymentStatus `json:"status" torm:"status,not_null"`
    ThirdPartyID  string        `json:"third_party_id" torm:"third_party_id"`
    CreatedAt     time.Time     `json:"created_at" torm:"created_at"`
    UpdatedAt     time.Time     `json:"updated_at" torm:"updated_at"`
}

type PaymentMethod int

const (
    PaymentMethodAlipay PaymentMethod = iota + 1
    PaymentMethodWechat
    PaymentMethodBank
    PaymentMethodCredit
)

type PaymentStatus int

const (
    PaymentStatusPending PaymentStatus = iota + 1
    PaymentStatusProcessing
    PaymentStatusSuccess
    PaymentStatusFailed
    PaymentStatusRefunded
)

func (s PaymentStatus) String() string {
    switch s {
    case PaymentStatusPending:
        return "pending"
    case PaymentStatusProcessing:
        return "processing"
    case PaymentStatusSuccess:
        return "success"
    case PaymentStatusFailed:
        return "failed"
    case PaymentStatusRefunded:
        return "refunded"
    default:
        return "unknown"
    }
}

func GeneratePaymentNo() string {
    return fmt.Sprintf("PAY%d", time.Now().UnixNano())
}

支付服务

go 复制代码
// services/payment-service/internal/service/payment_service.go
package service

import (
    "context"
    "fmt"
    "time"
    "ecommerce/services/payment-service/internal/models"
    "ecommerce/services/payment-service/internal/repository"
)

type CreatePaymentRequest struct {
    OrderID       int                    `json:"order_id"`
    UserID        int                    `json:"user_id"`
    Amount        float64                `json:"amount"`
    PaymentMethod models.PaymentMethod   `json:"payment_method"`
}

type PaymentService interface {
    CreatePayment(ctx context.Context, req *CreatePaymentRequest) (*models.Payment, error)
    ProcessPayment(ctx context.Context, paymentID int) error
    GetPayment(ctx context.Context, paymentID int) (*models.Payment, error)
    GetPaymentByOrderID(ctx context.Context, orderID int) (*models.Payment, error)
    RefundPayment(ctx context.Context, paymentID int) error
}

type paymentService struct {
    paymentRepo repository.PaymentRepository
}

func NewPaymentService(paymentRepo repository.PaymentRepository) PaymentService {
    return &paymentService{
        paymentRepo: paymentRepo,
    }
}

func (s *paymentService) CreatePayment(ctx context.Context, req *CreatePaymentRequest) (*models.Payment, error) {
    payment := &models.Payment{
        PaymentNo:     models.GeneratePaymentNo(),
        OrderID:       req.OrderID,
        UserID:        req.UserID,
        Amount:        req.Amount,
        PaymentMethod: req.PaymentMethod,
        Status:        models.PaymentStatusPending,
        CreatedAt:     time.Now(),
        UpdatedAt:     time.Now(),
    }
    
    if err := s.paymentRepo.Create(payment); err != nil {
        return nil, fmt.Errorf("创建支付记录失败: %w", err)
    }
    
    return payment, nil
}

func (s *paymentService) ProcessPayment(ctx context.Context, paymentID int) error {
    payment, err := s.paymentRepo.GetByID(paymentID)
    if err != nil {
        return err
    }
    
    if payment.Status != models.PaymentStatusPending {
        return fmt.Errorf("支付状态不正确")
    }
    
    // 更新状态为处理中
    if err := s.paymentRepo.UpdateStatus(paymentID, models.PaymentStatusProcessing); err != nil {
        return err
    }
    
    // 模拟第三方支付处理
    success := s.simulateThirdPartyPayment(payment)
    
    if success {
        payment.ThirdPartyID = fmt.Sprintf("THIRD_%d", time.Now().Unix())
        if err := s.paymentRepo.UpdateThirdPartyID(paymentID, payment.ThirdPartyID); err != nil {
            return err
        }
        return s.paymentRepo.UpdateStatus(paymentID, models.PaymentStatusSuccess)
    } else {
        return s.paymentRepo.UpdateStatus(paymentID, models.PaymentStatusFailed)
    }
}

func (s *paymentService) GetPayment(ctx context.Context, paymentID int) (*models.Payment, error) {
    return s.paymentRepo.GetByID(paymentID)
}

func (s *paymentService) GetPaymentByOrderID(ctx context.Context, orderID int) (*models.Payment, error) {
    return s.paymentRepo.GetByOrderID(orderID)
}

func (s *paymentService) RefundPayment(ctx context.Context, paymentID int) error {
    payment, err := s.paymentRepo.GetByID(paymentID)
    if err != nil {
        return err
    }
    
    if payment.Status != models.PaymentStatusSuccess {
        return fmt.Errorf("只能退款成功的支付")
    }
    
    // 模拟退款处理
    success := s.simulateRefundProcess(payment)
    
    if success {
        return s.paymentRepo.UpdateStatus(paymentID, models.PaymentStatusRefunded)
    } else {
        return fmt.Errorf("退款处理失败")
    }
}

// 模拟第三方支付处理
func (s *paymentService) simulateThirdPartyPayment(payment *models.Payment) bool {
    // 模拟90%的成功率
    return time.Now().Unix()%10 != 0
}

// 模拟退款处理
func (s *paymentService) simulateRefundProcess(payment *models.Payment) bool {
    // 模拟95%的成功率
    return time.Now().Unix()%20 != 0
}

🌐 API网关实现

网关路由配置

go 复制代码
// api-gateway/internal/router/router.go
package router

import (
    "net/http"
    "net/http/httputil"
    "net/url"
    
    "github.com/gin-gonic/gin"
    "api-gateway/internal/middleware"
)

type ServiceConfig struct {
    Name string
    URL  string
}

type Gateway struct {
    services map[string]*ServiceConfig
}

func NewGateway() *Gateway {
    return &Gateway{
        services: make(map[string]*ServiceConfig),
    }
}

func (g *Gateway) RegisterService(name, serviceURL string) {
    g.services[name] = &ServiceConfig{
        Name: name,
        URL:  serviceURL,
    }
}

func (g *Gateway) SetupRoutes() *gin.Engine {
    router := gin.Default()
    
    // 添加中间件
    router.Use(middleware.CORS())
    router.Use(middleware.Logger())
    router.Use(middleware.RateLimiter())
    
    // 健康检查
    router.GET("/health", func(c *gin.Context) {
        c.JSON(200, gin.H{"status": "ok"})
    })
    
    // API路由
    api := router.Group("/api/v1")
    
    // 用户服务路由
    userGroup := api.Group("/users")
    userGroup.Use(g.createProxy("user-service"))
    {
        userGroup.Any("/*path", func(c *gin.Context) {})
    }
    
    // 订单服务路由
    orderGroup := api.Group("/orders")
    orderGroup.Use(middleware.AuthRequired()) // 需要认证
    orderGroup.Use(g.createProxy("order-service"))
    {
        orderGroup.Any("/*path", func(c *gin.Context) {})
    }
    
    // 支付服务路由
    paymentGroup := api.Group("/payments")
    paymentGroup.Use(middleware.AuthRequired()) // 需要认证
    paymentGroup.Use(g.createProxy("payment-service"))
    {
        paymentGroup.Any("/*path", func(c *gin.Context) {})
    }
    
    return router
}

func (g *Gateway) createProxy(serviceName string) gin.HandlerFunc {
    return gin.HandlerFunc(func(c *gin.Context) {
        service, exists := g.services[serviceName]
        if !exists {
            c.JSON(http.StatusNotFound, gin.H{"error": "Service not found"})
            c.Abort()
            return
        }
        
        // 创建反向代理
        target, err := url.Parse(service.URL)
        if err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid service URL"})
            c.Abort()
            return
        }
        
        proxy := httputil.NewSingleHostReverseProxy(target)
        
        // 修改请求路径
        c.Request.URL.Host = target.Host
        c.Request.URL.Scheme = target.Scheme
        c.Request.Header.Set("X-Forwarded-Host", c.Request.Header.Get("Host"))
        c.Request.Host = target.Host
        
        proxy.ServeHTTP(c.Writer, c.Request)
    })
}

中间件实现

go 复制代码
// api-gateway/internal/middleware/middleware.go
package middleware

import (
    "net/http"
    "time"
    
    "github.com/gin-gonic/gin"
    "golang.org/x/time/rate"
)

// CORS中间件
func CORS() gin.HandlerFunc {
    return gin.HandlerFunc(func(c *gin.Context) {
        c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
        c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
        c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
        c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")
        
        if c.Request.Method == "OPTIONS" {
            c.AbortWithStatus(204)
            return
        }
        
        c.Next()
    })
}

// 日志中间件
func Logger() gin.HandlerFunc {
    return gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
        return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n",
            param.ClientIP,
            param.TimeStamp.Format(time.RFC1123),
            param.Method,
            param.Path,
            param.Request.Proto,
            param.StatusCode,
            param.Latency,
            param.Request.UserAgent(),
            param.ErrorMessage,
        )
    })
}

// 限流中间件
func RateLimiter() gin.HandlerFunc {
    limiter := rate.NewLimiter(100, 200) // 每秒100个请求,突发200个
    
    return func(c *gin.Context) {
        if !limiter.Allow() {
            c.JSON(http.StatusTooManyRequests, gin.H{
                "error": "Too many requests",
            })
            c.Abort()
            return
        }
        c.Next()
    }
}

// 认证中间件
func AuthRequired() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.GetHeader("Authorization")
        if token == "" {
            c.JSON(http.StatusUnauthorized, gin.H{
                "error": "Authorization header required",
            })
            c.Abort()
            return
        }
        
        // 这里应该验证JWT token
        // if !validateJWTToken(token) {
        //     c.JSON(http.StatusUnauthorized, gin.H{
        //         "error": "Invalid token",
        //     })
        //     c.Abort()
        //     return
        // }
        
        c.Next()
    }
}

🔧 服务发现与配置

Consul集成

go 复制代码
// shared/consul/consul.go
package consul

import (
    "fmt"
    "github.com/hashicorp/consul/api"
)

type ServiceRegistry struct {
    client *api.Client
}

func NewServiceRegistry(consulAddr string) (*ServiceRegistry, error) {
    config := api.DefaultConfig()
    config.Address = consulAddr
    
    client, err := api.NewClient(config)
    if err != nil {
        return nil, fmt.Errorf("创建Consul客户端失败: %w", err)
    }
    
    return &ServiceRegistry{client: client}, nil
}

func (sr *ServiceRegistry) RegisterService(serviceName, serviceAddr string, port int) error {
    registration := &api.AgentServiceRegistration{
        ID:      fmt.Sprintf("%s-%d", serviceName, port),
        Name:    serviceName,
        Port:    port,
        Address: serviceAddr,
        Check: &api.AgentServiceCheck{
            HTTP:                            fmt.Sprintf("http://%s:%d/health", serviceAddr, port),
            Timeout:                         "3s",
            Interval:                        "10s",
            DeregisterCriticalServiceAfter: "30s",
        },
    }
    
    return sr.client.Agent().ServiceRegister(registration)
}

func (sr *ServiceRegistry) DiscoverService(serviceName string) ([]string, error) {
    services, _, err := sr.client.Health().Service(serviceName, "", true, nil)
    if err != nil {
        return nil, fmt.Errorf("发现服务失败: %w", err)
    }
    
    var addresses []string
    for _, service := range services {
        addr := fmt.Sprintf("http://%s:%d", service.Service.Address, service.Service.Port)
        addresses = append(addresses, addr)
    }
    
    return addresses, nil
}

func (sr *ServiceRegistry) DeregisterService(serviceID string) error {
    return sr.client.Agent().ServiceDeregister(serviceID)
}

📊 监控和日志

Prometheus指标

go 复制代码
// shared/metrics/metrics.go
package metrics

import (
    "github.com/gin-gonic/gin"
    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
    "strconv"
    "time"
)

var (
    RequestsTotal = prometheus.NewCounterVec(
        prometheus.CounterOpts{
            Name: "http_requests_total",
            Help: "Total number of HTTP requests",
        },
        []string{"method", "path", "status"},
    )
    
    RequestDuration = prometheus.NewHistogramVec(
        prometheus.HistogramOpts{
            Name: "http_request_duration_seconds",
            Help: "HTTP request duration in seconds",
        },
        []string{"method", "path"},
    )
    
    DatabaseQueries = prometheus.NewCounterVec(
        prometheus.CounterOpts{
            Name: "database_queries_total",
            Help: "Total number of database queries",
        },
        []string{"operation", "table"},
    )
)

func init() {
    prometheus.MustRegister(RequestsTotal)
    prometheus.MustRegister(RequestDuration)
    prometheus.MustRegister(DatabaseQueries)
}

func PrometheusMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        
        c.Next()
        
        duration := time.Since(start).Seconds()
        status := strconv.Itoa(c.Writer.Status())
        
        RequestsTotal.WithLabelValues(c.Request.Method, c.FullPath(), status).Inc()
        RequestDuration.WithLabelValues(c.Request.Method, c.FullPath()).Observe(duration)
    }
}

func SetupMetrics(router *gin.Engine) {
    router.GET("/metrics", gin.WrapH(promhttp.Handler()))
}

结构化日志

go 复制代码
// shared/logger/logger.go
package logger

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

var Logger *zap.Logger

func InitLogger(level string) error {
    var zapLevel zapcore.Level
    switch level {
    case "debug":
        zapLevel = zapcore.DebugLevel
    case "info":
        zapLevel = zapcore.InfoLevel
    case "warn":
        zapLevel = zapcore.WarnLevel
    case "error":
        zapLevel = zapcore.ErrorLevel
    default:
        zapLevel = zapcore.InfoLevel
    }
    
    config := zap.Config{
        Level:       zap.NewAtomicLevelAt(zapLevel),
        Development: false,
        Sampling: &zap.SamplingConfig{
            Initial:    100,
            Thereafter: 100,
        },
        Encoding: "json",
        EncoderConfig: zapcore.EncoderConfig{
            TimeKey:        "timestamp",
            LevelKey:       "level",
            NameKey:        "logger",
            CallerKey:      "caller",
            MessageKey:     "message",
            StacktraceKey:  "stacktrace",
            LineEnding:     zapcore.DefaultLineEnding,
            EncodeLevel:    zapcore.LowercaseLevelEncoder,
            EncodeTime:     zapcore.ISO8601TimeEncoder,
            EncodeDuration: zapcore.SecondsDurationEncoder,
            EncodeCaller:   zapcore.ShortCallerEncoder,
        },
        OutputPaths:      []string{"stdout"},
        ErrorOutputPaths: []string{"stderr"},
    }
    
    var err error
    Logger, err = config.Build()
    if err != nil {
        return err
    }
    
    return nil
}

func Info(message string, fields ...zap.Field) {
    Logger.Info(message, fields...)
}

func Error(message string, fields ...zap.Field) {
    Logger.Error(message, fields...)
}

func Debug(message string, fields ...zap.Field) {
    Logger.Debug(message, fields...)
}

func Warn(message string, fields ...zap.Field) {
    Logger.Warn(message, fields...)
}

🐳 容器化和部署

Docker配置

dockerfile 复制代码
# services/user-service/Dockerfile
FROM golang:1.19-alpine AS builder

WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main ./cmd/main.go

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/

COPY --from=builder /app/main .
COPY --from=builder /app/configs ./configs

EXPOSE 8080

CMD ["./main"]

Docker Compose

yaml 复制代码
# docker-compose.yml
version: '3.8'

services:
  # 数据库
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: root123
      MYSQL_DATABASE: ecommerce
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql

  postgres:
    image: postgres:13
    environment:
      POSTGRES_DB: payments
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data

  # 缓存和消息队列
  redis:
    image: redis:6-alpine
    ports:
      - "6379:6379"

  # 服务发现
  consul:
    image: consul:1.11
    ports:
      - "8500:8500"
    command: consul agent -dev -ui -client=0.0.0.0

  # 微服务
  user-service:
    build: ./services/user-service
    ports:
      - "8081:8080"
    depends_on:
      - mysql
      - consul
    environment:
      - DB_HOST=mysql
      - CONSUL_HOST=consul

  order-service:
    build: ./services/order-service
    ports:
      - "8082:8080"
    depends_on:
      - mysql
      - consul
    environment:
      - DB_HOST=mysql
      - CONSUL_HOST=consul

  payment-service:
    build: ./services/payment-service
    ports:
      - "8083:8080"
    depends_on:
      - postgres
      - redis
      - consul
    environment:
      - DB_HOST=postgres
      - REDIS_HOST=redis
      - CONSUL_HOST=consul

  # API网关
  api-gateway:
    build: ./api-gateway
    ports:
      - "8080:8080"
    depends_on:
      - user-service
      - order-service
      - payment-service
      - consul
    environment:
      - CONSUL_HOST=consul

  # 监控
  prometheus:
    image: prom/prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml

  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin

volumes:
  mysql_data:
  postgres_data:

Kubernetes部署

yaml 复制代码
# k8s/user-service-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
  labels:
    app: user-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: user-service
  template:
    metadata:
      labels:
        app: user-service
    spec:
      containers:
      - name: user-service
        image: ecommerce/user-service:latest
        ports:
        - containerPort: 8080
        env:
        - name: DB_HOST
          value: "mysql-service"
        - name: CONSUL_HOST
          value: "consul-service"
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "200m"
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5

---
apiVersion: v1
kind: Service
metadata:
  name: user-service
spec:
  selector:
    app: user-service
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
  type: ClusterIP

🚀 启动和测试

启动命令

bash 复制代码
# 启动所有服务
docker-compose up -d

# 查看服务状态
docker-compose ps

# 查看日志
docker-compose logs -f user-service

# 停止服务
docker-compose down

API测试

bash 复制代码
# 用户注册
curl -X POST http://localhost:8080/api/v1/users/register \
  -H "Content-Type: application/json" \
  -d '{
    "username": "john_doe",
    "email": "john@example.com",
    "password": "password123"
  }'

# 用户登录
curl -X POST http://localhost:8080/api/v1/users/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "john@example.com",
    "password": "password123"
  }'

# 创建订单
curl -X POST http://localhost:8080/api/v1/orders \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your-jwt-token" \
  -d '{
    "user_id": 1,
    "items": [
      {
        "product_id": 1,
        "quantity": 2,
        "price": 99.99
      }
    ]
  }'

# 创建支付
curl -X POST http://localhost:8080/api/v1/payments \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your-jwt-token" \
  -d '{
    "order_id": 1,
    "user_id": 1,
    "amount": 199.98,
    "payment_method": 1
  }'

📈 性能优化

数据库连接池

go 复制代码
// shared/database/pool.go
package database

import (
    "time"
    "github.com/zhoudm1743/torm/db"
)

func ConfigureConnectionPool(connectionName string) error {
    // 获取底层数据库连接(这需要TORM支持)
    // 这里假设TORM提供了获取底层sql.DB的方法
    
    // sqlDB := db.GetSQLDB(connectionName)
    // sqlDB.SetMaxOpenConns(100)    // 最大连接数
    // sqlDB.SetMaxIdleConns(10)     // 最大空闲连接数
    // sqlDB.SetConnMaxLifetime(time.Hour) // 连接最大生命周期
    
    return nil
}

缓存策略

go 复制代码
// shared/cache/redis.go
package cache

import (
    "context"
    "encoding/json"
    "time"
    
    "github.com/go-redis/redis/v8"
)

type RedisCache struct {
    client *redis.Client
}

func NewRedisCache(addr, password string, db int) *RedisCache {
    rdb := redis.NewClient(&redis.Options{
        Addr:     addr,
        Password: password,
        DB:       db,
    })
    
    return &RedisCache{client: rdb}
}

func (r *RedisCache) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error {
    data, err := json.Marshal(value)
    if err != nil {
        return err
    }
    
    return r.client.Set(ctx, key, data, expiration).Err()
}

func (r *RedisCache) Get(ctx context.Context, key string, dest interface{}) error {
    val, err := r.client.Get(ctx, key).Result()
    if err != nil {
        return err
    }
    
    return json.Unmarshal([]byte(val), dest)
}

func (r *RedisCache) Delete(ctx context.Context, key string) error {
    return r.client.Del(ctx, key).Err()
}

🔒 安全性考虑

JWT认证

go 复制代码
// shared/auth/jwt.go
package auth

import (
    "fmt"
    "time"
    
    "github.com/golang-jwt/jwt/v4"
)

type Claims struct {
    UserID   int    `json:"user_id"`
    Username string `json:"username"`
    Email    string `json:"email"`
    jwt.RegisteredClaims
}

var jwtSecret = []byte("your-secret-key")

func GenerateToken(userID int, username, email string) (string, error) {
    claims := Claims{
        UserID:   userID,
        Username: username,
        Email:    email,
        RegisteredClaims: jwt.RegisteredClaims{
            ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
            IssuedAt:  jwt.NewNumericDate(time.Now()),
            NotBefore: jwt.NewNumericDate(time.Now()),
        },
    }
    
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    return token.SignedString(jwtSecret)
}

func ValidateToken(tokenString string) (*Claims, error) {
    token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
        if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
        }
        return jwtSecret, nil
    })
    
    if err != nil {
        return nil, err
    }
    
    if claims, ok := token.Claims.(*Claims); ok && token.Valid {
        return claims, nil
    }
    
    return nil, fmt.Errorf("invalid token")
}

📊 总结

本文通过构建一个简单的电商微服务系统,深入展示了Go语言微服务架构的最佳实践:

🎯 核心优势

  1. 模块化设计 - 每个服务职责单一,便于维护和扩展
  2. 技术多样性 - 不同服务可采用最适合的技术栈
  3. 独立部署 - 服务间松耦合,支持独立发布
  4. 弹性扩展 - 可根据负载需求灵活伸缩
  5. 故障隔离 - 单点故障不影响整体系统

🛠️ 技术特色

  • TORM集成 - 轻量级ORM,简化数据库操作
  • 云原生支持 - Docker容器化,Kubernetes编排
  • 完整监控 - Prometheus指标,Grafana可视化
  • 服务治理 - Consul服务发现,API网关统一入口
  • 安全保障 - JWT认证,HTTPS传输,数据加密

🚀 最佳实践

  1. 领域驱动设计 - 按业务领域划分服务边界
  2. 数据一致性 - 采用事件驱动架构保证最终一致性
  3. 错误处理 - 统一错误格式,完善降级策略
  4. 性能优化 - 连接池、缓存、异步处理
  5. 运维友好 - 结构化日志,健康检查,优雅关闭

通过本文的实践,您可以构建出生产级别的Go语言微服务系统,为企业数字化转型提供强有力的技术支撑。


微服务架构不是银弹,但在合适的场景下,它能帮助我们构建更加灵活、可靠和可扩展的系统。选择Go语言作为微服务开发语言,将让您的系统具备卓越的性能和优雅的代码结构。

相关推荐
rannn_11138 分钟前
【Javaweb学习|黑马笔记|Day1】初识,入门网页,HTML-CSS|常见的标签和样式|标题排版和样式、正文排版和样式
css·后端·学习·html·javaweb
柏油1 小时前
Spring @Cacheable 解读
redis·后端·spring
柏油2 小时前
Spring @TransactionalEventListener 解读
spring boot·后端·spring
两码事3 小时前
告别繁琐的飞书表格API调用,让飞书表格操作像操作Java对象一样简单!
java·后端
shark_chili4 小时前
面试官再问synchronized底层原理,这样回答让他眼前一亮!
后端
灵魂猎手4 小时前
2. MyBatis 参数处理机制:从 execute 方法到参数流转全解析
java·后端·源码
易元4 小时前
模式组合应用-桥接模式(一)
后端·设计模式
柑木4 小时前
隐私计算-SecretFlow/SCQL-SCQL的两种部署模式
后端·安全·数据分析
灵魂猎手4 小时前
1. Mybatis Mapper动态代理创建&实现
java·后端·源码
泉城老铁4 小时前
在秒杀场景中,如何通过动态调整线程池参数来应对流量突增
后端·架构