2. 分层架构设计:接入层、应用层、服务层、数据层的职责与解耦策略
说到高并发架构,我第一个想聊的就是分层。为什么?因为我在项目里踩过太多不分层的坑了。你想想看,一个OA系统,用户量从几百涨到几万,如果代码全揉在一起,那改一个功能就像拆炸弹——剪错一根线,整个系统就崩了。
分层架构,说白了就是「各司其职」。每一层只干自己的事,不越界。这样出了问题,你一眼就能看出是哪层的锅。我习惯把OA系统分成四层:接入层、应用层、服务层、数据层。咱们一层一层拆开看。
2.1 接入层:系统的「门卫」
接入层是系统的第一道防线。用户的所有请求,都得先过它这一关。它的职责很纯粹:流量过滤、负载均衡、安全防护。
我在项目中遇到过,有次OA系统突然被大量恶意请求打爆了。查了半天,发现是接入层没做好限流。从那以后,我坚持在接入层做三件事:
- IP白名单/黑名单:内部OA系统,只允许公司VPN或内网IP访问。
- 限流降级:比如用Nginx的limit_req模块,每秒超过1000个请求直接返回503。
- SSL卸载:把HTTPS解密放在接入层,后端服务不用操心加密解密,性能能提升不少。
核心原则:接入层不处理任何业务逻辑。它只做「转发」和「过滤」。业务代码一旦混进来,你就等着头疼吧。
举个例子,一个审批请求过来,接入层只做三件事:检查IP是否合法、判断QPS是否超标、把请求转发给应用层。完事。至于这个审批是请假还是报销,接入层一概不管。
2.2 应用层:系统的「调度中心」
应用层是离用户最近的一层。它负责接收接入层转来的请求,然后调用服务层完成业务。说白了,应用层就是个「调度员」,它不干具体的活,但知道该找谁干。
我建议应用层只做三件事:
- 参数校验:比如用户提交的请假天数是不是正整数,日期格式对不对。
- 会话管理:判断用户是否登录,权限够不够。
- 路由分发:根据请求类型,调用对应的服务层接口。
嗯,这里要注意。很多新手会把业务逻辑写在应用层。比如在Controller里直接写SQL查询。我曾经接手过一个项目,应用层里塞了上千行的业务代码,改一个字段要翻半天。后来我强制要求:应用层只做编排,不做实现。
我的习惯:应用层的代码,每行不超过50行。如果超过,说明你该抽一个服务出来了。
2.3 服务层:系统的「业务大脑」
服务层是核心。所有复杂的业务逻辑、计算、状态流转,都放在这一层。它不关心请求从哪来,也不关心数据存哪去,只关心「这个业务该怎么跑」。
我举个例子。OA系统里有个「审批流转」功能。应用层收到请求后,调用服务层的approvalService.submit()。服务层会做这些事:
- 检查审批人是否在岗
- 判断是否需要会签
- 更新审批状态
- 触发通知(邮件、短信)
你看,这些全是业务逻辑。如果哪天审批规则变了,你只需要改服务层,应用层和数据层完全不用动。这就是解耦的好处。
避坑指南:我曾经把缓存逻辑写在服务层里,结果缓存挂了,业务也跟着挂了。后来我学乖了:服务层只依赖接口,不依赖具体实现。缓存、消息队列这些基础设施,通过依赖注入进来,方便切换。
2.4 数据层:系统的「仓库管理员」
数据层负责跟数据库打交道。它的职责很单一:增删改查。但别小看它,高并发下数据层往往是瓶颈。
我习惯在数据层做这几件事:
- 读写分离:主库写,从库读。OA系统里,查询请求占80%以上,读写分离能大幅提升性能。
- 分库分表:比如审批记录表,按月份分表。查上个月的数据,只扫一张表,快得多。
- 连接池管理:用HikariCP或者Druid,设置合理的最大连接数。我曾经见过有人设了1000个连接,结果数据库直接挂了。
数据层还有一个容易被忽略的点:事务控制。我建议事务只放在服务层,数据层不开启事务。为什么?因为数据层只做单表操作,事务放在服务层可以控制多个数据操作的原子性。
核心原则:数据层不包含任何业务逻辑。它只提供「根据ID查用户」、「根据状态查审批单」这样的原子操作。业务逻辑一旦进入数据层,你就等着改SQL改到哭吧。
2.5 解耦策略:让各层「老死不相往来」
分层容易,解耦难。我见过很多系统,虽然分了层,但层与层之间耦合得死死的。比如应用层直接new了一个服务层的实现类,想换一个实现还得改代码。
我常用的解耦策略有这几个:
| 策略 | 说明 | 我的经验 |
|---|---|---|
| 接口隔离 | 层与层之间只通过接口通信 | 应用层只依赖服务层的接口,不依赖实现类。这样换实现就像换电池一样简单。 |
| 依赖注入 | 用Spring等框架管理依赖 | 我习惯把数据源的配置放在配置文件里,通过@Autowired注入。这样切换数据库环境,改个配置就行。 |
| 事件驱动 | 层与层之间通过消息解耦 | 比如审批通过后,服务层发一个事件,应用层监听事件做通知。这样服务层不用关心通知逻辑。 |
| 数据契约 | 定义统一的数据传输对象 | 各层之间传递的数据结构要固定。我吃过亏,有一次改了DTO的字段名,结果前端报错查了半天。 |
举个例子,服务层和数据层之间,我通常会定义一个Repository接口。数据层实现这个接口,服务层只调用接口方法。这样如果我想从MySQL切换到TiDB,只需要重新实现Repository接口,服务层代码一行都不用改。
一个小技巧:每层之间只传递「必要的数据」。比如应用层调用服务层时,只传用户ID和审批单ID,不要传整个用户对象。这样能减少层与层之间的依赖。
2.6 总结:分层不是目的,解耦才是
说了这么多,其实就一句话:分层是为了让系统更容易维护和扩展。你想想看,如果哪天OA系统要接入移动端,你只需要在接入层加一个API网关,应用层和服务层完全不用动。这就是分层的价值。
我个人习惯,在项目初期就定好分层规范。哪怕只有几个接口,也要按四层结构来写。因为等系统复杂了再重构,成本高得吓人。嗯,这个坑我踩过,希望你别踩。
下一章,咱们聊聊「服务层的高并发设计:无状态服务与线程池调优」。到时候我会分享一些实战中的调优参数,都是真金白银换来的经验。