在软件行业,对于什么是架构一直有很多的争论,每个人都有自己的理解。不同的书籍上、不同的作者,对于架构的定义也不统一,角度不同,定义不同。此君说的架构和彼君理解的架构未必是一回事。
因此我们在讨论架构之前,我们先讨论架构的概念定义,因为概念是人认识这个世界的基础和用来沟通的手段,如果对架构概念理解不一样,那沟通起来自然不顺畅。
系统:泛指由一群有关联的个体组成,根据某种规则运作,能完成个别元件不能独立完成的工作能力的群体。提到系统,我们务必了解以下几个关键词:
关联:系统是由一群有关联的个体组成的,没有关联的个体堆在一起不能成为一个系统。例如,把一个发动机和一台PC放在一起不能称之为一个系统,把发动机、底盘、轮胎、车架组合起来才能成为一台汽车。
规则:系统内的个体需要按照指定的规则运作,而不是单个个个体各自为政。规则规定了系统内个体分工和协作的方式。例如,汽车发动机负责产生动力,然后通过变速器和传动轴,将动力输出到车轮上,从而驱动汽车前进。
能力:系统能力与个体能力有本质的差别,系统能力不是个体能力之和,而是产生了新的能力。例如,汽车能够载重前进,而发动机、变速器、传动轴、车轮本身都不具备这样的能力。
子系统:也是由一群关联的个体组成的系统,多半是在更大的系统中的一部分。
它们都是系统的组成部分,从不同角度拆分系统而已。模块是逻辑单元,组件是物理单元。
模块就是从逻辑上将系统分解,即分而治之,将复杂问题简单化。模块的粒度可大可小,可以是系统、几个子系统、某个服务、函数、类、方法、功能块等等。划分模块的主要目的是职责分离。
组件可以包括应用服务、数据库、网络、物理机,还可以包括MQ、容器、Nginx等技术组件。划分组件的主要目的是单元复用。“组件”的英文单词Component,对应中文的“零件”一词,“零件”更容易理解一些。“零件”是一个物理的概念,并且具备“独立且可替换”的特点。现在越来越被的UI设计使用组件化和模块化。
框架通常指的是为了实现某个业界标准或完成特定基本任务的软件组件规范,也指为了实现某个软件组件规范时,提供规范所要求之基础功能的软件产品。
框架是组件实现的规范,例如:MVC、MVP、MVVM等,是提供基础功能的产品,例如开源框架:RubyonRails、Spring、Laravel、Django等,这是可以拿来直接使用或者在此基础上二次开发。
再例如,SpringMVC是MVC的开发框架,除了满足MVC的规范,Spring提供了很多基础功能来帮助我们实现功能,包括注解(@Controller等)、SpringSecurity、SpringJPA等很多基础功能。
框架是规范,架构是结构。
在TOGAF9是这么定义:一个系统基本的构件(子系统,模块,组件),体现在它的各个构件、构件间的相互关系、构件与环境间的关系,以及对系统设计和演进进行治理的原则中。
两种含义:
第一,一个系统的形式化描述,或指导系统实现的构件级的详细计划。
架构从字面意思上,是源于古代的建筑术语。
把架构拆分成两个字“架”和“构”。“架”就是“加”和“木”的结合,把木头加起来、连接起来就是架。“构”就是结构的意思。所以,“架构”就是把“木”按照一定的结构连接起来。
对应到软件架构,“木”代表构件(要素),“结构”代表架构的产物:木就是系统中的要素,我们将它们称之为架构构件(要素)。架构要素可以是子系统、模块、应用服务、组件。结构,是架构的产物。不同的软件系统会有不同的结构,这些结构是为解决不同场景而设计的。
连接,通过定义架构元素之间的接口和交互关系、集成机制,实现架构元素之间的连接。连接可以是分布式调用、进程间调用、组件之间的交互关系等。总结一下架构的组成=要素+结构+连接,将系统要素按照特定结构进行连接交互。
我在这重新定义架构(见仁见智):
软件架构指软件系统顶层结构设计。
架构是经过系统性地思考,权衡利弊之后在现有资源约束下的最合理决策,最终明确的系统骨架:包括子系统、模块、组件,以及他们之间协作关系、约束规范、指导原则,并由它来指导系统各方面的设计和指导团队中的每个人思想层面上的一致。
涉及四方面:
系统性思考的合理决策:比如技术选型、解决实施方案(包括执行目标计划)、成本评估、性价比评估等等。
结构:明确的系统骨架(结构):明确系统有哪些构件组成。
连接:系统协作关系:各个组成部分如何协作来实现业务请求。
规范:约束规范和指导原则:保证系统有序,高效、稳定运行,包括规范、原则、流程等内容。
如果没有架构设计,说明你的系统不够复杂。随着业务的增长,系统由单体应用渐进演化为分布式和微服务化。
系统整体的复杂性越来越高,技术团队可能从一个团队变成多个专业化团队。假如没有架构设计,系统定会是一个无序失控的状态,带来的问题:
当然,我们还能列举出更多问题。
架构设计的目的是为了解决系统复杂性带来的问题。其本质就是对系统进行有序化地重构以致符合当前业务的发展,并可以快速扩展。
从上面架构的定义,也知道架构设计的作用涉及四方面:系统性思考的合理决策;明确的系统骨架;系统协作关系;约束规范和指导原则。
无论是何种变化,架构师通过理解业务、全局把控,权衡业务需求和技术实现。选择合适技术,解决关键问题并指导研发落地实施,最终促进业务发展,提高效率。
在EA架构领域,有两种常见架构方法RUP和TOGAF,这两个框架也是我们常常了解架构分类的两个维度。
TOGAF9对架构的分类是这样的:
由于不同架构方法论,定义的架构分类也不同,RUP4+1架构方法主要是以架构生命周期为视角进行描述,而TOGAF9按架构涉及内容维度来描述。
因此我结合两者细分为业务架构、应用架构、数据架构、技术架构、代码架构、部署架构。
业务架构是战略,应用架构是战术,技术架构是装备。其中应用架构承上启下,一方面承接业务架构的落地,另一方面影响技术选型。熟悉业务,形成业务架构;根据业务架构作出相应的应用架构,最后技术架构落地实施。
你也可以理解成:业务架构是生产力,应用架构是生产关系,技术架构是生产工具。业务架构决定应用架构,应用架构需要适配业务架构,并随着业务架构不断进化,同时应用架构依托技术架构最终落地。
整体来说,架构演进路程是这样的:
单体应用->分布式应用服务化->微服务
下面我将做简单介绍。
企业一开始业务比较简单,只应用某个简单场景,应用服务支持数据增删改查和简单的逻辑即可,单体应用可以满足要求。
典型的三级架构:前端(Web/手机端)+中间业务逻辑层+数据库层。这是一种典型的JavaSpringMVC或者PythonDjango框架的应用。针对单体应用,非功能性需求的做法:
性能需求:使用缓存改善性能;
并发需求:使用集群改善并发;
读写分离:数据库的读写分离;
使用反向代理和cdn加速;
使用分布式文件和分布式数据库。
单体架构的应用比较容易部署、测试,在项目的初期,单体应用可以很好地运行。然而,随着需求的不断增加,越来越多的人加入开发团队,代码库也在飞速地膨胀。
慢慢地,单体应用变得越来越臃肿,可维护性、灵活性逐渐降低,维护成本越来越高。总体来说,单体架构应用可能会有这些缺点:复杂性高、技术债务积累、部署频率低、可靠性差、扩展能力受限并且可能阻碍技术创新。
为了解决上面提到的这些缺点,我们需要对系统按照业务功能模块拆分,将各个模块服务化,变成一个分布式系统。
业务模块分别部署在不同的服务器上,各个业务模块之间通过接口进行数据交互。该架构相对于单体架构来说,这种架构提供了负载均衡的能力,大大提高了系统负载能力,解决了网站高并发的需求。另外还有以下特点:
随着业务模式越来越复杂,订单、商品、库存、价格等各个模块都很深入,比如价格区分会员等级,访问渠道(app还是PC),销售方式(团购还是普通)等,还有大量的价格促销。
这些规则很复杂,容易相互冲突。我们需要把分散到各个业务的价格逻辑进行统一管理,以基础价格服务的方式透明地提供给上层应用,变成一个微内核的服务化架构,即微服务。
微服务的特点明显:易于开发与维护、单个微服务启动较快、局部修改容易部署、技术栈也不受限制。
然而,微服务虽然有很多吸引人的地方,但它并不是免费的午餐,使用它可能面临这些挑战:
广泛地应用在从数据中心设计到应用服务的部署:
在发生故障时,系统至少要有一个冗余的实例。
必须确保一个为自己,一个为客户、一个为失败。
灾难性的事故,例如损坏客户数据,往往在部署后好几天才出现。
系统最好按照预先的设计,通过发布或回滚解决问题。
通过版本化方式实现回滚设计,一旦发生灾难级别的故障可以通过回滚到最近版本来恢复服务。
降级开关通过配置中心集中化管理,例如:apollo配置中心,通过推送机制把开关推送到各个应用服务。
通过监控发现系统的可用性问题。
通过监控使系统自我诊断、自我修复成为可能。
通过监控确定系统可预留空间的使用情况。
通过监控掌握系统之间的交互关系,发现瓶颈。
数据是否全部集中在一个数据中心?
读写是否分离?
是否所有的客户信息都共享同一个数据结构?
服务调用是否允许延时的存在?
最好争取在多数人采用该技术的时候进入,先把新技术用在对可用性要求不高的功能上,一旦证明它可以可靠地处理日常的交易,再将此技术移植到关键任务领域中去。
避免单一业务占用全部资源,避免业务之间的相互影响。机房隔离避免单点故障。两个重要原则:
不共享原则:理想情况是负载均衡、网络前端、应用服务器、数据库,绝不共享任何服务、硬件和软件。
不跨区原则:不同隔离区之间无通讯,所有服务调用必须发生在同一个故障隔离区。
什么是水平可扩展?平台的水平扩展是指随着业务的发展,当需要扩大平台的服务能力时,不必重构软件系统,通过增加新的设备来满足业务增长的需要。
系统研发要投入资源,系统维护更要长期投入。
影响核心产品到市场的速度。
如果可以形成差异化的竞争优势,那么自己做,否则外购。
在大多数情况下,便宜的是最好的。标准、低成本、可互换、易于商品化是商品化硬件的特征。
如果架构设计得好,就可以通过购买最便宜的服务器轻松地实现水平扩展,前提是所有商品化硬件的总成本要低过高端硬件的总成本。
这里有三个要点:
小构建:小构建的成本较低,可以确保投资可以产生价值。
快试错:可依市场反馈,快速迭代,加快TTM,优化用户体验。
快速迭代需要完善的运维工具,比如从cmdb、持续集成工具、监控等等。
同步系统中个别子系统出现故障会对整个系统带来影响。这里常有2个现象:
同步系统中性能最慢的子系统,成为整个系统性能的瓶颈。
同步系统中扩展性最差的子系统,是整个系统扩展的瓶颈。
无状态定义:是应用服务运行的实例不会在本地存储需要持久化的数据,并且多个实例对于同一个请求响应的结果是完全一致的。比如单实例的mysql,zookeeper集群是有状态,而类似单纯tomcat服务是无状态的。
无状态的系统更利于扩展,更利于做负载均衡。状态是系统的吞吐量、易用性、可用性、性能和可扩展性的大敌,要尽最大可能避免。
Now:目前正使用系统的架构、设计、能力、性能和扩展性。
Now+1:下一代预研系统的架构、设计、能力、性能和扩展性。
Now+2:下一代规划系统的架构、设计、能力、性能和扩展性。
设计和构建自动化的过程。如果机器可以做,就不要依赖于人。人常犯错误,更令人沮丧的是,他们往往会以不同的方式多次犯同样的错误。
架构得再好,最终还是需要代码来落地,并且组织越大这个落地的难度越大。不单单是系统架构,每个解决方案每个项目也由自己的架构,如分层、设计模式等。
如果每一块砖瓦不够坚固,那么整个系统还是会由崩塌的风险。所谓“千里之堤,溃于蚁穴”。
架构不是“空中楼阁”,最终还是要落地的,但是架构师完全不去深入到第一线怎么知道“地”在哪、怎么才能落得稳稳当当?
世上没有最好架构,只有最合适的架构,不要企图一步到位。我们需要的不是一下子造出一辆汽车,而是从单轮车-->自行车-->摩托车,最后再到汽车。
你想象一下2年后才能造出的产品,当初市场还存在吗?
在创业公司初期,业务场景和需求边界很难把握,产品需要快速迭代和变现,需求频繁更新,这个时候需要的是快速实现。不要过多考虑未来的扩展,说不定功能做完,效果不好就无用了。
如果业务模式和应用场景边界都已经比较清晰,是应该适当的考虑未来的扩展性设计。
由于大公司巨大成功的光环效应,再加上从大公司挖来的技术高手的影响,网站在讨论架构决策时,最有说服力的一句话就成了“xx就是这么搞的”。
大公司的经验和成功模式固然重要,值得学习借鉴。但如果因此而变得盲从,就失去了坚持自我的勇气,在架构演化的道路上迟早会迷路。
技术是为业务而存在的,除此毫无意义。在技术选型和架构设计中,脱离网站业务发展的实际,一味追求时髦的新技术,可能会将技术发展引入崎岖小道,架构之路越走越难。