系统架构设计:分层架构、微服务拆分、服务注册与发现、API网关设计
好,咱们直接进入正题。抢单系统这种高并发场景,架构设计要是没想清楚,后面全是坑。我见过太多团队一上来就撸代码,结果压测一跑,服务直接雪崩。今天我就把这几年的实战经验掰开揉碎了讲给你听。
一、分层架构:别把代码写成大泥球
分层架构,说白了就是让代码各司其职。我习惯把抢单系统分成四层:
- 接入层:负责流量入口,处理限流、鉴权、防重放
- 业务层:核心抢单逻辑,订单生成,库存扣减
- 服务层:封装原子能力,比如用户服务、商品服务、订单服务
- 数据层:数据库、缓存、消息队列的读写操作
你可能会问,为什么非要分这么细?我在项目中遇到过一件事:有个同事把库存扣减逻辑直接写在Controller里,结果后来要加个预扣库存功能,改得那叫一个痛苦。分层的好处就是——改一层,不影响其他层。
核心原则:上层依赖下层,下层不依赖上层。接入层不能直接操作数据库,业务层不能处理HTTP请求。
二、微服务拆分:拆得不好,比不拆还惨
微服务不是拆得越细越好。我见过一个项目,把用户模块拆成了「用户基本信息服务」「用户地址服务」「用户积分服务」……结果一个简单的用户详情页要调5个接口,延迟直接爆炸。
抢单系统我建议这样拆:
| 服务名称 | 职责 | 拆分理由 |
|---|---|---|
| 抢单核心服务 | 处理抢单请求、库存扣减、订单生成 | 核心链路,需要独立扩缩容 |
| 用户服务 | 用户信息、黑名单、风控 | 与抢单逻辑解耦,方便复用 |
| 商品服务 | 商品信息、库存查询 | 库存操作频繁,需要独立优化 |
| 订单服务 | 订单持久化、状态流转 | 写压力大,需要异步处理 |
| 通知服务 | 推送抢单结果、短信、站内信 | 非核心链路,允许降级 |
我的经验:拆分时先看「变更频率」和「资源消耗」。经常一起改的,别拆;资源消耗差异大的,必须拆。
三、服务注册与发现:别让服务互相找不到
微服务之间怎么互相找到?你想想看,如果每个服务都硬编码对方的IP地址,那扩缩容的时候不得改死你?
我一般用Nacos或者Consul。抢单系统里,我推荐Nacos,原因很简单:它同时支持配置中心和注册中心,少维护一个组件。
核心流程是这样的:
- 服务启动时,把自己的IP和端口注册到Nacos
- 服务每隔几秒发一次心跳,告诉Nacos「我还活着」
- 调用方从Nacos拉取服务列表,然后负载均衡调用
- 服务挂了,Nacos踢掉它,调用方自动感知
// 伪代码示例:服务注册
@Service
public class OrderService {
@PostConstruct
public void register() {
// 注册到Nacos
nacosClient.registerInstance("order-service",
InetAddress.getLocalHost().getHostAddress(), 8080);
}
}
避坑指南:我曾经遇到过一个问题——服务注册成功了,但调用方就是调不通。查了半天,原来是服务注册的IP是内网IP,但调用方在另一个网段。所以注册时一定要确认网络可达性。
四、API网关设计:流量入口的把关人
网关是系统的第一道防线。我习惯用Spring Cloud Gateway,性能好,而且支持响应式编程。
网关在抢单系统里主要干这几件事:
- 路由转发:根据请求路径,转发到对应的微服务
- 限流熔断:防止流量把后端冲垮
- 鉴权校验:验证Token,拦截非法请求
- 日志记录:记录每次请求的入参、出参、耗时
我画了一张图,帮你理解网关在整个架构中的位置:
网关的限流策略,我一般用令牌桶算法。抢单开始前,先往桶里放好令牌。每个请求来了拿一个令牌,拿不到就直接返回「抢单太火爆」。这样能保证后端服务不会被打满。
// 网关限流配置示例
spring:
cloud:
gateway:
routes:
- id: order-service
uri: lb://order-service
predicates:
- Path=/order/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 100
redis-rate-limiter.burstCapacity: 200
小技巧:网关层可以做「灰度发布」。比如新版本上线,先让5%的流量走新服务,没问题了再全量切换。这个在抢单系统里特别实用,能避免一次上线搞崩全站。
嗯,架构设计这块就聊这么多。记住一句话:架构是演进来的,不是设计出来的。先跑通核心链路,再慢慢优化。别一开始就想搞个完美的架构,那反而容易把自己绕进去。