10.5--商品与秒杀活动测试

测试前准备

1. MySQL 已启动,数据库 secgo_mall 已创建
2. Redis 已启动(redis-server 或 Docker)
3. 服务已启动:go run cmd/secgo-mall/main.go
4. 安装Postman与ab/hey,并了解如何操作(这里选用hey)

测试工具推荐

工具 用途
ab (Apache Bench) 简单并发测试
wrk 高性能 HTTP 压测
redis-cli 手动验证 Redis 状态
Postman 单接口功能测试
hey(原weighttp) ab更适合windows系统
# hey安装
go install github.com/rakyll/hey@latest

测试一:商品模块

Step 1: 创建商品
POST http://localhost:8080/api/product/create
Headers: Authorization: Bearer <TOKEN>
Body: {
    "name": "iPhone 16",
    "description": "苹果旗舰手机",
    "price": 8999.00,
    "stock": 100
}
预期:返回商品信息,包含 product_id

Step 2: 查看商品列表
GET http://localhost:8080/api/product/list
Headers: Authorization: Bearer <TOKEN>
预期:返回包含刚才创建商品的列表

step 1:

刷新数据库可以看到product表中出现iPhone 16

商品创建-iPhone_16

step 2:

这里我多创建了一个iPhone 16 Pro商品,可以看到返回Json中带有列表中所有商品

获取商品列表


测试二:秒杀活动模块

Step 1: 创建秒杀活动
POST http://localhost:8080/api/seckill/create
Headers: Authorization: Bearer <TOKEN>
Body: {
    "product_id": 1,
    "name": "iPhone 16 限时秒杀",
    "price": 1.00,
    "stock": 10,
    "start_time": 1775966400,
    "end_time": 1776657600
}
注意:start_time 和 end_time 为 Unix 时间戳(秒)
start_time 为 2026-04-12 12:00:00
end_time 为 2026-04-20 12:00:00
预期:返回秒杀活动信息,status=0

Step 2: 验证 Redis 库存预热
redis-cli
> GET seckill:stock:1
"10"
预期:库存已写入 Redis

Step 3: 查看进行中的秒杀活动
GET http://localhost:8080/api/seckill/list/active
Headers: Authorization: Bearer <TOKEN>
预期:返回活动列表(当前 start_time 需设为当前时间之前才显示)

step 1:

创建16Pro秒杀活动

step 2:

可以看到秒杀活动完成了商品库存位于Redis中的预热

查看缓存库存

step 3:

正在进行(status == 1)的秒杀只有两个

获取活跃秒杀列表


测试三:抢购模块

Step 1: 抢购(正常)
POST http://localhost:8080/api/seckill/purchase
Headers: Authorization: Bearer <TOKEN>
Body: {"seckill_id": 2}
预期:{"code": 0, "message": "success", "data": {"message": "purchase success, order is being processed"}}

Step 2: 验证 Redis 库存减少
redis-cli
> GET seckill:stock:1
"9"
预期:库存从 10 减到 9

Step 3: 库存耗尽后再抢
重复 Step 1,直到库存为 0
> GET seckill:stock:1
"0"

Step 4: 库存为 0 后再抢
POST http://localhost:8080/api/seckill/purchase
Headers: Authorization: Bearer <TOKEN>
Body: {"seckill_id": 1}
预期:{"code": 0, "message": "stock not enough"}

step 1

由于尚未加入消息队列,抢购成功后并不会更新MySQL数据表中stock的数据

下单秒杀iPhone16Pro

step 2

缓存中的stock已经顺利更新

成功扣除缓存库存

step 3

在Postman中再send九次之后

缓存库存为空

step 4

在Postman中再次send一次,顺利报错

抢购(缓存缺货)


测试四:活动状态校验

场景 A: 活动未开始(start_time > now)
POST http://localhost:8080/api/seckill/purchase
Body: {"seckill_id": 活动id}
预期:{"code": -2, "message": "seckill not started"}

场景 B: 活动已结束(end_time < now)
POST http://localhost:8080/api/seckill/purchase
Body: {"seckill_id": 活动id}
预期:{"code": -3, "message": "seckill ended"}

准备工作

我们先创建一个新商品和两个活动(一个未开始与一个已结束)

POST http://localhost:8080/api/product/create
Headers: Authorization: Bearer <TOKEN>
Body: {
    "name": "Xiaomi 17",
    "description": "小米旗舰手机",
    "price": 4499.00,
    "stock": 100
}

商品创建-Xiaomi17

创建已结束秒杀

创建未开始秒杀

step A:

下单未开始秒杀

step B:

下单已结束秒杀


测试五:高并发测试(有/无 Redis 对比)

事先准备: 我们需要创建两个进行中的秒杀活动

这里我们选择商品3来进行创建,同时秒杀库存设置为100

注: 我在DataGrip将product表中的商品3库存修改为了500,Update的api我会在后续进行补充

A组
{
    "product_id": 3,
    "name": "Xiaomi 17 限时秒杀(秒杀测试)",
    "price": 1.00,
    "stock": 100,
    "start_time": 1775786400,
    "end_time": 1776391200
}

创建成功:

{
    "code": 0,
    "message": "success",
    "data": {
        "seckill_id": 7,
        "product_id": 3,
        "name": "Xiaomi 17 限时秒杀(有Redis测试)",
        "price": 1,
        "stock": 100,
        "start_time": "2026-04-10T10:00:00+08:00",
        "end_time": "2026-04-17T10:00:00+08:00",
        "status": 1,
        "created_at": "2026-04-14T11:55:30.311+08:00"
    }
}
B组
{
    "product_id": 3,
    "name": "Xiaomi 17 限时秒杀(无Redis测试)",
    "price": 1.00,
    "stock": 100,
    "start_time": 1775786400,
    "end_time": 1776391200
}

创建成功:

{
    "code": 0,
    "message": "success",
    "data": {
        "seckill_id": 8,
        "product_id": 3,
        "name": "Xiaomi 17 限时秒杀(无Redis测试)",
        "price": 1,
        "stock": 100,
        "start_time": "2026-04-10T10:00:00+08:00",
        "end_time": "2026-04-17T10:00:00+08:00",
        "status": 1,
        "created_at": "2026-04-14T11:56:35.715+08:00"
    }
}

对照组 A:有 Redis Lua 扣减(当前实现)

# 二选一:(自行去除换行和反斜杠,并替换JSON和TOKEN)

# 用 ab 模拟 100 并发,总共 100 请求
ab -n 120 -c 120 -p purchase.json -T application/json \
   http://localhost:8080/api/seckill/purchase

# 用 hey 模拟
hey -n 120 -c 120 -m POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <TOKEN>" \
  -D purchase.json \
  http://localhost:8080/api/seckill/purchase
# 验证
redis-cli GET seckill:stock:7
→ "0"
MySQL order 表订单数 = 100(无误)

A组并发请求前Redis库存

GroupA_并发请求

A组并发请求后Redis库存

也可以看到MySQL的order表中只创建了100份订单,而非120
![[A组order表中创建了100份订单.png]]

对照组 B:临时模拟无 Redis(直接打 MySQL)

为了保证直接扣减MySQL,我们需要对 service/seckill.go 中的 func SeckillPurchase(seckillId, userId uint) (int, error)改为下图所示:

![[SeckillPurchare修改.png|func SeckillPurchase(seckillId, userId uint) (int, error)]]

	// 跳过 Redis,直接 UPDATE MySQL
	tx := repository.DB.Begin()
	result := tx.Model(&model.Seckill{}).
		Where("seckill_id = ? AND stock > 0", seckillId).
		Update("stock", gorm.Expr("stock - 1"))
	tx.Commit()

	if result.RowsAffected == 0 {
		return 0, errors.New("stock not enough")
	}

	// 不发 MQ,直接返回
	return 1, nil

重启main.go

# 相同并发测试
ab -n 120 -c 120 -p purchase.json -T application/json \
   http://localhost:8080/api/seckill/purchase
   
# or
hey -n 120 -c 120 -m POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <TOKEN>" \
  -D purchase.json \
  http://localhost:8080/api/seckill/purchase

B组并发测试结果

我们不仅能看到超卖报错的提示(stock扣减时必须>0),也可以看到没有缓存下每条抢购请求的平均耗时都显著增加

注:记得将SeckillPurchase()修改为原样


完整测试流程清单

序号 接口 方法 验证点
1 /api/product/create POST 商品创建成功
2 /api/product/list GET 商品列表返回
3 /api/seckill/create POST 活动创建 + Redis 预热
4 redis-cli GET seckill:stock:1 CLI 库存预热成功
5 /api/seckill/list GET 活动列表返回
6 /api/seckill/purchase POST 抢购成功,库存-1
7 redis-cli GET seckill:stock:1 CLI 库存递减验证
8 多次 /api/seckill/purchase POST 库存耗尽返回失败
9 ab 高并发测试 CLI Lua 扣减不超卖
posted @ 2026-04-17 17:45  Chuan81  阅读(34)  评论(0)    收藏  举报