每一个程序员都有一个架构师的梦,可理想很丰满,现实很骨感—大部程序员工作中都做着CRUD,但进取心与持续学习的心态还是要有的。掌握架构设计的相关理论是成为架构师的前提。在看了一本《从零开始学架构》后,发现架构设计是有套路的。
这本书主要阐述了架构学习的方法论
1)清楚地理解架构设计相关的概念、本质、目的,避免架构师在实践过程中把握不住重点、分不清主次,眉毛胡子一把抓,导致架构设计变形或者“四不像” 。
2)掌握通用的架构设计原则,无论是何种业务或技术,架构师在判断和选择的时候有一套方法论可以参考,避免架构设计举棋不定,或者拍脑袋式设计。
3)掌握标准的架构设计流程,即使是刚开始做架构设计的新手,也能够按照步骤一步一步设计出合适的架构,避免某些步骤缺失导致错误的架构设计。
4)深入理解已有的架构模式,做到能够根据架构特点快速挑选合适的模式完成架构设计,或者在已有的模式上进行创新,或者将已有的模式组合出新的架构;
什么是架构
架构就是软件系统的结构,是顶层设计,它明确软件系统包括哪些个体:子系统、模块和组件等;同时明确了个体运作和个体之间协作的规则。框架是面向编程或配置的半成品;组件是从技术维度上的复用;模块是从业务维度上职责的划分;系统是相互协同可运行的实体。
架构设计的目的
做任何事情只有明确了目的,才能把握方向,从而制定方案并执行,架构设计也不例外。架构设计的主要目的是为了解决软件系统复杂度带来的问题。小到管理系统大到淘宝、微信,在设计开发过程中,都需要进行架构设计。而由于软件系统之间的复杂度不同,架构设计难度也不一样,但基本流程都相似,掌握了架构设计的流程,即使简单的对内的工具平台也能玩出花儿来。
软件复杂度的来源
既然架构设计的主要目的是为了解决软件系统复杂度带来的问题,那么软件的复杂度主要是由什么引起的呢,明确了复杂度的来源,也就明确了架构设计要解决的问题。
复杂度主要包括高性能、高可用、易扩展、可伸缩、安全性和低成本六个来源,这六个问题覆盖了应用系统要解决的大部分问题。
架构设计流程
重点说一下识别复杂度与设计备选方案这两个过程。
1、识别复杂度
架构的复杂度来源虽然有六个,但主要复杂度来自于高性能、高可用和易扩展。在讨论架构设计的时候我们更多的是讨论这三点,但架构并不一定任何时候都必须满足这三个需求。根据软件系统的不同,大部分情况下只需要满足其中的一个到两个,很少能用到三个。
2、设计备选方案
解决高性能、高可用和易扩展三个复杂度来源的成熟的技术方案有很多。比如:针对高性能的缓存、负载均衡、多路复用方案;针对高可用的主备、集群、异地多活方案;提升软件扩展性的设计模式、架构分层、插件化等方案。在明确了软件系统的复杂度主因(大部分情况下,核心复杂度只有1到2个)以后就可以按图索骥的找到备选解决方案。
高性能架构方案
高性能是架构设计复杂度来源之一,任何软件系统对高性能都有所要求,只是要求的标准不一样。对性能要求越高的软件系统架构设计越复杂,反之越容易。
高性能架构主要包括存储高性能与计算(服务)高性能两个方面。
存储高性能
对存储系统进行高性能设计的方法如下图所示。
-
关系数据库的高性能设计,主要的做法是读写分离、分库分表;
-
高性能NoSQL(Not only SQL),NoSQL不应该是关系型数据库的替代者,而应该是作为补充者,因为NoSQL其实是牺牲了ACID中某一个甚至某几个特性来适应大数据场景下的性能要求,在某些高保障场景下(如金融领域)其实是不适用的;
-
缓存,主要也是为了解决的关系行数据库中无能为力解决的问题,解决思路是,将数据取出来之后放到缓存服务器,减少对关系型数据库的访问,缓存需要的注意的点是缓存穿透、缓存雪崩、缓存热点等问题;
计算(服务)高性能
高性能架构设计主要集中在两方面:尽量提升单服务器的性能,将单服务器的性能发挥到极致。如果单服务器无法支撑性能,设计服务器集群方案。除了这两点,最终系统能否实现高性能,还和具体的实现及编码相关。但架构设计是高性能的基础,如果架构设计没有做到高性能,则后面的具体实现和编码能提升的空间是有限的。形象地说,架构设计决定了系统性能的上限,实现细节决定了系统性能的下限。
计算(服务)高性能包括单机与集群两个方向,这两个方向的高性能方案分别如下图所示。
单服务器实现高性能的关键之一就是服务器采取的并发模型,并发模型有两个关键设计点:
-
服务器如何管理连接
-
服务器如何处理请求
两个设计点最终都和操作系统的I/O模型及进程模型相关。
-
I/O 模型:阻塞、非阻塞、同步、异步
-
进程模型:单进程、多进程、多线程
PPC和TPC模式的优点是实现简单,缺点是都无法支撑高并发的场景,尤其是互联网发展到现在,各种海量用户业务的出现,PPC和TPC完全无能为力。Reactor和Proactor模式是更好的应对高并发场景的单服务器模式。
集群实现高性能的复杂性主要体现在需要增加一个任务分配器(负载均衡器),以及为任务选择一个合适的任务分配算法。负载均衡系统包括 3 种:DNS 负载均衡、硬件负载均衡和软件负载均衡算法。
高可用架构方案
如果系统不可用,性能再高也没卵用。与高性能一样,高也需要有指标来量化,比如4个9。大型网站的高可用又分为应用高可用、服务高可用和数据高可用。
业务及应用高可用
业务及服务高可用的设计方法如下图所示。
业务高可用的一般做法是做异地多活,难点是异地模式会因为物理延迟产生数据不一致问题,常见的模式有:同城异地、跨城异地和跨国异地。
接口服务高可用方案主要有以下4种
-
降级:将业务或者接口功能降低,只提供部分功能或者完全停掉所有功能。常见实现降级方式有系统后门降级和独立系统降级。
-
限流:降级是故障后的事后处理,而限流是故障前的预防。限流是指只允许能够承受的访问量进来,超出系统系统访问能力的将被放弃。常用的限流方式可以分为分类基于请求限流和基于资源限流。
-
排队:是流量限制的一个变种,限流是直接拒绝用户,排队是让用户等待一段时间。
-
服务熔断:熔断与降级概念非常容易混淆,降级是为了应对服务自身的故障。熔断的目的是为了应对外部系统故障的情况:主动断开依赖的其他外部的不可用服务,防止造成更多的雪崩效应。
数据高可用
数据高可用分两部分,一是数据一致性,二是数据备份。
BASE理论本质上是对CAP的延伸和补充,更具体地说,是对CAP中AP方案的一个补充。现实的分布式系统中,想实现完美的CAP根本不可能,也不需要这么严格的CAP,BASE几乎适用于所有场景,对于一些对数据一致性要求达到100%的场景(如金融领域),可以选择CA,也就是单机。
刚性事务(如单机数据库事务)完全遵循ACID规范。柔性事务(如分布式事务)为了满足可用性、性能与降级服务的需要,降低一致性(Consistency)与隔离性(Isolation)的要求,遵循 BASE理论,传统的ACID事务对隔离性的要求非常高,在事务执行过程中,必须将所有的资源对象锁定,因此对并发事务的执行极度不友好,柔性事务(比如分布式事务)的理念则是将锁资源对象操作从本地资源对象层面上移至业务逻辑层面,再通过放宽对强一致性要求,以换取系统吞吐量的提升。
存储高可用
存储高可用方案的本质都是通过将数据复制到多个存储设备,通过数据冗余的方式来实现高可用,其复杂性主要体现在如何应对复制延迟和中断导致的数据不一致问题。解决方案大致分成双机架构和集群架构,但这两者并不是互斥的,可以相互包含,例如OceanBase作为分布式关系型数据库,总体上采用的是数据分散集群,单一数据库节点又采用了一主多从冗余的架构。
在CAP理论提出十二年之后,其作者又出来辟谣。“三选二”的公式一直存在着误导性,它会过分简单化各性质之间的相互关系。首先,由于分区很少发生,那么在系统不存在分区的情况下没什么理由牺牲C或A。其次,C与A之间的取舍可以在同一系统内以非常细小的粒度反复发生,而每一次的决策可能因为具体的操作,乃至因为牵涉到特定的数据或用户而有所不同。最后,这三种性质都可以在程度上衡量,并不是非黑即白的有或无。可用性显然是在0%到100%之间连续变化的,一致性分很多级别,连分区也可以细分为不同含义,如系统内的不同部分对于是否存在分区可以有不一样的认知。所以一致性和可用性并不是水火不容,非此即彼的。Paxos、Raft等分布式一致性算法就是在一致性和可用性之间做到了很好的平衡的见证。
OceanBase的redolog是使用分布式一致性算法Paxos实现的,每一笔事务,主库执行完成后,要同步到半数以上库(包括主库自身),例如3个库中的2个库,或者5个库中的3个库,事务才成功,这样少数库异常后业务并不受影响。所以在CAP理论中,虽然OceanBase使用的是强一致模型,但是OceanBase能在一定网络分区的情况下做到极高的可用性(通俗点讲就是多于半数的机器还活着的时候就能干活),虽然选择了C与P,但A也能做到5个9。
可扩展架构方案
这个世界唯一不变的是变,与建筑架构不同,软件系统架构是一个不断演变的过程。如果软件系统从一开始没做好软件架构,遇到每次大的改变都需要重构,将是不能接受的。
可扩展性架构设计方式有很多,但万变不离其宗,所有的可扩展架构的背后基本思想都可以总结为一个字:“拆”。可以面向流程拆分,面向服务拆分,面向功能拆分(这三种拆分方式其实也不是互斥的,而可以相互借用 )。
其中可扩展架构大致分成三种类型:分层架构与SOA、微服务、微内核。
传统的可扩展架构模式——分层与SOA
无论采取何种分层维度,分层架构设计最核心的一点就是需要保证各层之间的差异足够清晰,边界足够明显,让人看到架构图后就能看懂整个架构,这也是分层不能分太多层的原因。否则如果两个层的差异不明显,就会出现程序员小明认为某个功能应该放在A层,而程序员老王却认为同样的功能应该放在B层,这样会导致分层混乱。
分层架构之所以能够较好地支撑系统扩展,本质在于隔离关注点,即每个层中的组件只会处理本层的逻辑。分层结构的另外一个特点就是层层传递,也就是说一旦分层确定,整个业务流程是按照层进行依次传递的,不能在层之间进行跳跃。最简单的C/S结构,用户必须先使用C 层,然后C层再传递到S层,用户是不能直接访问S层的。
微服务架构
微服务与SOA都是关注服务,但是这两者服务粒度不同,架构设计也不同,SOA是为了兼容企业中存在很久的系统,而微服务是为了适应互联网系统开发。微服务需要配套的基础设施支持,这些基础设施包括自动化测试、自动化部署、配置中心、服务发现、服务路由、监控、容错、服务安全等,只有把这些基础设施都做好了才能很好的实践微服务。
微服务需要避免踩的陷阱,简单提炼为:
-
微服务拆分过细,过分强调“small”,造成服务数量众多,复杂度反而上升
-
微服务基础设施不健全,忽略了“automated”
-
微服务并不轻量级,规模大了后,“lightweight”不再适应
微内核——插件化架构
微内核是一种插件化架构,关键点是插件管理、插件连接、插件通信,常见的微内核结构:OGSI架构、规则引擎架构,微内核架构基本示意图如下。
上图中核心系统Core System功能比较稳定,不会因为业务功能扩展而不断修改,插件模块可以根据业务功能的需要不断地扩展。微内核的架构本质就是将变化部分封装在插件里面,从而达到快速灵活扩展的目的,而又不影响整体系统的稳定。微内核的核心系统设计的关键技术有:插件管理、插件连接和插件通信。常见的插件连接机制有OSGi(Eclipse 使用)、消息模式、依赖注入(Spring使用),甚至使用分布式的协议都是可以的,比如RPC或者HTTP Web的方式。
最受欢迎的开源关系型数据库MySQL就是采用插件化架构,最典型的就是存储引擎模块,插件式架构使存储引擎的加载、卸载与server层解耦。