从零开始学架构——可扩展架构模式

可扩展架构模式的基本思想和模式

软件系统与硬件和建筑系统最大的差异在于软件是可扩展的,一个硬件生产出来后就不会再进行改变、一个建筑完工后也不会再改变其整体结构
例如,一颗 CPU 生产出来后装到一台 PC 机上,不会再返回工厂进行加工以增加新的功能;金字塔矗立千年历经风吹雨打,但其现在的结构和当时建成完工时的结构并无两样。
相比之下,软件系统就完全相反,真正有生命力的软件系统,都是在不断迭代和发展的。典型的如 Windows 操作系统,从 Windows 3.0 到 Windows 95 到 Windows XP,直到现在的 Windows 10,一直在跟着技术的发展而不断地发展。如果一个软件系统开发出来后,再也没有任何更新和调整,反而说明了这套软件系统没有发展、没有生命力。
软件系统的这种天生和内在的可扩展的特性,既是魅力所在,又是难点所在。

  • 魅力体现在我们可以通过修改和扩展,不断地让软件系统具备更多的功能和特性,满足新的需求或者顺应技术发展的趋势。
  • 难点体现在如何以最小的代价去扩展系统,因为很多情况下牵一发动全身,扩展时可能出现到处都要改,到处都要推倒重来的情况。这样做的风险不言而喻:改动的地方越多,投入也越大,出错的可能性也越大。

因此,如何避免扩展时改动范围太大,是软件架构可扩展性设计的主要思考点。

可扩展的基本思想

可扩展性架构的设计方法很多,但万变不离其宗,所有的可扩展性架构设计,背后的基本思想都可以总结为一个字:拆!
拆,就是将原本大一统的系统拆分成多个规模小的部分,扩展时只修改其中一部分即可,无须整个系统到处都改,通过这种方式来减少改动范围,降低改动风险。
面对软件系统,拆没那么简单,因为我们并不是要摧毁一个软件系统,而是要通过拆让软件系统变得更加优美(具备更好的可扩展性)。形象地说,软件系统中的“拆”是建设性的,因此难度要高得多。
按照不同的思路来拆分软件系统,就会得到不同的架构。常见的拆分思路有如下三种

  • 面向流程拆分:将整个业务流程拆分为几个阶段,每个阶段作为一部分
  • 面向服务拆分:将系统提供的服务拆分,每个服务作为一部分
  • 面向功能拆分:将系统提供的功能拆分,每个功能作为一部分

理解这三种思路的关键就在于如何理解“流程”、“服务”、“功能”三者的联系和区别。
从范围上来看,从大到小依次为:流程 > 服务 > 功能,单纯从概念解释可能难以理解,但实际上看几个案例就很清楚了
以TCP/IP协议栈为例:
从零开始学架构——可扩展架构模式_第1张图片

  • 流程
    对应 TCP/IP 四层模型,因为 TCP/IP 网络通信流程是:应用层 → 传输层 → 网络层 → 物理 + 数据链路层,不管最上层的应用层是什么,这个流程都不会变。
  • 服务
    对应应用层的 HTTP、FTP、SMTP 等服务,HTTP 提供 Web 服务,FTP 提供文件服务,SMTP 提供邮件服务,以此类推
  • 功能
    每个服务都会提供相应的功能。例如,HTTP 服务提供 GET、POST 功能,FTP 提供上传下载功能,SMTP 提供邮件发送和收取功能
    以一个简单的学生信息管理系统为例,拆分方式是:
  1. 面向流程拆分
    展示层 → 业务层 → 数据层 → 存储层,各层含义是:
    展示层:负责用户页面设计,不同业务有不同的页面。例如,登录页面、注册页面、信息管理页面、安全设置页面等
    业务层:负责具体业务逻辑的处理。例如,登录、注册、信息管理、修改密码等业务
    数据层:负责完成数据访问。例如,增删改查数据库中的数据、记录事件到日志文件等
    存储层:负责数据的存储。例如,关系型数据库 MySQL、缓存系统 Memcache 等
    最终架构如下,
    从零开始学架构——可扩展架构模式_第2张图片

  2. 面向服务拆分
    将系统拆分为注册、登录、信息管理、安全设置等服务,最终架构示意图如下:
    从零开始学架构——可扩展架构模式_第3张图片

  3. 面向功能拆分
    每个服务都可以拆分为更多注册服务:提供多种方式进行注册,包括手机号注册、身份证注册、学生邮箱注册三个功能。
    登录服务:包括手机号登录、身份证登录、邮箱登录三个功能。
    信息管理服务:包括基本信息管理、课程信息管理、成绩信息管理等功能。
    安全设置服务:包括修改密码、安全手机、找回密码等功能细粒度的功能,例如:
    从零开始学架构——可扩展架构模式_第4张图片

通过学生信息管理系统的案例可以发现,不同的拆分方式,架构图差异很大。但好像无论哪种方式,最终都是可以实现的。既然如此,我们何必费尽心机去选择呢,随便挑选一个不就可以了?

当然不能随便挑,否则架构设计就没有意义了,架构师也就要丢掉饭碗了。原因在于:不同的拆分方式,本质上决定了系统的扩展方式。

可扩展方式

当我们谈可扩展性时,很多同学都会有一个疑惑:就算是不拆分系统,只要在设计和写代码时做好了,同样不会出现到处改的问题啊?例如,在面向服务拆分的案例中,增加“学号注册”,就算是不拆分为服务,也可以控制修改的范围,那为何我们要大费周章地去拆分系统呢?
在一个理想的环境,你的团队都是高手,每个程序员都很厉害,对业务都很熟悉,新来的同事很快就知晓所有的细节……那确实不拆分也没有问题。但现实却是:团队有菜鸟程序员,到底是改 A 处实现功能还是改 B 处实现功能,完全取决于他觉得哪里容易改;有的程序员比较粗心;有的程序员某天精神状态不太好;新来的同事不知道历史上某行代码为何那么“恶心”,而轻易地将其改漂亮了一些……所有的这些问题都可能出现,这时候你就会发现,合理的拆分,能够强制保证即使程序员出错,出错的范围也不会太广,影响也不会太大
下面是不同拆分方式应对扩展时的优势。

  1. 面向流程拆分
    扩展时大部分情况只需要修改某一层,少部分情况可能修改关联的两层,不会出现所有层都同时要修改
    -例如学生信息管理系统,如果我们将存储层从 MySQL 扩展为同时支持 MySQL 和 Oracle,那么只需要扩展存储层和数据层即可,展示层和业务层无须变动
  2. 面向服务拆分
    对某个服务扩展,或者要增加新的服务时,只需要扩展相关服务即可,无须修改所有的服务
    同样以学生管理系统为例,如果我们需要在注册服务中增加一种“学号注册”功能,则只需要修改“注册服务”和“登录服务”即可,“信息管理服务”和“安全设置”服务无须修改
  3. 面向功能拆分
    对某个功能扩展,或者要增加新的功能时,只需要扩展相关功能即可,无须修改所有的服务
    同样以学生管理系统为例,如果我们增加“学号注册”功能,则只需要在系统中增加一个新的功能模块,同时修改“登录功能”模块即可,其他功能都不受影响
    不同的拆分方式,将得到不同的系统架构,典型的可扩展系统架构有:
  • 面向流程拆分:分层架构
  • 面向服务拆分:SOA、微服务
  • 面向功能拆分:微内核架构

当然,这几个系统架构并不是非此即彼的,而是可以在系统架构设计中进行组合使用的。以学生管理系统为例,我们最终可以这样设计架构:
整体系统采用面向服务拆分中的“微服务”架构,拆分为“注册服务”“登录服务”“信息管理服务”“安全服务”,每个服务是一个独立运行的子系统。
其中的“注册服务”子系统本身又是采用面向流程拆分的分层架构。
“登录服务”子系统采用的是面向功能拆分的“微内核”架构。

传统可扩展架构模式:分层架构和SOA

相比于高性能、高可用架构模式在最近几十年的迅猛发展来说,可扩展架构模式的发展可以说是步履蹒跚
最近几年火热的微服务模式算是可扩展模式发展历史中为数不多的亮点
这也导致了现在谈可扩展的时候必谈微服务,甚至微服务架构都成了架构设计的银弹,高性能也用微服务、高可用也用微服务
很多时候这样的架构设计看起来高大上,实际上违背了架构设计的“合适原则”和“简单原则”
在实践中更好的进行可扩展架构设计,接下来将分别介绍几种可扩展架构模式,指出每种架构模式的关键点和优缺点。

分层架构

分层架构是很常见的架构模式,它也叫 N 层架构,通常情况下,N 至少是 2 层。例如,C/S 架构、B/S 架构。常见的是 3 层架构(例如,MVC、MVP 架构)、4 层架构,5 层架构的比较少见,一般是比较复杂的系统才会达到或者超过 5 层,比如操作系统内核架构。
按照分层架构进行设计时,根据不同的划分维度和对象,可以得到多种不同的分层架构

  • C/S 架构、B/S 架构
    划分的对象是整个业务系统,划分的维度是用户交互,即将和用户交互的部分独立为一层,支撑用户交互的后台作为另外一层。例如,下面是 C/S 架构结构图
    从零开始学架构——可扩展架构模式_第5张图片

  • MVC 架构、MVP 架构
    划分的对象是单个业务子系统,划分的维度是职责,将不同的职责划分到独立层,但各层的依赖关系比较灵活。例如,MVC 架构中各层之间是两两交互的:
    从零开始学架构——可扩展架构模式_第6张图片

  • 逻辑分层架构
    划分的对象可以是单个业务子系统,也可以是整个业务系统,划分的维度也是职责。虽然都是基于职责划分,但逻辑分层架构和 MVC 架构、MVP 架构的不同点在于,逻辑分层架构中的层是自顶向下依赖的。典型的有操作系统内核架构、TCP/IP 架构。例如,下面是 Android 操作系统架构图:
    从零开始学架构——可扩展架构模式_第7张图片
    典型的 J2EE 系统架构也是逻辑分层架构,架构图如下:
    从零开始学架构——可扩展架构模式_第8张图片
    针对整个业务系统进行逻辑分层的架构图如下:
    从零开始学架构——可扩展架构模式_第9张图片
    无论采取何种分层维度,分层架构设计最核心的一点就是需要保证各层之间的差异足够清晰,边界足够明显,让人看到架构图后就能看懂整个架构,这也是分层不能分太多层的原因。否则如果两个层的差异不明显,就会出现程序员小明认为某个功能应该放在 A 层,而程序员老王却认为同样的功能应该放在 B 层,这样会导致分层混乱。如果这样的架构进入实际开发落地,则 A 层和 B 层就会乱成一锅粥,也就失去了分层的意义。

分层架构之所以能够较好地支撑系统扩展,本质在于隔离关注点(separation of concerns),即每个层中的组件只会处理本层的逻辑。
比如说,展示层只需要处理展示逻辑,业务层中只需要处理业务逻辑,这样我们在扩展某层时,其他层是不受影响的,通过这种方式可以支撑系统在某层上快速扩展。例如,Linux 内核如果要增加一个新的文件系统,则只需要修改文件存储层即可,其他内核层无须变动。

当然,并不是简单地分层就一定能够实现隔离关注点从而支撑快速扩展,分层时要保证层与层之间的依赖是稳定的,才能真正支撑快速扩展。例如,Linux 内核为了支撑不同的文件系统格式,抽象了 VFS 文件系统接口,架构图如下
从零开始学架构——可扩展架构模式_第10张图片
如果没有VFS,只是简单地将ext2、ext3、reiser等文件系统划分为“文件系统层”,那么这个分层是达不到支撑可扩展的目的的。因为增加一个新的文件系统后,所有基于文件系统的功能都要适配新的文件系统接口;而有了VFS后,只需要VFS适配新的文件系统接口,其他基于文件系统的功能以来的是VFS,不会受到影响。

分层结构的另外一个特点就是层层传递,也就是说一旦分层确定,整个业务流程是按照层进行依次传递的,不能在层之间进行跳跃。

最简单的 C/S 结构,用户必须先使用 C 层,然后 C 层再传递到 S 层,用户是不能直接访问 S 层的。传统的 J2EE 4 层架构,收到请求后,必须按照下面的方式传递请求:
从零开始学架构——可扩展架构模式_第11张图片

分层结构的这种约束,好处在于强制将分层依赖限定为两两依赖,降低了整体系统复杂度。

举个例子
Persistence Layer如下,

public class AvatarDao {
    public static String getAvatarUrl(int userId){
        // 具体实现代码 略
        return "http://avatar.csdn.net/xxx.jpg";
    }
}

Business Layer如下,

public class AvatarBizz {
    public static String getAvatarUrl(int userId){
        return AvatarDao.getAvatarUrl(userId);
    }
}

Presentation Layer如下,

public class AvatarView {
    public void displayAvatar(int userId) {
        String url = AvatarBizz.getAvatarUrl(userId);
        // 渲染代码略
        return;
    }
}

例如,Business Layer 被 Presentation Layer 依赖,自己只依赖Persistence Layer。但分层结构的代价就是冗余,也就是说,不管这个业务有多么简单,每层都必须要参与处理,甚至可能每层都写了一个简单的包装函数。分层架构的优势就体现在通过分层强制约束两两依赖,一旦自由选择绕过分层,时间一长,架构就会变得混乱。

例如,Presentation Layer 直接访问 Persistence Layer,Business Layer 直接访问 Database Layer,这样做就失去了分层架构的意义,也导致后续扩展时无法控制受影响范围,牵一发动全身,无法支持快速扩展。
除此以外,虽然分层架构的实现在某些场景下看起来有些啰嗦和冗余,但复杂度却很低
分层架构另外一个典型的缺点就是性能,因为每一次业务请求都需要穿越所有的架构分层,有一些事情是多余的,多少都会有一些性能的浪费

这里所谓的性能缺点只是理论上的分析,实际上分层带来的性能损失,如果放到 20 世纪 80 年代,可能很明显
但到了现在,硬件和网络的性能有了质的飞越,其实分层模式理论上的这点性能损失,在实际应用中,绝大部分场景下都可以忽略不计

SOA

SOA 的全称是 Service Oriented Architecture,中文翻译为“面向服务的架构”。SOA 出现 的背景是企业内部的 IT 系统重复建设且效率低下,主要体现在:

  • 企业各部门有独立的 IT 系统,比如人力资源系统、财务系统、销售系统,这些系统可能都涉及人员管理,各 IT 系统都需要重复开发人员管理的功能。例如,某个员工离职后,需要分别到上述三个系统中删除员工的权限。
  • 各个独立的 IT 系统可能采购于不同的供应商,实现技术不同,企业自己也不太可能基于这些系统进行重构。
  • 随着业务的发展,复杂度越来越高,更多的流程和业务需要多个 IT 系统合作完成。由于各个独立的 IT 系统没有标准的实现方式(例如,人力资源系统用 Java 开发,对外提供 RPC;而财务系统用 C# 开发,对外提供 SOAP 协议),每次开发新的流程和业务,都需要协调大量的 IT 系统,同时定制开发,效率很低。

为了应对传统 IT 系统存在的问题,SOA 提出了 3 个关键概念

服务

所有业务功能都是一项服务,服务就意味着要对外提供开放的能力,当其他系统需要使用这项功能时,无须定制化开发。服务可大可小,可简单也可复杂。
例如,人力资源管理可以是一项服务,包括人员基本信息管理、请假管理、组织结构管理等功能。
而人员基本信息管理也可以作为一项独立的服务。
组织结构管理也可以作为一项独立的服务。
到底是划分为粗粒度的服务,还是划分为细粒度的服务,需要根据企业的实际情况进行判断。

ESB

ESB 的全称是 Enterprise Service Bus,中文翻译为“企业服务总线”。
从名字可以看出,ESB 参考了计算机总线的概念。
计算机中的总线将各个不同的设备连接在一起,ESB 将企业中各个不同的服务连接在一起。
因为各个独立的服务是异构的,如果没有统一的标准,则各个异构系统对外提供的接口是各式各样的。
SOA 使用 ESB 来屏蔽异构系统对外提供各种不同的接口方式,以此来达到服务间高效的互联互通。

松耦合

松耦合的目的是减少各个服务间的依赖和互相影响。
因为采用 SOA 架构后,各个服务是相互独立运行的,甚至都不清楚某个服务到底有多少对其他服务的依赖。
如果做不到松耦合,某个服务一升级,依赖它的其他服务全部故障,这样肯定是无法满足业务需求的。
实际上真正做到松耦合并没有那么容易,要做到完全后向兼容,是一项复杂的任务。

从零开始学架构——可扩展架构模式_第12张图片

SOA 架构是比较高层级的架构设计理念,一般情况下我们可以说某个企业采用了 SOA 的架构来构建 IT 系统,但不会说某个独立的系统采用了 SOA 架构
SOA 解决了传统 IT 系统重复建设和扩展效率低的问题,但其本身也引入了更多的复杂性。SOA 最广为人诟病的就是 ESB,ESB 需要实现与各种系统间的协议转换、数据转换、透明的动态路由等功能。下图就是ESB将JSON转换为Java的流程图,
从零开始学架构——可扩展架构模式_第13张图片
下图中ESB将REST协议转换为RMI和AMQP两个不同的协议:
从零开始学架构——可扩展架构模式_第14张图片
ESB 虽然功能强大,但现实中的协议有很多种,如 JMS、WS、HTTP、RPC 等,数据格式也有很多种,如 XML、JSON、二进制、HTML 等
ESB 要完成这么多协议和数据格式的互相转换,工作量和复杂度都很大,而且这种转换是需要耗费大量计算性能的。
当 ESB 承载的消息太多时,ESB 本身会成为整个系统的性能瓶颈。

你可能感兴趣的:(架构,网络)