第二章:项目架构设计——整体架构图、分层架构设计、微服务拆分策略、技术选型

好,咱们接着聊。上一章我们把秒杀系统的业务需求、核心难点都捋了一遍。这一章,我带你看看整个项目的骨架是怎么搭起来的。说白了,就是画一张大图,把各个模块、技术栈都摆上去。

我个人习惯,做架构设计之前,先想清楚两件事:流量怎么走数据怎么存。想明白这两点,剩下的就是填坑了。

2.1 整体架构图:一张图看懂全貌

先给你看个简化版的架构图。别怕,我拆开讲。

用户端(浏览器/APP)
    ↓
CDN(静态资源加速)
    ↓
Nginx(反向代理 + 限流 + 负载均衡)
    ↓
网关层(Spring Cloud Gateway / Zuul)
    ↓
业务服务层(微服务集群)
    ├── 用户服务(user-service)
    ├── 商品服务(product-service)
    ├── 订单服务(order-service)
    ├── 秒杀服务(seckill-service) ← 核心
    └── 支付服务(payment-service)
    ↓
中间件层
    ├── Redis(缓存 + 分布式锁 + 计数器)
    ├── RabbitMQ / RocketMQ(削峰填谷)
    └── Elasticsearch(商品搜索)
    ↓
数据持久层
    ├── MySQL(主库:订单、用户、商品)
    └── Redis Cluster(缓存集群)

这张图,我当年做第一个秒杀项目时,画了不下十版。每次压测发现问题,就回来改一版。嗯,架构是改出来的,不是设计出来的。

核心思路:流量从外到内,层层过滤。CDN挡掉静态资源,Nginx做第一层限流,网关做第二层校验,业务层做最后的逻辑处理。每一层都尽量把无效请求挡在外面,别让它们打到数据库。

2.2 分层架构设计:每一层各司其职

我习惯把系统分成四层。你想想看,每一层都有自己该干的事,别越界。

层级 职责 技术选型 避坑点
接入层 流量入口、限流、防爬虫、SSL卸载 Nginx + Lua 我曾经遇到过Nginx配置不当导致连接数打满,整个服务挂了。后来加了限流模块才稳住。
网关层 路由转发、鉴权、参数校验、灰度发布 Spring Cloud Gateway 网关别做太重,只做路由和简单校验。复杂的业务逻辑丢到下层。
业务层 核心业务逻辑、数据一致性、事务管理 Spring Boot + MyBatis-Plus 秒杀服务要独立部署,别跟其他业务混在一起。否则一个接口慢,拖垮整个系统。
数据层 数据存储、缓存、消息队列 MySQL + Redis + RocketMQ MySQL扛不住高并发,必须用Redis做前置缓存。MQ用来削峰,别让数据库直接面对洪峰。

这里有个细节:每一层之间通过RPC或消息队列通信,别搞成同步调用链。我曾经见过一个项目,秒杀请求经过5个服务同步调用,结果一个服务慢了几百毫秒,整个链路都堵死了。后来改成异步+回调,问题就解决了。

2.3 微服务拆分策略:拆到什么粒度合适?

微服务拆分,说白了就是「高内聚、低耦合」。但拆得太细,运维成本高;拆得太粗,又发挥不出微服务的优势。

我建议按业务边界来拆。秒杀系统里,我一般拆成这几个服务:

  • 用户服务(user-service):登录、注册、用户信息查询。这个服务相对稳定,改动少。
  • 商品服务(product-service):商品信息、库存管理。注意,库存是秒杀的核心数据,要单独维护。
  • 秒杀服务(seckill-service):秒杀活动管理、秒杀逻辑、库存扣减。这是最核心的服务,也是压力最大的。
  • 订单服务(order-service):订单生成、订单状态管理。秒杀成功后,异步生成订单。
  • 支付服务(payment-service):支付对接、退款处理。这个服务可以复用公司现有的支付模块。

我的经验:秒杀服务一定要独立部署,并且做好资源隔离。我曾经见过一个项目,秒杀服务和普通商品服务混在一起,结果秒杀流量一来,普通用户连商品详情页都打不开了。后来把秒杀服务单独拆出来,用独立的服务器集群,问题就解决了。

另外,服务之间的通信,我建议用异步消息。比如秒杀成功后,秒杀服务发一条消息到MQ,订单服务消费消息去生成订单。这样即使订单服务挂了,也不会影响秒杀的核心流程。

2.4 技术选型:为什么选这些?

技术选型这事儿,没有银弹。我选技术栈的标准很简单:社区活跃、文档齐全、团队熟悉。下面是我常用的组合:

技术组件 选型理由 替代方案 我的建议
Spring Boot 生态成熟、上手快、配置简单 Spring Cloud、Quarkus 别纠结版本,用最新的稳定版就行。我一般用2.7.x或3.x。
Redis 高性能缓存、分布式锁、计数器、限流 Memcached、Tair Redis一定要用集群模式,单机扛不住。我踩过单机Redis OOM的坑。
RocketMQ 高吞吐、低延迟、支持事务消息 RabbitMQ、Kafka 秒杀场景推荐RocketMQ,事务消息能保证最终一致性。Kafka也行,但运维成本高。
MySQL 稳定、成熟、支持事务 PostgreSQL、TiDB MySQL做持久化没问题,但别让它直接扛高并发。记得做读写分离。

注意:技术选型不是越新越好。我见过有人用最新的Spring Boot 3.x + JDK 17,结果发现一些老库不兼容,折腾了好几天。我的建议是:用团队最熟悉的技术栈,稳定第一。

2.5 核心流程:一个秒杀请求的完整旅程

最后,我带你走一遍一个秒杀请求的完整流程。你想想看,一个用户点击「立即秒杀」按钮后,发生了什么?

  1. 用户点击按钮 → 请求到达Nginx。Nginx做第一层限流,比如每秒只允许1000个请求通过,超出的直接返回「排队中」。
  2. Nginx转发到网关 → 网关做鉴权,检查用户是否登录、token是否有效。无效的直接返回「请登录」。
  3. 网关路由到秒杀服务 → 秒杀服务先查Redis,看库存是否还有。如果库存为0,直接返回「已售罄」。
  4. 秒杀服务扣减库存 → 用Redis的Lua脚本原子扣减库存。扣减成功后,发送一条消息到MQ。
  5. MQ异步处理 → 订单服务消费消息,生成订单。如果订单生成失败,通过事务消息回滚库存。
  6. 返回结果给用户 → 用户看到「秒杀成功」或「秒杀失败」。

这个流程里,最核心的就是第4步:用Redis原子扣减库存。为什么?因为MySQL扛不住高并发,而Redis单机就能扛10万+ QPS。我当年第一次做秒杀时,直接用MySQL扣库存,结果数据库直接被打挂了。后来改成Redis预扣库存,MySQL异步落库,系统才稳下来。

总结一下:架构设计的核心就是「分层过滤、异步解耦、缓存加速」。每一层都尽量把无效请求挡在外面,让最核心的秒杀逻辑在Redis里完成,MySQL只做最终的数据持久化。

好,这一章就到这里。下一章我们开始动手写代码,从搭建Spring Boot项目开始。到时候我会带你一步步实现秒杀的核心逻辑。