秒杀系统 Kafka 架构进阶优化

文章目录

  • 前言
    • 1. Kafka Topic 分区(Partition)设计
    • 2. Kafka 消费者高可用部署(Consumer Scaling)
    • 3. Kafka + Redis 多级限流降级设计
    • 4. 秒杀链路全链路追踪(Tracing)
    • 5. Kafka 死信队列(DLQ)& 重试机制设计
    • 6. 秒杀订单支付过期处理机制
    • 7. Kafka 高可靠配置推荐(Broker 端)
  • 最终版链路总览(超大图思维导图)
    • 小结


前言

✅ 秒杀系统 + Kafka 全链路高可用优化方案


1. Kafka Topic 分区(Partition)设计

秒杀订单 Topic 如何合理分区?

设计策略 说明
商品维度分区 根据 skuId 哈希到不同分区,防止某个 SKU 打爆单分区
用户维度分区(可选) 高频用户集中活动场景,可以按 userId hash

✅ 建议:

kafka-topics.sh --create --topic seckill-orders --partitions 6 --replication-factor 1
  • 秒杀系统建议 分区数 > 机器数 × 2,方便后续水平扩容
  • 选 6、12、24 这种容易分均的数量

import { Kafka } from 'kafkajs';

const kafka = new Kafka({
  clientId: 'nest-app',
  brokers: ['localhost:9092'],
});

const producer = kafka.producer();

async function sendOrderMessage(order: { skuId: string; [key: string]: any }) {
  // 假设 seckill-order topic 有多个分区
  // 用简单 hash 算法将 skuId 路由到不同分区
  function getPartition(skuId: string, partitionCount: number): number {
    let hash = 0;
    for (let i = 0; i < skuId.length; i++) {
      hash = (hash * 31 + skuId.charCodeAt(i)) % partitionCount;
    }
    return hash;
  }

  // 获取 topic 分区数(实际可缓存或用配置)
  const partitionCount = 4; // 假设 seckill-order 有 4 个分区

  const partition = getPartition(order.skuId, partitionCount);

  await producer.send({
    topic: 'seckill-order',
    messages: [
      {
        key: order.skuId, // key 也可以用于分区路由
        value: JSON.stringify(order),
        partition, // 指定分区
      },
    ],
  });
}

不想手动指定 partition,直接用 key:

await producer.send({
  topic: 'seckill-order',
  messages: [
    {
      key: order.skuId, // Kafka 默认会用 key 做分区 hash
      value: JSON.stringify(order),
    },
  ],
});

2. Kafka 消费者高可用部署(Consumer Scaling)

秒杀订单消费模块如何容灾?

策略 说明
多实例部署 同一 Group ID 多实例部署,自动分区分配
自动 Rebalance 消费者宕机后,Kafka 会 Rebalance,剩余实例继续消费
拉取模型优化 开启 fetch.min.bytesfetch.max.wait.ms 批量拉取
手动 Commit 消费成功后才提交 offset,确保消息不丢

3. Kafka + Redis 多级限流降级设计

秒杀场景经常爆发几十万 QPS,你可以这样防御:

层级 限流方式
接口层 Nginx/网关层限流,如 5000 QPS
应用层 Nest.js 内置 @Throttle 全局限速
Redis 层 秒杀 Redis Key 设置超高并发的 Lua 限流脚本
Kafka 层 Broker 端最大堆积量设置,消费端保护机制

✅ 比如 Redis 限流 Lua:

-- 每秒只允许200个抢购
local limit = 200
local current = redis.call('incr', KEYS[1])
if tonumber(current) == 1 then
  redis.call('expire', KEYS[1], 1)
end
if tonumber(current) > limit then
  return 0
else
  return 1
end

4. 秒杀链路全链路追踪(Tracing)

秒杀量大时排查问题困难,推荐引入链路追踪:

工具 说明
OpenTelemetry Kafka Producer/Consumer 打埋点
Jaeger 收集、展示链路,排查秒杀慢点、瓶颈
Prometheus + Grafana Kafka lag、QPS、Error Rate 监控

✅ 典型链路:

用户请求 -> NestJS SeckillController -> Redis -> KafkaProducer -> KafkaBroker
-> KafkaConsumer -> OrderService -> MySQL -> 响应状态

每一跳都可以打 span traceId,秒级排查问题!


5. Kafka 死信队列(DLQ)& 重试机制设计

消费失败怎么办?生产大厂都会设计 DLQ!

✅ 消息失败处理流程:

正常消费 -> 异常捕获 -> 重试 N 次 -> 仍失败 -> 推送到 DLQ Topic
  • 配置专门的 seckill-orders-dlq 死信 Topic
  • 后台监控 DLQ,有运维补偿处理机制

6. 秒杀订单支付过期处理机制

秒杀订单如果未支付,应该自动取消。

实现方式 说明
Redis 过期事件 订单超时时间 setex,自动触发 key 过期
延时消息 Kafka 配合定时轮询扫描,触发取消
定时任务 NestJS @Schedule 扫描 PENDING 订单

✅ 推荐轻量做法:

  • Redis SetEx(order:expire:{orderId})+ 监听过期 key

7. Kafka 高可靠配置推荐(Broker 端)

replication.factor=3
min.insync.replicas=2
acks=all
unclean.leader.election.enable=false
log.retention.hours=72

这样即使一台 Kafka 节点宕机,也不会导致数据丢失。


最终版链路总览(超大图思维导图)

客户端秒杀请求
    ↓
Nginx限流 → Nest限流
    ↓
Redis扣减库存 (Lua原子性)
    ↓
Kafka异步投递订单(分区、分组、冗余副本)
    ↓
Kafka Consumer消费订单 → 创建订单(PENDING)
    ↓
前端轮询查状态
    ↓
支付回调(或超时) → 更新订单(PAID/TIMEOUT)
    ↓
全链路Tracing + Lag监控 + DLQ异常重试

小结

秒杀系统 = 把“超大流量”变成“可控小流量”,保证扣库存快,订单写入稳,异常可恢复,链路可追踪,Kafka 消息安全可靠!


你可能感兴趣的:(kafka,架构)