Spring Boot微服务架构下的电商秒杀系统设计与实现
·
Spring Boot微服务架构下的电商秒杀系统设计与实现
一、业务场景
电商大促期间(如双11),热门商品(如限量手机)面临瞬时百万级QPS请求。传统同步下单流程(查库存→扣库存→生成订单→支付)极易因数据库行锁、连接池耗尽、响应延迟导致系统雪崩。
二、技术选型与架构设计
- 核心框架:Spring Boot 3.2(基于Java 17 + Jakarta EE 9)
- API网关:Spring Cloud Gateway(路由+全局限流)
- 库存管理服务:独立微服务 + Redis原子操作(
DECR+EXPIRE) - 订单服务:响应式编程(Spring WebFlux) + R2DBC异步写入MySQL
- 消息解耦:Kafka作为削峰填谷中枢,解耦下单与订单落库
- 高可用保障:Resilience4j熔断降级 + Sentinel热点参数限流
三、关键代码实现
1. 秒杀接口(带令牌桶限流)
@RestController
@RequestMapping("/api/seckill")
public class SeckillController {
@Autowired private SeckillService seckillService;
@PostMapping("/{skuId}")
public Mono<ResponseEntity<String>> doSeckill(
@PathVariable String skuId,
@RequestHeader("X-User-ID") String userId) {
return Mono.fromCallable(() -> seckillService.trySeckill(skuId, userId))
.onErrorResume(e -> Mono.just(ResponseEntity.badRequest().body("秒杀失败: " + e.getMessage())))
.map(result -> ResponseEntity.ok(result));
}
}
2. 原子化库存扣减(Redis Lua脚本)
// Lua script for atomic inventory check & decrement
String script = "if redis.call('exists', KEYS[1]) == 1 then " +
" local stock = tonumber(redis.call('get', KEYS[1])); " +
" if stock > 0 then " +
" redis.call('decr', KEYS[1]); " +
" return 1; " +
" else " +
" return -1; " +
" end " +
"else " +
" return 0; " +
"end";
Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
Collections.singletonList("seckill:stock:" + skuId));
3. 异步下单(Kafka生产者)
@Service
public class KafkaOrderProducer {
@Autowired private KafkaTemplate<String, SeckillOrderEvent> kafkaTemplate;
public void sendOrderEvent(SeckillOrderEvent event) {
kafkaTemplate.send("seckill-order-topic", event.getUserId(), event);
}
}
四、面试官追问(模拟真实技术深挖)
- Redis单线程模型为何能支撑高并发秒杀?Lua脚本执行期间是否阻塞其他命令?
- 若Kafka消费者积压,如何保证订单不丢失且最终一致性?请结合事务消息或死信队列设计。
- 如何防止黄牛使用脚本刷单?请从网关层(设备指纹+行为分析)、服务层(滑动窗口限频)给出方案。
五、答案详解(小白友好)
✅ 问题1答案:Redis单线程靠避免上下文切换+纯内存操作+IO多路复用(epoll)实现高性能;Lua脚本是原子执行的,期间会阻塞其他命令——这正是我们依赖它做库存强一致的原因。但要注意脚本执行时间不能过长(<10ms),否则影响吞吐。
✅ 问题2答案:采用Kafka事务消息(initTransactions() + sendOffsetsToTransaction())确保“消息发送”与“本地数据库更新”原子性;若消费失败,配置max.poll.interval.ms和enable.auto.commit=false,手动提交offset,并将失败消息转入DLQ主题,由定时任务重试+人工介入。
✅ 问题3答案:网关层集成OpenResty+Lua生成设备唯一指纹(CPU核数+屏幕分辨率+字体列表Hash),并记录用户IP+UA的请求速率(每分钟≤5次);服务层用Redis+ZSET实现滑动窗口限频:ZREMRANGEBYSCORE key -inf (now-60) + ZCARD统计当前窗口请求数。
本文覆盖Spring Boot、Redis、Kafka、R2DBC、Resilience4j等12+技术栈,适合作为Java高级工程师面试准备材料。欢迎在评论区讨论优化方案!
更多推荐


所有评论(0)