Spring Boot微服务架构下的电商秒杀系统设计与实现

张开发
2026/4/16 10:17:50 15 分钟阅读

分享文章

Spring Boot微服务架构下的电商秒杀系统设计与实现
Spring Boot微服务架构下的电商秒杀系统设计与实现一、业务背景与挑战在大型电商平台如双11、618中秒杀活动是典型的高并发、低延迟、强一致性的业务场景。瞬时流量可达数百万QPS而库存扣减必须严格保证不超卖、不多卖。传统单体架构关系型数据库直连的方式极易导致数据库连接池耗尽、行锁竞争激烈、响应超时甚至服务雪崩。本文以某头部电商大厂真实面试题为蓝本完整呈现一套基于Spring Boot 3.xJakarta EE 9、Redis、RabbitMQ与MySQL的云原生秒杀解决方案。二、技术选型与架构概览核心语言与平台Java 17 JVMG1 GC调优Web框架Spring Boot 3.2 Spring WebFlux非阻塞IO处理预热请求数据库与ORMMySQL 8.0分库分表 MyBatis-Plus HikariCP连接池最小空闲连接20缓存技术Redis ClusterLua脚本原子扣减库存消息队列RabbitMQ异步下单、解耦库存扣减与订单生成安全框架Spring Security JWT防刷接口用户身份鉴权监控与运维Micrometer Prometheus Grafana实时监控QPS、Redis命中率、MQ堆积量三、关键代码实现1. Redis Lua脚本实现原子库存扣减-- KEYS[1]: 商品ID, ARGV[1]: 扣减数量 if redis.call(exists, KEYS[1]) 0 then return -1 -- 库存Key不存在 end local stock tonumber(redis.call(get, KEYS[1])) if stock tonumber(ARGV[1]) then return 0 -- 库存不足 end redis.call(decrby, KEYS[1], ARGV[1]) return 1 -- 扣减成功2. Spring Boot Controller层WebFlux响应式RestController RequestMapping(/api/seckill) public class SeckillController { Autowired private SeckillService seckillService; PostMapping(/try) public MonoResponseEntityString trySeckill( RequestBody SeckillRequest request, RequestHeader(Authorization) String token) { return Mono.fromCallable(() - { // 1. JWT校验 用户风控滑动窗口限流 if (!jwtValidator.validate(token)) { throw new BusinessException(非法访问); } // 2. 调用Lua脚本预扣减 Long result redisTemplate.execute( new DefaultRedisScript(...lua script..., Long.class), Collections.singletonList(request.getProductId()), String.valueOf(request.getQuantity()) ); if (result 1L) { // 3. 发送MQ消息异步创建订单 rabbitTemplate.convertAndSend(seckill.order.exchange, order.route, request); return ResponseEntity.ok(排队中请等待结果通知); } else if (result 0L) { return ResponseEntity.status(429).body(库存不足); } else { return ResponseEntity.status(500).body(系统异常); } }).onErrorResume(BusinessException.class, e - Mono.just(ResponseEntity.badRequest().body(e.getMessage())) ); } }3. 消费端订单落库事务性消息 本地事务表Component public class OrderConsumer { RabbitListener(queues seckill.order.queue) public void handleOrder(SeckillRequest request) { // 1. 先插入本地事务表状态PROCESSING orderMapper.insertWithTx(request); // 2. 调用下游订单服务OpenFeign orderService.createOrder(request); // 3. 更新本地事务表状态SUCCESS orderMapper.updateStatus(request.getId(), SUCCESS); } }四、面试官视角3轮递进式提问第一轮基础原理为什么不用MySQL乐观锁version字段直接扣减库存Redis单线程模型如何支撑百万QPSLua脚本为何能保证原子性WebFlux的Mono/Flux与传统Servlet线程模型本质区别是什么第二轮深度设计 4. 如果MQ消费失败如何保证最终一致性请画出事务消息本地事务表的状态机。 5. 如何防止黄牛使用脚本恶意刷单请结合Spring Security与前端验证码、设备指纹、行为分析给出方案。 6. 当Redis集群某个节点宕机库存数据丢失怎么办是否需要双写DB第三轮高阶扩展 7. 若将秒杀迁移到Kubernetes环境如何通过HPAHorizontal Pod Autoscaler根据Prometheus指标如Redis queue length自动扩缩容 8. 如何用Resilience4j实现熔断降级当库存服务不可用时前端应展示什么友好提示 9. 假设业务要求支持“阶梯价秒杀”买得越多单价越低现有Lua脚本需如何重构面试官微笑点头“很好今天就到这里。你的系统设计思维和源码细节把握很扎实。我们会在3个工作日内通过邮件通知后续流程——祝你拿到心仪的offer”五、答案详解小白也能懂✅问题1答案MySQL乐观锁在高并发下会产生大量失败重试ABA问题CPU浪费严重而Redis Lua在服务端原子执行无网络往返开销。✅问题4答案本地事务表是核心。消费者先写表PROCESSING再调用订单服务若失败则定时任务扫描该表对超时PROCESSING记录重试或告警人工介入最终达到100%最终一致。✅问题7答案需自定义Prometheus指标rabbitmq_queue_messages_ready{queueseckill.order.queue}配置HPAmetrics使用该指标当值1000时触发扩容至10个Pod。延伸学习建议动手用Docker Compose一键部署上述全套环境Redis Cluster RabbitMQ MySQL Spring Boot App并用JMeter模拟10万并发压测观察Grafana面板各项指标变化。

更多文章