一文说清楚分层架构
软件架构中,如果只能保留俩个最重要的架构模式,那必须是分层架构和流水线架构,如果你构建或者分析一个已有系统的时候,不知道从何处入手,可以先尝试从分层架构或者流水线架构入手。
架构的本质是系统拆分成子系统/子模块,并相互协作。分层和流水线架构是直观的拆分方式
本博文包括内容为分层架构的介绍和举例,后续博客将介绍流水线架构,微内核架构等
需要注意的是:分层和流水线这这种概念既出现在架构书籍中,也出现在程序设计模式书籍中。架构与设计,对于程序员来说俩着并无区别,通常来讲,架构更关注整体系统,设计模式更关注系统中的一个子系统,或者模块。
分层架构风格
定义
将系统分为多层,每层完成部分系统功能,并传递到下一层完成剩下的功能。每个层只关注划分到自己层的功能。分层架构有利于快速将一个复杂系统分解成多个子系统。
实例:
Linux,TCP/ISO网络模型,程序语言编译,单体应用发展为客户端-服务器端,或者JavaEE应用的N层架构,Zookeeper等分布式服务
在写本书的时候,JDK不断发布新功能额,有数以千计的性能,稳定性和安全更新。 Linux的内核也发布6.5,同样包含支持更多硬件以及内部性能提升。JVM作为应用程序运行在Linux上. JVM和Linux内核优化,互相不影响,这得益于分层架构风格。

Java企业开发采用经典的的三层架构。表现层负责展生成展现页面,或者从用户请求那里收集参数,业务层负责业务处理,如处理生成订单,并根据订单占用库存, 持久层又叫数据库访问层,把业务数据持久化到数据库。

另外一个例子是语言编译,以表达式引擎为例子,计算表达式“ab+2*11”,分为如下3步,每一步的处理结果传到下一步
- 表达式字符流转化成tokens流
- 将tokens流转化成AST
- 遍历AST,计算表达式结果

使用伪代码表示:
Tokens tokens = parseString(intput);
AST tree = parseToken(tokens);
// tree 对象可被缓存 input->tree,以避免再次解析
Map map = new HashMap();
map.put("ab",10);
int ret = visitAndCalc(tree,map);| 层 | 完成功能 |
|---|---|
| parseString | 解析结果是“ab” ,"+","2","*","11" 5个token |
| parseToken | parseToken 构造一个语法树,如上图AST所示 |
| visitAndCalc | 遍历树,计算每个节点的值。 可选的,可以在遍历计算前,对表达式进行优化,如2*11是个常量,可以直接 |
所有的架构和设计书,都把语言编译归类为流水线架构,也有少量文章认为从高层次讲还是以分层架构为主。我这边把语言编译归类为分层架构是因为现代的流水线架构通常具备流水线基础组件(如工作流引擎)以及可动态组装流水线节点,语言编译显然不符合流水线架构
分布式协调服务,多个服务器需要达成一致,每个服务通常有如下分层架构

- 最上层是数据,以zookeeper为例子,是一个在内存的数结构DataTree(对于其他分布式服务,比如Redis,是KV数据结构)
- 中间这一层是Raft/ZAB层,这一层负责执行各种操作OP,实现领导选择,分布式提交等协调或者对上一层数据操作。
- 最下层是负责与其他服务器通信的网络层,可以用Netty或者JDK NIO实现。OP命令将被序列化发送到到其他从节点
分层架构主要有3个优势
- 开发人员或者团队可只关注整个结构中的其中某一层,以Java的3层架构来说,表现层人员只关注页面的渲染和数据提交,这类开发人员精通 Spring MVC,以及模板渲染技术。 业务层关注如何实现业务逻辑,这类开发人员更熟悉业务模型, 数据库操作层的开发人员负责处理数据库访问。另外以Linux系统体系来说,应用开发人员不需要关注如何与硬件打叫道,它只需要调用内核提供的系统接口。
- 可以很容易的用新的实现来替换原有层次的实现,现在Web应用采用前后端分离,因此传统视图层采用模板引擎渲染HTML不在适合一些应用,可以替换成VUE,React这样的技术。业务层和持久层不需要调整。再比如,如果库存从数据库转成redis存储,那么,只需要数据库操作层改动! 像Zookeeper这样的分布式协调服务,可以替换网络层;或者替换ZAB层,使用新的一致性协议。
- 层于层之间还可以添加和删除层,不影响其他层。 以上面表达式计算为例子,可以在遍历语法树前,可以添加一层优化语法树,对常量表达式2*11 进行提前计算。类似
Tokens tokens = parseString(intput);
AST tree = parseToken(tokens);
AST tree = fold(tree) // 优化,2*11直接优化为结果22,上图+号的左边节点为22常量
Map map = new HashMap();
map.put("ab",10);
int ret = visitAndCalc(tree,map);分层架构主要有3个缺点
- 会导致各层级联的修改,分层架构能屏蔽 层内的修改,使得可修改性提升,但有些修改需要穿透每一层,比如创建订单,如果增加了个优惠卷ID需要保存,那么,除了数据库订单库需要增加一个优惠卷字段外,每一层都需要调整,以传输优惠卷ID
- 性能可能出现损耗,比如每一层都对入参数据进行校验,以及调用者的权限校验,以及对入参做一定的数据转化适合本层调用。
- 过度分层:架构师容易过度对系统进行分层从而带来上诉俩个缺点。TCP模型从实践出发,就精简了理论上OSI模型,从7层精简为4层。
