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