架构定义:软件架构指软件系统的顶层结构。
架构是顶层设计;框架是面向编程或配置的半成品;组件是从技术维度上的复用;模块是从业务维度上职责的划分;系统是相互协同可运行的实体。
不存在"银弹"
软件设计过程中,模块、对象、组件本质上是对一定规模软件在不同粒度和层次上的“拆分”方法论,软件架构是一种对软件的“组织”方法论。一分一合,其目的是为了软件研发过程中的成本、进度、质量得到有效控制。但是,一个成功的软件设计是要适应并满足业务需求,同时不断“演化”的。设计需要根据业务的变化、技术的发展不断进行“演进”,这就决定了这是一个动态活动,出现新问题,解决新问题,没有所谓的“一招鲜”。
误区不考虑生产实际,为了架构设计而设计。
架构设计的主要目的是为了解决软件系统复杂度带来的问题。
做业务架构必须与其面向的实际应用场景相匹配,应该是综合当前团队经验、成本、资源、进度、业务所处阶段等实际情况,取得一个实际上的局部最优解。
软件系统中高性能带来的复杂度主要体现在两方面,一方面是单台计算机内部为了高性能带来的复杂度;另一方面是多台计算机集群为了高性能带来的复杂度。
单机复杂度
计算机内部复杂度最关键的地方就是操作系统。
操作系统发展到现在,如果我们要完成一个高性能的软件系统,需要考虑如多进程、多线程、进程间通信、多线程并发等技术点,而且这些技术并不是最新的就是最好的,也不是非此即彼的选择。在做架构设计的时候,需要花费很大的精力来结合业务进行分析、判断、选择、组合,这个过程同样很复杂。举一个最简单的例子:Nginx 可以用多进程也可以用多线程,JBoss 采用的是多线程;Redis 采用的是单进程,Memcache 采用的是多线程,这些系统都实现了高性能,但内部实现差异却很大。
进程间通信的各种方式包括管道、消息队列、信号量、共享存储等。
集群的复杂度
任务分配
任务分配的意思是指每台机器都可以处理完整的业务任务,不同的任务分配到不同的机器上执行。
任务分解
当各个逻辑任务分解到独立的子系统后,整个系统的性能瓶颈更加容易发现,而且发现后只需要针对有瓶颈的子系统进行性能优化或者提升,不需要改动整个系统,风险会小很多。
换而言之,可以从垂直与水平两个维度来考虑。垂直维度主要是针对单台计算机,通过升级软、硬件能力实现性能提升;水平维度则主要针对集群系统,利用合理的任务分配与任务分解实现性能的提升。
垂直维度可包括以下措施:
增大内存减少I/O操作
更换为固态硬盘(SSD)提升I/O访问速度
使用RAID增加I/O吞吐能力
置换服务器获得更多的处理器或分配更多的虚拟核
升级网络接口或增加网络接口
水平维度可包括以下措施:
功能分解:基于功能将系统分解为更小的子系统
多实例副本:同一组件重复部署到多台不同的服务器
数据分割:在每台机器上都只部署一部分数据
垂直维度方案比较适合业务阶段早期和成本可接受的阶段,该方案是提升性能最简单直接的方式,但是受成本与硬件能力天花板的限制。
水平维度方案所带来的好处要在业务发展的后期才能体现出来。起初,该方案会花费更多的硬件成本,另外一方面对技术团队也提出了更高的要求;但是,没有垂直方案的天花板问题。一旦达到一定的业务阶段,水平维度是技术发展的必由之路。因此,作为技术部门,需要提前布局 ,未雨绸缪,不要被业务抛的太远。
高可用的定义:系统无中断地执行其功能的能力,代表系统的可用性程度,是进行系统设计时的准则之一。
系统的高可用方案五花八门,但万变不离其宗,本质上都是通过“冗余”来实现高可用。
系统不可用的原因:
硬件故障。网站多运行在普通的商用服务器,而这些服务器本身就不具备高可用性,再加之网站系统背后有数量众多服务器,那么一定时间内服务器宕机是大概率事件,直接导致部署在该服务器上的服务受影响。
软件BUG或网站更新升级发布。BUG不能消灭,只能减少;上线后的系统在运行过程中,难免会出现故障,而这些故障同样直接导致某些网站服务不可用;此外,网站更新升级发布也会引起相对较频繁的服务器宕机。
不可抗拒力。如地震、水灾、战争等。
如何做到高可用:
核心思想:网站高可用的主要技术手段是服务与数据的冗余备份与故障转移。同一服务组件部署在多台服务器上;数据存储在多台服务器上互相备份。通过上述技术手段,当任何一台服务器宕机或出现各种不可预期的问题时,就将相应的服务切换到其他可用的服务器上,不影响系统的整体可用性,也不会导致数据丢失。
从架构角度看可用性:当前网站系统多采用经典的分层模型,从上到下为:应用层、服务层与数据层。应用层主要实现业务逻辑处理;服务层提供可复用的服务;数据层负责数据读写;在部署架构上常采用应用和数据分离部署,应用会部署到不同服务器上,这些服务器被称为应用层的服务器;这些可复用的服务也会各自部署在不同服务器上,称为服务层的服务器;而各类数据库系统、文件柜等数据则部署在数据层的服务器。
硬件故障方面引起不可用的技术解决措施:(1)应用服务器。可通过负载均衡设备将多个应用服务器构建为集群对外提供服务(前提是这些服务需要设计为无状态,即应用服务器不保存业务的上下文信息,而仅根据每次请求提交的数据进行业务逻辑的操作响应),当均衡设备通过心跳检测手段检测到应用服务器不可用时,则将其从集群中移除,并将请求切换到其他可用的应用服务上。(2)服务层服务器。这些服务器被应用层通过分布式服务框架(如Dubbo)访问,分布式服务框架可在应用层客户端程序中实现软件负载均衡,并通过服务注册中心提供服务的服务器进行心跳检测,当发现有服务器不可用时,立即通知客户端程序修改服务列表,同时移除响应的服务器。(3)数据服务器。需要在数据写入时进行数据同步复制,将数据写入多台服务器上,实现数据冗余备份;当数据服务器宕机时,应用程序将访问切换到有备份数据的服务器上。
软件方面引起不可用的技术解决措施:通过软件开发过程进行质量保证。通过预发布验证、严格测试、灰度发布等手段,尽量减少上线服务的故障。
可扩展性指系统为了应对将来需求变化而提供的一种扩展能力,当有新的需求出现时,系统不需要或者仅需要少量修改就可以支持,无须整个系统重构或者重建。
设计具备良好可扩展性的系统,有两个基本条件:正确预测变化、完美封装变化。换而言之,有两个思考角度:(1)从业务维度。对业务深入理解,对可预计的业务变化进行预测。(2)从技术维度。利用扩展性好的技术,实现对变化的封装。
预测变化
预测变化的复杂性在于:
应对变化
第一种应对变化的常见方案是将“变化”封装在一个“变化层”,将不变的部分封装在一个独立的“稳定层”。
第二种常见的应对变化的方案是提炼出一个“抽象层”和一个“实现层”。抽象层是稳定的,实现层可以根据具体业务需要定制开发,当加入新的功能时,只需要增加新的实现,无须修改抽象层。
在实际软件系统架构设计中,常通过以下技术手段实现良好的可扩展性:(1)使用分布式服务(框架)构建可复用的业务平台。(2)使用分布式消息队列降低业务模块间的耦合性。
低成本
What:低成本是架构设计中需要考虑一个约束条件,但不会是首要目标。低成本本质上是与高性能和高可用冲突的,当无法设计出满足成本要求的方案,就只能协调并调整成本目标。
How:一般通过“创新”达到低成本的目标。(1)引入新技术。主要复杂度在于需要去熟悉新技术,并且将新技术与已有技术结合;一般中小型公司基本采用该方式达到目标。(2)开创一个全新技术领域。主要复杂度在于需要去创造全新的理念和技术,并且与旧技术相比,需要有质的飞跃,复杂度更高;一般大公司拥有更多的资源、技术实力会采用该方式来达到低成本的目标。
安全
What:安全是一个庞大而又复杂的技术领域,一旦出问题,对业务和企业形象影响非常大。从技术的角度来讲,包括(1)功能安全-“防小偷”,减少系统潜在的缺陷,阻止黑客破坏行为;(2)架构安全—“防强盗”,保护系统不受恶意访问和攻击,保护系统的重要数据不被窃取。由于是蓄意破坏系统,因此对影响也大得多。架构设计时需要特别关注架构安全。
How:(1)功能安全。是一个逐步完善的过程,而且往往都是在问题出现后才能有针对性的提出解决方案,与编码实现有关。(2)架构安全。传统企业主要通过防火墙实现不同区域的访问控制,功能强大、性能一般,但是成本更高。互联网企业更多地是依靠运营商或者云服务商强大的带宽和流量清洗的能力,较少自己来设计和实现。
规模
What:规模带来复杂度的主要原因就是“量变引起质变”,当数量超过一定的阈值后,复杂度会发生质的变化。随着业务的发展,规模带来的常见复杂度有(1)业务功能越来越多,调用逻辑越来越复杂;(2)数据容量、类型、关联关系越来越多。
How:规模问题需要与高性能、高可用、高扩展、高伸缩性统一考虑。常采用“分而治之,各个击破”的方法策略。
架构设计的几个共性原则:合适原则、简单原则、演化原则。
合适原则宣言:“合适优于业界领先”。
简单原则宣言:“简单优于复杂”。
演化原则宣言:“演化优于一步到位”。
架构设计由需求所驱动,本质目的是为了解决软件系统的复杂性;为此,我们在进行架构设计时,需要以理解需求为前提,首要进行系统复杂性的分析。具体做法是:
(1)构建复杂度的来源清单——高性能、可用性、扩展性、安全、低成本、规模等。
(2)结合需求、技术、团队、资源等对上述复杂度逐一分析是否需要?是否关键?
“高性能”主要从软件系统未来的TPS、响应时间、服务器资源利用率等客观指标,也可以从用户的主观感受方面去考虑。
“可用性”主要从服务不中断等质量属性,符合行业政策、国家法规等方面去考虑。
“扩展性”则主要从功能需求的未来变更幅度等方面去考虑。
(3)按照上述的分析结论,得到复杂度按照优先级的排序清单,越是排在前面的复杂度,就越关键,就越优先解决。
需要特别注意的是:随着所处的业务阶段不同、外部的技术条件和环境的不同,得到的复杂度问题的优先级排序就会有所不同。一切皆变化。
经过架构设计流程第 1 步——识别复杂度,确定了系统面临的主要复杂度问题,进而明确了设计方案的目标,就可以开展架构设计流程第 2 步——设计备选方案。架构设计备选方案的工作更多的是从需求、团队、技术、资源等综合情况出发,对主流、成熟的架构模式进行选择、组合、调整、创新。
(1)设计最优秀的方案。不要面向“简历”进行架构设计,而是要根据“合适”、“简单”、“演进”的架构设计原则,决策出与需求、团队、技术能力相匹配的合适方案。
(2)只做一个方案。一个方案容易陷入思考问题片面、自我坚持的认知陷阱。
(1)备选方案不要过于详细。备选阶段解决的是技术选型问题,而不是技术细节。
(2)备选方案的数量以 3~5个为最佳。
(3)备选方案的技术差异要明显。
(4)备选方案不要只局限于已经熟悉的技术。
评估和选择备选方案的操作方式为:列出我们需要关注的质量属性点,然后分别从这些质量属性的维度去评估每个方案,再综合挑选适合当时情况的最优方案。
常见的方案质量属性点有:性能、可用性、硬件成本、项目投入、复杂度、安全性、可扩展性等。在评估这些质量属性时,需要遵循架构设计原则 1“合适原则”和原则 2“简单原则”,避免贪大求全,基本上某个质量属性能够满足一定时期内业务发展就可以了。