第二章:项目架构设计——整体架构图、分层架构设计、微服务拆分策略、技术选型
好,咱们接着聊。上一章我们把秒杀系统的业务需求、核心难点都捋了一遍。这一章,我带你看看整个项目的骨架是怎么搭起来的。说白了,就是画一张大图,把各个模块、技术栈都摆上去。
我个人习惯,做架构设计之前,先想清楚两件事:流量怎么走,数据怎么存。想明白这两点,剩下的就是填坑了。
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 核心流程:一个秒杀请求的完整旅程
最后,我带你走一遍一个秒杀请求的完整流程。你想想看,一个用户点击「立即秒杀」按钮后,发生了什么?
- 用户点击按钮 → 请求到达Nginx。Nginx做第一层限流,比如每秒只允许1000个请求通过,超出的直接返回「排队中」。
- Nginx转发到网关 → 网关做鉴权,检查用户是否登录、token是否有效。无效的直接返回「请登录」。
- 网关路由到秒杀服务 → 秒杀服务先查Redis,看库存是否还有。如果库存为0,直接返回「已售罄」。
- 秒杀服务扣减库存 → 用Redis的Lua脚本原子扣减库存。扣减成功后,发送一条消息到MQ。
- MQ异步处理 → 订单服务消费消息,生成订单。如果订单生成失败,通过事务消息回滚库存。
- 返回结果给用户 → 用户看到「秒杀成功」或「秒杀失败」。
这个流程里,最核心的就是第4步:用Redis原子扣减库存。为什么?因为MySQL扛不住高并发,而Redis单机就能扛10万+ QPS。我当年第一次做秒杀时,直接用MySQL扣库存,结果数据库直接被打挂了。后来改成Redis预扣库存,MySQL异步落库,系统才稳下来。
总结一下:架构设计的核心就是「分层过滤、异步解耦、缓存加速」。每一层都尽量把无效请求挡在外面,让最核心的秒杀逻辑在Redis里完成,MySQL只做最终的数据持久化。
好,这一章就到这里。下一章我们开始动手写代码,从搭建Spring Boot项目开始。到时候我会带你一步步实现秒杀的核心逻辑。