跳转至

02 - 电商平台(微服务架构)

目标: 掌握微服务架构,能设计高并发电商系统

时间: 8-12周

难度: ⭐⭐⭐⭐⭐


🎯 项目概述

业务场景

Text Only
一个中型电商平台,需要支持:
- 日活用户:100万
- 日订单量:10万
- 峰值QPS:5000
- 商品数量:100万
- 需要支持大促活动(秒杀)

技术架构

Text Only
前端:
- React + TypeScript
- Redux Toolkit + RTK Query
- Ant Design Pro

后端(微服务):
- API Gateway: Kong / Spring Cloud Gateway
- 用户服务: Go / Java Spring Boot
- 商品服务: Go / Java Spring Boot
- 订单服务: Go / Java Spring Boot
- 库存服务: Go / Java Spring Boot
- 支付服务: Go / Java Spring Boot
- 搜索服务: Elasticsearch
- 推荐服务: Python + TensorFlow

基础设施:
- 数据库: MySQL(主从)+ Redis Cluster
- 消息队列: RabbitMQ / Kafka
- 缓存: Redis Cluster
- 搜索引擎: Elasticsearch
- 容器化: Docker + Kubernetes
- 监控: Prometheus + Grafana

📚 服务拆分

服务划分原则

Text Only
按业务领域拆分:

1. 用户服务 (User Service)
   - 用户注册/登录
   - 用户信息管理
   - 权限管理

2. 商品服务 (Product Service)
   - 商品信息管理
   - 商品分类
   - 商品搜索

3. 订单服务 (Order Service)
   - 订单创建
   - 订单状态管理
   - 订单查询

4. 库存服务 (Inventory Service)
   - 库存管理
   - 库存扣减/释放
   - 库存预警

5. 支付服务 (Payment Service)
   - 支付处理
   - 退款处理
   - 支付状态查询

6. 购物车服务 (Cart Service)
   - 购物车管理
   - 购物车合并

7. 搜索服务 (Search Service)
   - 商品搜索
   - 搜索建议
   - 筛选过滤

8. 推荐服务 (Recommendation Service)
   - 个性化推荐
   - 热门商品
   - 相关推荐

9. 通知服务 (Notification Service)
   - 短信通知
   - 邮件通知
   - 站内信

🏗️ 核心功能实现

1. 用户服务

Go
// user-service/main.go
package main

import (
    "time"

    "github.com/gin-gonic/gin"
    "golang.org/x/crypto/bcrypt"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

type User struct {
    ID        uint   `gorm:"primarykey"`
    Username  string `gorm:"uniqueIndex;size:50"`
    Email     string `gorm:"uniqueIndex;size:100"`
    Password  string `gorm:"size:255"`
    Phone     string `gorm:"size:20"`
    Status    int    `gorm:"default:1"`
    CreatedAt time.Time
    UpdatedAt time.Time
}

func main() {
    // 连接数据库
    dsn := "user:password@tcp(127.0.0.1:3306)/user_db?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{})

    r := gin.Default()

    // 注册
    r.POST("/api/v1/users/register", func(c *gin.Context) {
        var user User
        if err := c.ShouldBindJSON(&user); err != nil {
            c.JSON(400, gin.H{"error": err.Error()})
            return
        }

        // 密码加密
        hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
        user.Password = string(hashedPassword)

        result := db.Create(&user)
        if result.Error != nil {
            c.JSON(500, gin.H{"error": result.Error.Error()})
            return
        }

        c.JSON(201, gin.H{
            "id": user.ID,
            "username": user.Username,
            "email": user.Email,
        })
    })

    // 登录
    r.POST("/api/v1/users/login", func(c *gin.Context) {
        var req struct {
            Username string `json:"username"`
            Password string `json:"password"`
        }

        if err := c.ShouldBindJSON(&req); err != nil {
            c.JSON(400, gin.H{"error": err.Error()})
            return
        }

        var user User
        result := db.Where("username = ?", req.Username).First(&user)
        if result.Error != nil {
            c.JSON(401, gin.H{"error": "Invalid credentials"})
            return
        }

        // 验证密码
        err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password))
        if err != nil {
            c.JSON(401, gin.H{"error": "Invalid credentials"})
            return
        }

        // 生成JWT
        token := generateJWT(user.ID)

        c.JSON(200, gin.H{
            "token": token,
            "user": gin.H{
                "id": user.ID,
                "username": user.Username,
            },
        })
    })

    r.Run(":8081")
}

2. 订单服务(分布式事务)

Go
// order-service/main.go
package main

import (
    "context"
    "encoding/json"
    "fmt"

    "github.com/segmentio/kafka-go"
    "gorm.io/gorm"
)

// Saga模式实现分布式事务
type OrderSaga struct {
    db *gorm.DB
    kafkaWriter *kafka.Writer
}

func (s *OrderSaga) CreateOrder(ctx context.Context, req CreateOrderRequest) error {
    // 1. 创建订单(本地事务)
    order := Order{
        UserID: req.UserID,
        TotalAmount: req.TotalAmount,
        Status: "PENDING",
    }

    tx := s.db.Begin()
    if err := tx.Create(&order).Error; err != nil {
        tx.Rollback()
        return err
    }

    // 2. 发送扣减库存消息
    inventoryMsg := map[string]interface{}{  // interface定义行为契约
        "order_id": order.ID,
        "items": req.Items,
    }
    msgBytes, _ := json.Marshal(inventoryMsg)

    err := s.kafkaWriter.WriteMessages(ctx, kafka.Message{
        Key:   []byte(fmt.Sprintf("order-%d", order.ID)),
        Value: msgBytes,
        Topic: "inventory-deduct",
    })

    if err != nil {
        tx.Rollback()
        return err
    }

    tx.Commit()
    return nil
}

// 库存扣减成功回调
func (s *OrderSaga) OnInventoryDeducted(ctx context.Context, orderID uint) error {
    // 更新订单状态为已支付
    return s.db.Model(&Order{}).Where("id = ?", orderID).Update("status", "PAID").Error
}

// 库存扣减失败补偿
func (s *OrderSaga) OnInventoryDeductFailed(ctx context.Context, orderID uint) error {
    // 取消订单
    return s.db.Model(&Order{}).Where("id = ?", orderID).Update("status", "CANCELLED").Error
}

3. 秒杀系统(高并发)

Go
// seckill-service/main.go
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "sync"
    "time"

    "github.com/redis/go-redis/v9"
    "github.com/segmentio/kafka-go"
    "gorm.io/gorm"
)

type SeckillService struct {
    redisClient *redis.Client
    db          *gorm.DB
    kafkaWriter *kafka.Writer
    // 本地库存缓存
    localStock map[string]int64
    mu         sync.RWMutex
}

// 预热库存到Redis
func (s *SeckillService) PreheatStock(productID string, stock int64) error {
    key := fmt.Sprintf("seckill:stock:%s", productID)
    return s.redisClient.Set(context.Background(), key, stock, 24*time.Hour).Err()
}

// 秒杀下单
func (s *SeckillService) Seckill(ctx context.Context, userID, productID string) (bool, error) {
    // 1. 用户限流(一个用户只能秒杀一次)
    userKey := fmt.Sprintf("seckill:user:%s:%s", productID, userID)
    exists, err := s.redisClient.Exists(ctx, userKey).Result()
    if err != nil || exists > 0 {
        return false, fmt.Errorf("already participated")
    }

    // 2. 扣减Redis库存(Lua脚本保证原子性)
    stockKey := fmt.Sprintf("seckill:stock:%s", productID)
    luaScript := `
        local stock = tonumber(redis.call('get', KEYS[1]))
        if stock == nil or stock <= 0 then
            return 0
        end
        redis.call('decr', KEYS[1])
        return 1
    `

    result, err := s.redisClient.Eval(ctx, luaScript, []string{stockKey}).Result()
    if err != nil || result.(int64) == 0 {
        return false, fmt.Errorf("out of stock")
    }

    // 3. 标记用户已参与
    s.redisClient.Set(ctx, userKey, 1, 24*time.Hour)

    // 4. 异步创建订单(发送到消息队列)
    orderMsg := map[string]string{
        "user_id":    userID,
        "product_id": productID,
    }
    msgBytes, _ := json.Marshal(orderMsg)

    // 发送到Kafka异步处理
    s.kafkaWriter.WriteMessages(ctx, kafka.Message{
        Value: msgBytes,
        Topic: "seckill-order",
    })

    return true, nil
}

// 异步处理订单
func (s *SeckillService) ProcessSeckillOrder(ctx context.Context, msg kafka.Message) error {
    var orderMsg map[string]string
    json.Unmarshal(msg.Value, &orderMsg)

    // 创建订单
    order := Order{
        UserID:    orderMsg["user_id"],
        ProductID: orderMsg["product_id"],
        Status:    "PAID", // 秒杀直接支付
    }

    return s.db.Create(&order).Error
}

🎯 项目检查点

  • 完成所有服务的开发
  • 实现分布式事务
  • 完成秒杀功能
  • 实现服务治理
  • 完成监控告警
  • 通过压力测试

这个项目完成后,你将掌握微服务架构的核心技能! 🚀