如何成为一个架构师?

做一名系统架构师需要具备哪些条件?应该如何去学习?
关注者
1,378
被浏览
365,292
登录后你可以
不限量看优质回答私信答主深度交流精彩内容一键收藏

要成为一个架构师,需要什么呢?官网君来解答啦~

如何带领团队“攻城略地”?优秀的架构师这样做

阿里妹导读:架构师是一个既能掌控整体又能洞悉局部瓶颈并依据具体的业务场景给出解决方案的团队领导型人物。看似完美的“人格模型”背后,是艰辛的探索。今天,阿里巴巴技术专家九摩将多年经验,进行系统性地总结,帮助更多架构师在进阶这条路上走得更“顺畅”,姿态更“优雅”。

架构师职责

架构师不是一个人,他需要建立高效卓越的体系,带领团队去攻城略地,在规定的时间内完成项目。

架构师需要能够识别定义并确认需求,能够进行系统分解形成整体架构,能够正确地技术选型,能够制定技术规格说明并有效推动实施落地。

按 TOGAF 的定义,架构师的职责是了解并关注实际上关系重大但未变得过载的一些关键细节和界面,架构师的角色有:理解并解析需求,创建有用的模型,确认、细化并扩展模型,管理架构。

从业界来看对于架构师的理解可以大概区分为:

  • 企业架构师:专注于企业总体 IT 架构的设计。
  • IT 架构师-软件产品架构师:专注于软件产品的研发。
  • IT 架构师-应用架构师:专注于结合企业需求,定制化 IT 解决方案;大部分需要交付的工作包括总体架构、应用架构、数据架构,甚至部署架构。
  • IT 架构师-技术架构师:专注于基础设施,某种软硬件体系,甚至云平台,提交:产品建议、产品选型、部署架构、网络方案,甚至数据中心建设方案等。

阿里内部没有在职位 title 上专门设置架构师了,架构师更多是以角色而存在,现在还留下可见的 title 有两个:首席架构师和解决方案架构师,其中解决方案架构师目前在大部分 BU 都有设置,特别是在阿里云和电商体系。

解决方案架构师

工作方式理解

  • 了解和挖掘客户痛点,项目定义,现有环境管理;
  • 梳理明确高阶需求和非功能性需求;
  • 客户有什么资产,星环(阿里电商操作系统)/阿里云等有什么解决方案;
  • 沟通,方案建议,多次迭代,交付总体架构;
  • 架构决策。

职责

1.从客户视图来看:

  • 坚定客户高层信心:利用架构和解决方案能力,帮忙客户选择星环/阿里云平台的信心。
  • 解决客户中层问题:利用星环/阿里云平台服务+结合应用架构设计/解决方案能力,帮忙客户解决业务问题,获得业务价值。
  • 引领客户 IT 员工和阿里生态同学:技术引领、方法引领、产品引领。

2.从项目视图看:

  • 对接管理部门:汇报技术方案,进度;技术沟通。
  • 对接客户 PM,项目 PM:协助项目计划,人员管理等。负责所有技术交付物的指导。
  • 对接业务部门和需求人员:了解和挖掘痛点,帮忙梳理高级业务需求,指导需求工艺。
  • 对接开发:产品支持、技术指导、架构指导。
  • 对接测试:配合测试计划和工艺制定。配合性能测试或者非功能性测试。
  • 对接运维:产品支持,运维支持。
  • 对接配置&环境:产品支持。
  • 其他:阿里技术资源聚合。

3.从阿里内部看:

  • 销售方案支持;
  • 市场宣贯;
  • 客户需求Facade;
  • 解决方案沉淀。

架构师职责明确了,那么有什么架构思维可以指导架构设计呢?请看下述的架构思维。

架构思维

自顶向下构建架构

要点主要如下:

1.首先定义问题,而定义问题中最重要的是定义客户的问题。定义问题,特别是识别出关键问题,关键问题是对客户有体感,能够解决客户痛点,通过一定的数据化来衡量识别出来,关键问题要优先给出解决方案。

2.问题定义务必加入时间维度,把手段/方案和问题定义区分开来。

3.问题定义中,需要对问题进行升层思考后再进行升维思考,从而真正抓到问题的本质,理清和挖掘清楚需求;要善用第一性原理思维进行分析思考问题。

4.问题解决原则:先解决客户的问题(使命),然后才能解决自己的问题(愿景);务必记住不是强调我们怎么样,而是我们能为客户具体解决什么问题,然后才是我们变成什么,从而怎么样去更好得服务客户。

5.善用多种方法对客户问题进行分析,转换成我们产品或者平台需要提供的能力,比如仓储系统 WMS 可以提供哪些商业能力。

6.对我们的现有的流程和能力模型进行梳理,找到需要提升的地方,升层思考和升维思考真正明确提升部分。

7.定义指标,并能够对指标进行拆解,然后进行数学建模。

8.将抽象出来的能力诉求转换成技术挑战,此步对于技术人员来说相当于找到了靶子,可以进行方案的设计了,需要结合自底向上的架构推导方式。

9.创新可以是业务创新,也可以是产品创新,也可以是技术创新,也可以是运营创新,升层思考、升维思考,使用第一性原理思维、生物学(进化论--进化=变异+选择+隔离、熵增定律、分形和涌现)思维等哲科思维可以帮助我们在业务,产品,技术上发现不同的创新可能。可以说哲科思维是架构师的灵魂思维。

自底向上推导应用架构

先根据业务流程,分解出系统时序图,根据时序图开始对模块进行归纳,从而得到粒度更大的模块,模块的组合/聚合构建整个系统架构。

基本上应用逻辑架构的推导有4个子路径,他们分别是:

  1. 业务概念架构:业务概念架构来自于业务概念模型和业务流程;
  2. 系统模型:来自于业务概念模型;
  3. 系统流程:来自业务流程;
  4. 非功能性的系统支撑:来自对性能、稳定性、成本的需要。

效率、稳定性、性能是最影响逻辑架构落地成物理架构的三大主要因素,所以从逻辑架构到物理架构,一定需要先对效率、稳定性和性能做出明确的量化要求。

自底向上重度依赖于演绎和归纳。

如果是产品方案已经明确,程序员需要理解这个业务需求,并根据产品方案推导出架构,此时一般使用自底向上的方法,而领域建模就是这种自底向上的分析方法。

对于自底向上的分析方法,如果提炼一下关键词,会得到如下两个关键词:

1.演绎:演绎就是逻辑推导,越是底层的,越需要演绎:

  • 从用例到业务模型就属于演绎;
  • 从业务模型到系统模型也属于演绎;
  • 根据目前的问题,推导出要实施某种稳定性措施,这是也是演绎。

2.归纳:这里的归纳是根据事物的某个维度来进行归类,越是高层的,越需要归纳:

  • 问题空间模块划分属于归纳;
  • 逻辑架构中有部分也属于归纳;
  • 根据一堆稳定性问题,归纳出,事前,事中,事后都需要做对应的操作,是就是根据时间维度来进行归纳。

领域驱动设计架构

大部分传统架构都是基于领域模型分析架构,典型的领域实现模型设计可以参考DDD(领域驱动设计),详细可以参考《实现领域驱动设计》这本书,另外《UML和模式应用》在领域建模实操方面比较好,前者偏理论了解,后者便于落地实践。

领域划分设计步骤:

1.对用户需求场景分析,识别出业务全维度 Use Case;

2.分析模型鲁棒图,识别出业务场景中所有的实体对象。鲁棒图 —— 是需求设计过程中使用的一种方法(鲁棒性分析),通过鲁棒分析法可以让设计人员更清晰,更全面地了解需求。它通常使用在需求分析后及需求设计前做软件架构分析之用,它主要注重于功能需求的设计分析工作。需求规格说明书为其输入信息,设计模型为其输出信息。它是从功能需求向设计方案过渡的第一步,重点是识别组成软件系统的高级职责模块、规划模块之间的关系。鲁棒图包含三种图形:边界、控制、实体,三个图形如下:

3、领域划分,将所有识别出的实体对象进行分类;

4、评估域划分合理性,并进行优化。

基于数据驱动设计架构

随着 IoT、大数据和人工智能的发展,以领域驱动的方式进行架构往往满足不了需求或者达不到预期的效果,在大数据时代,在大数据应用场景,我们需要转变思维,从领域分析升维到基于大数据统计分析结果来进行业务架构、应用架构、数据架构和技术架构。这里需要架构师具备数理统计分析的基础和 BI 的能力,以数据思维来架构系统,典型的系统像阿里的数据分析平台采云间和菜鸟的数据分析平台 FBI。

上述四种思维,往往在架构设计中是融合使用的,需要根据业务或者系统的需求来选择侧重思维方式。

有了架构思维的指导,具体有没有通用/标准化的架构框架以更好的执行架构设计?请看常见的架构框架。下述的架构框架其实本身也包含了重要的一些架构思维。

常见架构框架

TOGAF

TOGAF 是 The Open Group Architecture Framework 的缩写,它由 The Open Group 开发,The Open Group 是一个非盈利的技术行业联盟,它不断更新和重申 TOGAF。

TOGAF 强调商业目标作为架构的驱动力,并提供了一个最佳实践的储藏库,其中包括 TOGAF 架构开发方法(ADM)、TOGAF 架构内容框架、TOGAF 参考模型、架构开发方法(ADM)指引和技术、企业连续统一体和 TOGAF 能力框架。

ADM

ADM是一个迭代的步骤顺序以发展企业范围的架构的方法。

架构内容框架

参考模型

ADM指引和技术

1、架构迭代阶段:

2、在不同水平运用ADM:

3、利益相关者分类:

企业连续统一体

架构指导及支持解决方案:基础 通用系统 行业组织特定

能力框架

(更多内容可以参考《TOGAF标准9.1版本》或者 https://www.opengroup.org/togaf

Zachman

第一个最有影响力的框架方法论就是 Zachman 框架,它是 John Zachman 首次在1987年提出的。

Zachman 框架模型分两个维度:横向维度采用6W(what、how、where、who、when、why)进行组织,纵向维度反映了 IT 架构层次,从上到下(Top-Down),分别为范围模型、企业模型、系统模型、技术模型、详细模型、功能模型。横向结合6W,Zachman 框架分别由数据、功能、网络、人员、时间、动机分别对应回答What、How、Where、Who、When 与 Why 这六个问题。

ITSA

ITSA诞生于1986年的惠普,世界最早的企业架构框架(IT战略与架构)。建模原则就是“Everything you need, and nothing you don’t”,只放你要的东西。

DODAF

DODAF 是美国国防部架构框架,是一个控制“EA开发、维护和决策生成”的组织机制,是统一组织“团队资源、描述和控制EA活动”的总体结构。

DODAF 涵盖 DoD 的所有业务领域,定义了表示、描述、集成 DoD 范围内众多架构的标准方法,确保架构描述可比较、评估,提供了对 FoS (系统族)和 SoS (体系)进行理解、比较、集成和互操作共同的架构基础,提供开发和表达架构描述的规则和指南,但不指导如何实现。

DODAF 核心是8个视点和52个模型。

1.全景视点 AV

与所有视点相关的体系结构描述的顶层概貌。提供有关体系结构描述的总体信息,诸如体系结构描述的范围和背景。范围包括体系结构描述的专业领域和时间框架。背景由构成体系结构描述背景的相互关联各种条件组成,包括条令,战术、技术和程序,相关目标和构想的描述,作战概念(CONOPS),想定和环境条件。

2.能力视点CV

能力视点(CV)集中反映了与整体愿景相关的组织目标,这些愿景指在特定标准和条件下进行特定行动过程或是达成期望效果的能力,它们综合使用各种手段和方式来完成一组任务。

CV 为体系结构描述中阐述的能力提供了战略背景和相应的高层范围,比作战概念图中定义的基于想定的范围更全面。

这些模型是高层的,用决策者易于理解的术语来描述能力,以便沟通能力演进方面战略构想。

3.作战视点OV

作战视点(OV)集中反映了完成 DoD 使命的机构、任务或执行的行动以及彼此间必须交换的信息。描述信息交换的种类、频度、性质,信息交换支持哪些任务和活动。

4.服务视点 SvcV

服务视点(SvcV)集中反映了为作战行动提供支撑的系统、服务和相互交织的功能。DoD 流程包括作战、业务、情报和基础设施功能。SvcV 功能和服务资源及要素可以链接到 0V 中的体系结构数据。这些系统功能和服务资源支撑作战行动,促进信息交换。

5.系统视点 SV

系统视点(SV)集中反映支持作战行动中的自动化系统、相互交联和其他系统功能的信息。随着对面向服务环境和云计算的重视,在 DoDAF 的未来版本中也许不会有系统视点。

6.数信视点 DIV

数据和信息视点(DIV),简称数信视点,反映了体系结构描述中的业务信息需求和结构化的业务流程规则。

描述体系结构描述中与信息交换相关的信息,诸如属性、特征和相互关系。
必要时,本视点模型中用到的数据需要由多个架构团队来共同考虑。

7.标准视点 StdV

标准视点(StdV)是用来管控系统各组成部分或要素的编排、交互和相互依赖的规则的最小集。其目的是确保系统能满足特定的一组操作需求。

标准视点提供技术系统的实施指南,以工程规范为基础,确立通用的积木块,开发产品线。

包括一系列技术标准、执行惯例、标准选项、规则和规范,这些标准在特定体系结构描述中可以组成管控系统和系统/服务要素的文件(profile)。

8.项目视点 PV

项目视点(PV)集中反映了项目是如何有机地组织成一个釆办项目的有序组合。
描述多个采办项目之间关联关系,每个采办项目都负责交付特定系统或能力。

TOGAF,Zachman,ITSA 和 DODAF 是非常不错的架构框架,尤其前两者应用很广泛,TOGAF 还有专门的架构认证。当我们掌握了这些框架,我们是不是需要一些架构原则来指导更具体的设计?请看下文。

架构原则

设计原则就是架构设计的指导思想,它指导我们如何将数据和函数组织成类,如何将类链接起来成为组件和程序。反向来说,架构的主要工作就是将软件拆解为组件,设计原则指导我们如何拆解、拆解的粒度、组件间依赖的方向、组件解耦的方式等。

设计原则有很多,我们进行架构设计的主导原则是 OCP(开闭原则),在类和代码的层级上有:SRP(单一职责原则)、LSP(里氏替换原则)、ISP(接口隔离原则)、DIP(依赖反转原则);在组件的层级上有:REP(复用、发布等同原则)、CCP(共同闭包原则)、CRP(共同复用原则),处理组件依赖问题的三原则:无依赖环原则、稳定依赖原则、稳定抽象原则。

1.OCP(开闭原则):设计良好的软件应该易于扩展,同时抗拒修改。这是我们进行架构设计的主导原则,其他的原则都为这条原则服务。

2.SRP(单一职责原则):任何一个软件模块,都应该有且只有一个被修改的原因,“被修改的原因“指系统的用户或所有者,翻译一下就是,任何模块只对一个用户的价值负责,该原则指导我们如何拆分组件。

举个例子,CTO 和 COO 都要统计员工的工时,当前他们要求的统计方式可能是相同的,我们复用一套代码,这时 COO 说周末的工时统计要乘以二,按照这个需求修改完代码,CTO 可能就要过来骂街了。当然这是个非常浅显的例子,实际项目中也有很多代码服务于多个价值主体,这带来很大的探秘成本和修改风险,另外,当一份代码有多个所有者时,就会产生代码合并冲突的问题。

3.LSP(里氏替换原则):当用同一接口的不同实现互相替换时,系统的行为应该保持不变。该原则指导的是接口与其实现方式。

你一定很疑惑,实现了同一个接口,他们的行为也肯定是一致的呀,还真不一定。假设认为矩形的系统行为是:面积=宽*高,让正方形实现矩形的接口,在调用 setW 和 setH 时,正方形做的其实是同一个事情,设置它的边长。这时下边的单元测试用矩形能通过,用正方形就不行,实现同样的接口,但是系统行为变了,这是违反 LSP 的经典案例。

4.ISP(接口隔离原则):不依赖任何不需要的方法、类或组件。该原则指导我们的接口设计。当我们依赖一个接口但只用到了其中的部分方法时,其实我们已经依赖了不需要的方法或类,当这些方法或类有变更时,会引起我们类的重新编译,或者引起我们组件的重新部署,这些都是不必要的。所以我们最好定义个小接口,把用到的方法拆出来。

5.DIP(依赖反转原则):指一种特定的解耦(传统的依赖关系创建在高层次上,而具体的策略设置则应用在低层次的模块上)形式,使得高层次的模块不依赖于低层次的模块的实现细节,依赖关系被颠倒(反转),从而使得低层次模块依赖于高层次模块的需求抽象。

跨越组建边界的依赖方向永远与控制流的方向相反。该原则指导我们设计组件间依赖的方向。

依赖反转原则是个可操作性非常强的原则,当你要修改组件间的依赖方向时,将需要进行组件间通信的类抽象为接口,接口放在边界的哪边,依赖就指向哪边。

6.REP(复用、发布等同原则):软件复用的最小粒度应等同于其发布的最小粒度。直白地说,就是要复用一段代码就把它抽成组件,该原则指导我们组件拆分的粒度。

7.CCP(共同闭包原则):为了相同目的而同时修改的类,应该放在同一个组件中。CCP 原则是 SRP 原则在组件层面的描述。该原则指导我们组件拆分的粒度。

对大部分应用程序而言,可维护性的重要性远远大于可复用性,由同一个原因引起的代码修改,最好在同一个组件中,如果分散在多个组件中,那么开发、提交、部署的成本都会上升。

8.CRP(共同复用原则):不要强迫一个组件依赖它不需要的东西。CRP 原则是 ISP原则在组件层面的描述。该原则指导我们组件拆分的粒度。

相信你一定有这种经历,集成了组件 A,但组件 A 依赖了组件 B、C。即使组件 B、C 你完全用不到,也不得不集成进来。这是因为你只用到了组件 A 的部分能力,组件 A 中额外的能力带来了额外的依赖。如果遵循共同复用原则,你需要把 A 拆分,只保留你要用的部分。

REP、CCP、CRP 三个原则之间存在彼此竞争的关系,REP 和 CCP 是黏合性原则,它们会让组件变得更大,而 CRP 原则是排除性原则,它会让组件变小。遵守REP、CCP 而忽略 CRP,就会依赖了太多没有用到的组件和类,而这些组件或类的变动会导致你自己的组件进行太多不必要的发布;遵守 REP、CRP 而忽略 CCP,因为组件拆分的太细了,一个需求变更可能要改 n 个组件,带来的成本也是巨大的。

除了上述设计原则,还有一些重要的指导原则如下:

1.N+1设计:系统中的每个组件都应做到没有单点故障;

2.回滚设计:确保系统可以向前兼容,在系统升级时应能有办法回滚版本;

3.禁用设计:应该提供控制具体功能是否可用的配置,在系统出现故障时能够快速下线功能;

4.监控设计:在设计阶段就要考虑监控的手段,便于有效的排查问题,比如引入traceId、业务身份 Id 便于排查监控问题;

5.多活数据中心设计:若系统需要极高的高可用,应考虑在多地实施数据中心进行多活,至少在一个机房断电的情况下系统依然可用;

6.采用成熟的技术:刚开发的或开源的技术往往存在很多隐藏的 bug,出了问题没有很好的商业支持可能会是一个灾难;

7.资源隔离设计:应避免单一业务占用全部资源;

8.架构水平扩展设计:系统只有做到能水平扩展,才能有效避免瓶颈问题;

9.非核心则购买的原则:非核心功能若需要占用大量的研发资源才能解决,则考虑购买成熟的产品;

10.使用商用硬件:商用硬件能有效降低硬件故障的机率;

11.快速迭代:系统应该快速开发小功能模块,尽快上线进行验证,早日发现问题大大降低系统交付的风险;

12.无状态设计:服务接口应该做成无状态的,当前接口的访问不依赖于接口上次访问的状态。

架构师知道了职责,具备很好的架构思维,掌握了通用的架构框架和方法论,使用架构原则进行架构设计,不同的业务和系统要求不一样,那么有没有针对不同场景的系统架构设计?下文就针对分布式架构演进、单元化架构、面向服务 SOA 架构、微服务架构、Serverless 架构进行介绍,以便于我们在实际运用中进行参考使用。

常见架构

分布式架构演进

初始阶段架构

特征:应用程序,数据库,文件等所有资源都放在一台服务器上。

应用服务和数据服务以及文件服务分离

说明:好景不长,发现随着系统访问量的再度增加,webserver 机器的压力在高峰期会上升到比较高,这个时候开始考虑增加一台 webserver。

特征:应用程序、数据库、文件分别部署在独立的资源上。

使用缓存改善性能

说明:系统访问特点遵循二八定律,即80%的业务访问集中在20%的数据上。缓存分为本地缓存 远程分布式缓存,本地缓存访问速度更快但缓存数据量有限,同时存在与应用程序争用内存的情况。

特征:数据库中访问较集中的一小部分数据存储在缓存服务器中,减少数据库的访问次数,降低数据库的访问压力。

使用“应用服务器”集群

说明:在做完分库分表这些工作后,数据库上的压力已经降到比较低了,又开始过着每天看着访问量暴增的幸福生活了。突然有一天,发现系统的访问又开始有变慢的趋势了,这个时候首先查看数据库,压力一切正常,之后查看 webserver,发现apache 阻塞了很多的请求, 而应用服务器对每个请求也是比较快的,看来是请求数太高导致需要排队等待,响应速度变慢。

特征:多台服务器通过负载均衡同时向外部提供服务,解决单台服务器处理能力和存储空间上限的问题。

描述:使用集群是系统解决高并发、海量数据问题的常用手段。通过向集群中追加资源,提升系统的并发处理能力,使得服务器的负载压力不再成为整个系统的瓶颈。

数据库读写分离

说明:享受了一段时间的系统访问量高速增长的幸福后,发现系统又开始变慢了,这次又是什么状况呢,经过查找,发现数据库写入、更新的这些操作的部分数据库连接的资源竞争非常激烈,导致了系统变慢。

特征:数据库引入主备部署。

描述:把数据库划分为读库和写库,通过引入主从数据库服务,读和写操作在不同的数据库服务处理,读库可以有多个,通过同步机制把写库的数据同步到读库,对于需要查询最新写入数据场景,可以通过在缓存中多写一份,通过缓存获得最新数据。

反向代理和CDN加速

特征:采用CDN和反向代理加快系统的访问速度。

描述:为了应付复杂的网络环境和不同地区用户的访问,通过 CDN 和反向代理加快用户访问的速度,同时减轻后端服务器的负载压力。CDN 与反向代理的基本原理都是缓存。

“分布式文件”系统 和 “分布式数据库”

说明:随着系统的不断运行,数据量开始大幅度增长,这个时候发现分库后查询仍然会有些慢,于是按照分库的思想开始做分表的工作

特征:数据库采用分布式数据库,文件系统采用分布式文件系统。

描述:任何强大的单一服务器都满足不了大型系统持续增长的业务需求,数据库读写分离随着业务的发展最终也将无法满足需求,需要使用分布式数据库及分布式文件系统来支撑。

分布式数据库是系统数据库拆分的最后方法,只有在单表数据规模非常庞大的时候才使用,更常用的数据库拆分手段是业务分库,将不同的业务数据库部署在不同的物理服务器上。

使用 NoSQL 和搜索引擎

特征:系统引入 NoSQL 数据库及搜索引擎。

描述:随着业务越来越复杂,对数据存储和检索的需求也越来越复杂,系统需要采用一些非关系型数据库如 NoSQL 和分数据库查询技术如搜索引擎。

应用服务器通过统一数据访问模块访问各种数据,减轻应用程序管理诸多数据源的麻烦。

业务拆分

特征:系统上按照业务进行拆分改造,应用服务器按照业务区分进行分别部署。

描述:为了应对日益复杂的业务场景,通常使用分而治之的手段将整个系统业务分成不同的产品线,应用之间通过超链接建立关系,也可以通过消息队列进行数据分发,当然更多的还是通过访问同一个数据存储系统来构成一个关联的完整系统。

纵向拆分:将一个大应用拆分为多个小应用,如果新业务较为独立,那么就直接将其设计部署为一个独立的 Web 应用系统纵向拆分相对较为简单,通过梳理业务,将较少相关的业务剥离即可。

横向拆分:将复用的业务拆分出来,独立部署为分布式服务,新增业务只需要调用这些分布式服务横向拆分需要识别可复用的业务,设计服务接口,规范服务依赖关系。

分布式服务

特征:公共的应用模块被提取出来,部署在分布式服务器上供应用服务器调用。

描述:随着业务越拆越小,应用系统整体复杂程度呈指数级上升,由于所有应用要和所有数据库系统连接,最终导致数据库连接资源不足,拒绝服务。

分布式服务的问题和挑战:

(1) 当服务越来越多时,服务URL配置管理变得非常困难,F5硬件负载均衡器的单点压力也越来越大。

(2) 当进一步发展,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。

(3) 服务的调用量越来越大,服务的容量问题就暴露出来,这个服务需要多少机器支撑?什么时候该加机器?

(4) 服务多了,沟通成本也开始上升,调某个服务失败该找谁?服务的参数都有什么约定?

(5) 一个服务有多个业务消费者,如何确保服务质量?

(6) 随着服务的不停升级,总有些意想不到的事发生,比如 cache 写错了导致内存溢出,故障不可避免,每次核心服务一挂,影响一大片,人心慌慌,如何控制故障的影响面?服务是否可以功能降级?或者资源劣化?

针对这些问题,下述的单元化架构,微服务架构以及 Serveless 架构可以一定程度解决,另外针对业务系统,需要做到业务与业务隔离、管理域和运行域分开、业务与平台隔离方可解决上述问题。

单元化架构

1、什么是单元化:单元化架构是从并行计算领域发展而来。在分布式服务设计领域,一个单元(Cell)就是满足某个分区所有业务操作的自包含的安装。而一个分区(Shard),则是整体数据集的一个子集,如果你用尾号来划分用户,那同样尾号的那部分用户就可以认为是一个分区。单元化就是将一个服务设计改造让其符合单元特征的过程。

2、单元化的必要性:随着硬件的不断升级,计算机硬件能力已经越来越强,CPU越来越快,内存越来越大,网络越来越宽。这让我们看到了在单台机器上垂直扩展的机会。尤其是当你遇到一个性能要求和容量增长可以预期的业务,单元化给我们提供另外的机会,让我们可以有效降低资源的使用,提供更高性能的服务。

更高性能更低成本是我们的主要目标,经过单元化改造,我们得以用更少(约二分之一)的机器,获得了比原来更高(接近百倍)的性能。性能的提升很大部分原因在于服务的本地化,而服务的集成部署又进一步降低了资源的使用。除了性能收益,还有很多收益,比如更好的隔离性,包括请求隔离和资源隔离,比如更友好的升级,产品可以灰度发布等。单元化改造后对高峰的应对以及扩容方式等问题的解决。

3、如何做到单元化:先看下图传统的服务架构,服务是分层的,每一层使用不同的分区算法,每一层都有不同数量的节点,上层节点随机选择下层节点。

在单元化架构下,服务虽然分层划分,但每个单元自成一体。按照层次来讲的话,所有层使用相同的分区算法,每一层都有相同数量的节点,上层节点也会访问指定的下层节点。

SOA架构

SOA(Service-Oriented Architecture,面向服务的架构)是一个组件模型,它将应用程序的不同功能单元(称为服务)通过这些服务之间定义良好的接口和契约联系起来。接口是采用中立的方式进行定义的,它应该独立于实现服务的硬件平台、操作系统和编程语言。这使得构建在各种各样的系统中的服务可以以一种统一和通用的方式进行交互。面向服务架构,它可以根据需求通过网络对松散耦合的粗粒度应用组件进行分布式部署、组合和使用。服务层是 SOA 的基础,可以直接被应用调用,从而有效控制系统中与软件代理交互的人为依赖性。

SOA的实施具有几个鲜明的基本特征。实施 SOA 的关键目标是实现企业 IT 资产的最大化作用。要实现这一目标,就要在实施 SOA 的过程中牢记以下特征:

(1)可从企业外部访问
(2)随时可用
(3)粗粒度的服务接口分级
(4)松散耦合
(5)可重用的服务
(6)服务接口设计管理
(7)标准化的服务接口
(8)支持各种消息模式
(9)精确定义的服务契约

为了实现 SOA,企业需要一个服务架构,下图显示了一个例子:

在上图中, 服务消费者(service consumer)可以通过发送消息来调用服务。这些消息由一个服务总线(service bus)转换后发送给适当的服务实现。这种服务架构可以提供一个业务规则引(business rules engine),该引擎容许业务规则被合并在一个服务里或多个服务里。这种架构也提供了一个服务管理基础(service management infrastructure),用来管理服务,类似审核,列表(billing),日志等功能。此外,该架构给企业提供了灵活的业务流程,更好地处理控制请求(regulatory requirement),例如Sarbanes Oxley(SOX),并且可以在不影响其他服务的情况下更改某项服务。

微服务架构

先来看看传统的 web 开发方式,通过对比比较容易理解什么是 Microservice Architecture。和 Microservice 相对应的,这种方式一般被称为 Monolithic(单体式开发)。

所有的功能打包在一个 WAR包里,基本没有外部依赖(除了容器),部署在一个JEE容器(Tomcat,JBoss,WebLogic)里,包含了 DO/DAO,Service,UI 等所有逻辑。

优点:

  • 开发简单,集中式管理;
  • 基本不会重复开发;
  • 功能都在本地,没有分布式的管理和调用消耗。

缺点:

  • 效率低:开发都在同一个项目改代码,相互等待,冲突不断;
  • 维护难:代码功功能耦合在一起,新人不知道何从下手;
  • 不灵活:构建时间长,任何小修改都要重构整个项目,耗时;
  • 稳定性差:一个微小的问题,都可能导致整个应用挂掉;
  • 扩展性不够:无法满足高并发下的业务需求。

常见的系统架构遵循的三个标准和业务驱动力:

  • 提高敏捷性:及时响应业务需求,促进企业发展;
  • 提升用户体验:提升用户体验,减少用户流失;
  • 降低成本:降低增加产品、客户或业务方案的成本。

基于微服务架构的设计:

目的:有效的拆分应用,实现敏捷开发和部署。

关于微服务的一个形象表达:

  • X轴:运行多个负载均衡器之后的运行实例;
  • Y轴:将应用进一步分解为微服务(分库);
  • Z轴:大数据量时,将服务分区(分表)。

SOA和微服务的区别:

  • SOA喜欢重用,微服务喜欢重写;
  • SOA喜欢水平服务,微服务喜欢垂直服务;
  • SOA喜欢自上而下,微服务喜欢自下而上。

Serverless 架构

1、思想:无服务器是一种架构理念,其核心思想是将提供服务资源的基础设施抽象成各种服务,以 API 接口的方式供给用户按需调用,真正做到按需伸缩、按使用收费。

2、优势:消除了对传统的海量持续在线服务器组件的需求,降低了开发和运维的复杂性,降低运营成本并缩短了业务系统的交付周期,使得用户能够专注在价值密度更高的业务逻辑的开发上。

3、内容:目前业界较为公认的无服务器架构主要包括两个方面,即提供计算资源的函数服务平台 FaaS,以及提供托管云服务的后端服务 BaaS。

函数即服务(Function as a Service):是一项基于事件驱动的函数托管计算服务。通过函数服务,开发者只需要编写业务函数代码并设置运行的条件,无需配置和管理服务器等基础设施,函数代码运行在无状态的容器中,由事件触发且短暂易失,并完全由第三方管理,基础设施对应用开发者完全透明。函数以弹性、高可靠的方式运行,并且按实际执行资源计费,不执行不产生费用。

后端即服务(Backend as a Service):BaaS 覆盖了应用可能依赖的所有第三方服务,如云数据库、身份验证、对象存储等服务,开发人员通过 API 和由 BaaS 服务商提供的 SDK,能够集成所需的所有后端功能,而无需构建后端应用,更不必管理虚拟机或容器等基础设施,就能保证应用的正常运行。

三个less感觉很好:

  • Codeless 对应的是服务开发,实现了源代码托管,你只需要关注你的代码实现,而不需要关心你的代码在哪,因为在整个开发过程中你都不会感受到代码库和代码分支的存在。
  • Applicationless 对应的是服务发布,在服务化框架下,你的服务发布不再需要申请应用,也不需要关注你的应用在哪。
  • Serverless 对应的则是服务运维,有了 Serverless 化能力,你不再需要关注你的机器资源,Servlerless 会帮你搞定机器资源的弹性扩缩容。

架构师在完成上述架构设计后,最终是需要协同利益相关方一起按项目化运作落地拿结果,那么应该如何保证利益相关方在项目落地的满意度,如何保证按照架构很好的拿到项目成功的结果呢?架构管理能力是架构师非常重要的能力。

架构管理

架构共赢模型

架构结果管理

参考资料:

https://developer.alipay.com/article/8538
https://www.cnblogs.com/wintersun/p/8972949.html
https://www.atatech.org/articles/95466
https://www.atatech.org/articles/104688
https://yuque.antfin-inc.com/tmf/documents/how-to-desigin-domain

声明:本文部分内容参考阿里内部和外部一些文章,详情见上述参考资料;撰写本文的重点是系统体系化地总结认识架构师的工作,以便于更好的互动学习和成长,部分观点是个人观点。

本文作者: 九摩

阿里毕玄:系统架构师如何做好系统设计?


阿里妹导读:毕玄是阿里巴巴资深技术专家,07年加入阿里,一手打造了HSF,十多年来更见证参与了阿里在基础技术上的演进与发展。他觉得系统设计是远比 Java 编程技能更难的培训,很容易变成务虚课。为了挑战难题,毕玄决定大胆尝试在内部搞了个民间培训。于是就有了今天的文章,希望这些深入浅出的解读能给架构师们带来一些收获。
阿里巴巴资深技术专家 毕玄

本文转自毕玄老师个人公众号:hellojavacases(不常更新,一更就是经典)


系统设计我一直觉得是远比Java编程技能更难多了的培训,很容易变成务虚课,全是一堆理论,所以,以前从来不敢尝试做这方面的培训,今年由于一些情况,决定大胆尝试下,就在内部搞了个民间的培训,还真有不少同学捧场。在开始上这门课后,我觉得收获最大的搞不好是我自己,整理思路,从和学员的互动中学习到了很多,能更好地对系统设计的一些方法论进行抽象总结,所以我对这些捧场的学员们说:其实是我们一起在共创系统设计的课程 。

我给系统设计培训定的目标是:

  1. 通过这堂课掌握一个思考框架,知道做系统设计的套路,系统设计不是简单的上来就画画框什么,必须按照一定的套路才能更好的进行系统设计;
  2. 拓宽知识面,系统设计中非常重要的是考虑的全面性,以更好的进行权衡取舍,所以能不能借助系统设计培训来拓宽知识面非常重要。

而要达到这样的效果,怎么去上这堂课挑战其实是不小的:想要传达的思考框架到底是什么?怎么样变成不是纯粹的理论,务虚的传达,从而让大家能更好的掌握并真正的去运用这个思考框架?以前真的没仔细的思考过系统设计的思考框架的问题,其实吧很多的系统设计的模板就是一个思考框架,但在不理解的情况下,是很难应用好的。

系统设计的套路

回顾了下自己做过的几个系统的设计,发现现在自己在做系统设计的时候确实是会按照一个套路去做,这个套路就是:

系统设计的目的->系统设计的目标->围绕目标的核心设计->围绕核心设计形成的设计原则->各子系统,模块的详细设计。

1) 系统设计的目的

是指做这个系统设计的目的到底是什么,很多人在做系统设计时,是搞不清为什么要做一个新系统的设计,或者为什么要做一个系统的重构/演进的设计,如果搞不清楚这个目的,后面的系统设计上是很容易形成偏差的,导致本来是为了解决一个问题,要去做新的系统或重构/升级旧的系统,但最后完全脱离了初心。

另外,还有很重要的一点是,一个大架构师是需要给很多人讲解系统设计的,只有理解并讲清了系统设计的目的,团队才能更好的去实现。

2) 系统设计的目标

围绕上面的目的,能不能形成一些可衡量的目标,从而确保最终系统实现和最初的目的不要出现太大的偏差,相信很多人都经历过最终的系统实现和系统设计偏差极大的现象,主要的原因基本都是没有制定衡量系统设计的目标,并在系统设计上让系统能透出这些目标的情况。

3) 围绕目标的核心设计

这步最重要的就是通过设计如何去实现上面的目标,这个环节中技术的专业、视野、全面的考虑、权衡取舍的主观原则、解题的思路,这是形成最后的核心设计的关键。

在核心设计的这个阶段中,会产生一些新的衡量设计最后实现情况的目标,这些也都要增加到系统设计中,确保最后的实现和设计的偏差度是可视的。

4) 围绕核心设计形成的设计原则

有了上面的核心设计后,可以真正的形成一些设计原则,确保后面的子系统/模块的详细设计中能够遵循,并在详细设计中体现出来,这样才能让整个大的系统设计的一致性。

5) 各子系统/模块的详细设计

这个部分我倒觉得难度不会太大,毕竟有了前面的铺垫,就是解好一个更小范围的题,程序员群体在解题能力上通常是不错的,所以我一直觉得数学功底好是程序员的基本,数学就是典型的解题的学科。

ps: 这就是做分享/培训的好处,借机仔细整理自己的碎片,从而形成体系。

关于怎么更好的让大家能掌握并运用这个思考框架,我的想法就是在讲每个步骤的时候讲讲自己当年在这些步骤上犯的错,实际的经验,这样我觉得也许大家以后在做系统设计时就能想起,所以我觉得系统设计这种培训绝对是需要具备大量实际经验的架构师才能做。

在运用部分,采取的方法就是通过互动,让大家按照同样的思考框架来讲自己的所负责的系统,通过互动来更好的彼此对齐,并借此逐渐变成习惯。

系统设计之系统建设的目的

作为系统设计的第一步,重要性毋庸置疑,如果连系统建设的目的都没搞清楚,后面所有的步骤都会错误。如果我们去看很多的系统设计,会发现压根就没有系统建设的目的的仔细分析。

当需要做系统设计时,就意味着需要建设一套新系统,或者对原有的系统进行比较大的架构的改造升级,这一定是基于什么原因才要去做的。之所以要分析好系统建设的目的,一方面是为了避免出发点有问题,系统建设的目的应该是充分反映出解决业务层面临的挑战,或者系统用户层面面临的问题的,而不是出于个人诉求,另一方面是为了确保在后续的系统设计中能保证目的的达成。

从对系统建设的目的的分析中,是很容易看出格局和高度的,这两个词看起来非常虚,但其实很实,格局和高度通常是指所做的事影响的范围大小,例如从所在的一个小团队,到所在的大部门,到所在的大BU,到所在的大BG,到跨多BG的业务板块,到整个集团,甚至是到这个社会,这里一定要实事求是,别这里讲的是世界形势,后面的整个系统设计又完全解决不了这里所说的。

结合我自己的经历来说,在早期做HSF时,在系统建设的目的这点上是最为缺失的,也导致了自己在HSF阶段犯下了几次大的错误,例如最典型的就是HSF做动态化的那次系统架构改造,如果仔细的去分析当时做这件事的目的,就会看到这个是出于技术情怀,而不是业务端面临的业务挑战,或者说HSF的用户面临的问题,也就是之前说的出发点的问题,我觉得这是很多技术人员非常容易犯的错误,就是纯粹是出于技术诉求发动的很大动作的系统重构,我自己是在有一年有一位阿里的高管在给我做辅导时,提到首先要思考清楚为什么做某件事,并且能讲清楚原因,才明白了动机真的是非常重要的,后面做事的时候才能没那么技术化了。

因为HSF/Ali HBase的经历,到了后面做阿里容器/调度、异地多活的时候在目的这块才算是能更好的把握,能更好的去结合阿里的业务所面临的挑战来看要做的事。

大多数时候,驱动系统设计这件事的发生是其他方提出的,作为架构师,做好需求的转换,决定是否要建设新系统,或重构升级老系统,深刻地去理解系统建设的目的也非常重要,因为架构师是最终要给整个技术团队讲为什么要做的,这能够更好地让团队明白做这件事的价值和意义。

总得来说,我认为做系统设计前,一定要先对于系统建设的目的分析清楚,确保系统建设有价值和有意义,同时确保后面的整个系统设计是能让目的达成的。

系统建设的目标

在分析清楚了系统建设的目的后,到了目标这个环节,最重要的是要把前面的目的的描述,转换为可衡量的目标的描述,之所以要形成可衡量的目标,最重要的原因是为了确保最后实现的系统是达成了系统建设的目的的,相信很多人都碰到过设计出来的系统和最后落地的系统很不一致的现象,通常这都是缺乏了可衡量的目标造成的。

举两个例子说下:

第一个是2011年做容器化,建设这套系统的目的是为了应对预计会越来越大的机器成本,目标相应的制定为支撑相同的业务量,机器下降一半。

第二个是2013年做异地多活,建设这套系统的目的是为了能够让业务具备更强的抵御灾害的能力,尽管后面发现因为有了异地多活,有了更多的好处,但那些确实在系统设计之初是完全没放在建设目的里的,后面能做到纯属巧合,例如因为有了异地多活使得后面的弹性借助云资源成为了现实,因为有了异地多活,基础设施技术的演进可以更加快速,在设计之初根据目的相应制定的目标为业务能够部署在中国多个地点(地点间距离>1千公里),多个地点部署的业务都处于承接流量的状态,且流量从A点切换到B点能在30s内完成。

有了清晰的可衡量的系统建设的目标,意味着:

  1. 确保了系统设计过程中可以非常针对性的围绕目标来做,避免偏题;
  2. 更重要也是最容易遗漏的一点,是可以做一个用来跟踪系统建设效果的系统,例如之前做容器化,我们会有一个展示,是容器化后的集群多少机器支撑了多少的业务量,和目前还未容器化的集群的一个对比;异地多活,会有一个管控系统,用来展示系统的部署情况,以及流量切换。只有有了跟踪系统建设目标是否达标的系统,才能真正确保系统建设完毕后和初心保持了一致,否则很多系统建设的时候是一个目的,最后做完了是另外的状况,所以这个跟踪效果的体系是一定要在系统建设的时候同步就做好的。

从目的->目标这个部分,理论上并不复杂,但也很容易漏掉,导致后面的系统设计环节出问题,关键是要形成可衡量的目标,以及相应的跟踪目标达成情况的系统。

达成目标的核心问题

如果要达成系统设计的可衡量的目标,到底面临了一些什么核心问题,只有明白了面临什么核心问题,才能更加明确的进行系统设计来解决这些问题。

还是用我自己的经历来讲这个话题。

最开始做HSF的时候,为什么要做HSF是比较清晰的,在可衡量的目标上也有一个大概的要支持每条上亿的服务调用,但由于当时的技术功底问题,导致了在提炼核心问题上是有很大差距的,这些也造成了后来HSF总是不断的重构、修修补补之类。所以,我从来就不认为技术功底不好的人能做好一个架构师,架构师绝对不是看到的随手画几个框那么简单,那通常只是个结果,但要合理的把框画出来是需要基于非常坚实的技术功底,HSF在最初设计时认为的核心问题就是怎么实现一个易用有服务定义的RPC框架,但对于如何支撑好上亿的交互调用量,服务化上线后给业务研发会带来什么问题(例如排查问题变复杂了),在核心问题上是有很大的缺失的,例如HSF上线后才发现的中间的负载均衡的问题,而这个问题是导致了HSF结构重新设计的,这个后来回头看就会发现如果是一个知识面更广的架构师可能一开始就会想到这个核心问题,所以如果回过头去看,HSF这样的框架,要达成目标,要解决的核心问题应该是:

  1. 易用、能支撑上亿次服务交互的RPC框架;
  2. 服务间的软件负载均衡问题;
  3. 服务交互的问题排查;

在做T4(容器)的时候,目的、目标都还比较清晰,问题的提炼现在回顾也做的还ok,T4要解的核心问题为如何实现在一台机器上跑20个应用,T4出现的问题更多是对于核心问题的设计方案上,这个到下篇讲围绕核心问题的系统设计上再写。

到了做异地多活的时候,目的、目标的清晰化都ok,对于异地多活而言,要做到在中国多个城市都可同时支撑流量,并且可在几十秒内完成流量切换,异地多活中物理距离所带来的网络延时是不可突破的,怎么做到多地活且流量可动态切换,要做到这个,面临的核心问题是:

  1. 如何将流量进行切分,且让请求的整个处理过程能封闭在local完成;
  2. 如何保障异地多活后的数据一致性?

到了最近几年做统一调度的时候,整个做系统设计的思考框架我觉得算是比较熟练了,所以统一调度的目的、目标都很清晰,结合当时的情况,要实现统一调度的目标,其面临的核心问题是:

  1. 如何实现一套在线业务资源的调度系统去满足各种资源诉求?
  2. 如何尽可能扩大统一的资源池,解决资源池统一面临的资源竞争、资源被抢、多种不同资源规格等问题?
  3. 如何实现在线业务、离线任务两套调度系统的互通?
  4. 如何解决在线业务、离线任务混合部署时的资源竞争的问题?

从上面的这些cases来看,可以看到,从可衡量的目标映射到技术层面要去解决的核心问题,是很需要技术功底的,对于工程类型的项目、产品而言,工程经验在这个时候也会特别重要,而通常我也觉得这是衡量一个优秀架构师很直接的地方。

解决核心问题的设计

继前面的系统建设的目的、可衡量的目标,达成目标的核心问题后,进入到解决核心问题的设计环节了,技术人员其实最擅长的是直奔这个主题,而且估计更期盼的也是这篇,有些时候会导致跳过前面的目的、目标环节,导致最终做出来的系统要么没贴合业务挑战,要么嘛偏离了做这个系统的初衷,所以我仍然强烈建议做系统设计的同学不要着急,一步一步来。

继续结合自己的cases来讲讲解决核心问题的设计这个环节,回顾自己的cases,犯了不少的错误,也碰到了非常多复杂的权衡选择的状况,才逐渐更加明白一个架构师应该具备的一些能力。

HSF的设计

HSF在设计之初要解决的第一个核心问题就是做一个易用,能支撑每天上亿次服务调用的服务方式的RPC框架。

易用这点在第一个版本犯了错,不过还好是第一个版本,否则纠正错误的代价会无比巨大,那个版本里,如果要把一个spring的bean发布为HSF服务,或者调用一个HSF服务,需要写一个文件,在文件里描述发布的服务和调用的服务,并且在这个文件放在jboss的某个目录里,这个方式看起来对在写代码的过程中完全没有侵入,但导致的巨大问题是这文件放在哪里写,写完后部署的阶段怎么自动放到对应的目录去,在第二个版本里才把这个调整为用一个Spring Bean的方式来做服务的发布和调用,尽管这一定程度导致了业务代码需要有对HSF的明显的依赖,但对维护、部署等都变的很标准,所以从这里可以看到,设计是全方位的,要考虑到的不仅仅是怎么实现,还有别人怎么用,运行、维护阶段又是怎么样的。

HSF犯的第二个错,就是在能支撑每天上亿次服务调用的RPC框架这点上,是给我自己代码生涯最大的教训,甚至彻底改变了我之后做设计时的技术选型风格。在做HSF之前,我从来没做过一天访问量超过100w的系统,完全搞不清一个每天上亿次的系统到底有什么不同,HSF最早的版本在通讯框架上选择了JBoss-Remoting,原因也其实比较简单,因为我们用的Web容器是JBoss,结果这个版本在一个非常重要的系统上线时,出现了严重的故障,导致了整个网站的响应速度都变的很慢。当时查了几乎整整一天都没查出原因到底是什么,后来回滚恢复,所以可以肯定是HSF上线造成的,等到回滚后的一个星期内才查出原因,是因为JBoss-Remoting在调用远端时,默认的超时时间为60s,而我们后端的那个系统在处理某些服务的时候会特别慢,进而导致了共用的处理线程池满了,所以整个网站的表现就变慢了。

这次问题让我彻底明白了访问量大的系统最重要的是对整个系统的处理过程要非常的清楚,因为在访问量大的情况下,一些小的问题有可能会放大成很大的问题,进而到故障,所以访问量大的系统对技术的可控性要求是极高的,这也是最大的不同,可控性并不代表一定要完全自己写,但要求如果用到开源的东西,要对开源的东西的代码逻辑非常熟悉,为了解决上面的问题,HSF基于Mina写了一个自己版本的通讯框架,自己来处理连接方式、线程池等,后面在做各种HSF改造,以及其他技术改造时,基本都遵循了技术可控性这个原则。

在前面核心问题那篇里也讲到,HSF在设计时其实核心问题提炼的就是有问题的,导致了后面在负载均衡、服务化后问题排查这两点上出现了严重的返工现象,而这些本其实都可以避免,就像现在再去做服务化框架的人基本都不会犯这样的错了。

在负载均衡这点上,在早期版本里,是通过硬件负载均衡设备来做的,这里造成了好几个问题,一是需要先配置要调用的服务的vip地址,当然,这可以通过一个中央的配置服务器之类的方式,第二个是HSF采用的是长连接的方式,通过vip去连接后端的一个集群时,这里会出现非常麻烦的问题,例如后端集群发布重启,很有可能就会造成连接的极度不均衡,进而导致故障。

除了上面两个问题后,还有一个触发HSF去做改造的原因是当时的硬件负载设备出现了流量跑满的现象,而这是必须要经过的一个点,会造成全站全部崩溃,不希望在未来系统中有个这么大的高风险的集中点,再加上上面的两个问题,决定做彻底的改造,于是HSF开始设计了目前看起来在服务框架体系中非常经典的软件方式的服务注册、发现和寻址的结构。

在负载均衡这件事上,现在回顾也可以看出这个仍然是当初对一个访问量巨大的系统考虑不够全面造成的。

在服务化后会带来的排查问题这点上,当初设计的时候更是完全没有考虑到,导致了后面排查问题效率低、人力投入大等等问题,后来为了解决这个问题,学习了Google家Dapper的思想,但花了很长时间这东西才真正落地。

除了上面这些外,HSF其实还有各种设计问题,例如最早的通讯协议里竟然是没有版本号的,导致后面升级时处理兼容的复杂,又例如更麻烦的一个话题就是在多语言支持上。

HSF作为我第一个真正做的访问量巨大且核心的系统的设计,由于当初的技术功底,犯下了无数错误,导致了N次返工、故障和弥补,当然也让自己得到了很大的成长,这几年回过头想这个问题,越来越觉得必须无比感谢我当时的主管对我巨大的包容和支持,HSF的经历,让我在解决核心问题的设计这个环节上,明白的是作为一个架构师,在技术选型上深厚的技术功底,在整个设计方案上知识的广度,考虑的全面性(从开发态、部署态、运行态和运维态)都是要求极高的。

T4的设计

T4在核心问题的提炼上没有太大的问题,但在怎么解这个问题的设计上那犯下的错误现在来看都是低级到不行。

为了做到在一台机器上能比以前用虚拟机的方式运行更多的应用进程,最早我们采用的方法是各种hack,其实要实现的就是进程级隔离,结果就是hack到了一定程度后,确实勉强能用了,但上线了一些小范围,有了一些用户后,发现我们的hack是很难枚举的,非常痛苦,直到有一天“发现”了LXC,才走对了路线。

除了上面这个选型层面的问题外,T4的过程中还碰到过很多类似的问题,例如用什么方法去控制磁盘空间的限制,最早我们也是用的同样的image的方式,但image的方式对磁盘空间超卖其实是非常不友好的,后来为了把这个方案更换成dir quota的方案,一帮人几乎是连续折腾了一个多月,因为线上已经在运行的要通过cp文件等方法来弄。

HSF的那段看到的很多是在技术深度上的问题,而T4的这段设计,现在回顾最主要的问题是这个技术领域视野的严重问题,所以我认为作为架构师,在相应的技术领域要有足够的视野,一定要知道这个领域的工程界、学术界是什么情况,这样对自己在结合目的、目标以及一些约束条件下做出更合理的技术选型是非常重要的,之前也写过一篇关于如何扩充技术视野的文章。

异地多活的设计

到了做异地多活这个阶段,也许是因为有了前面的一些积累,总结反思,我自己觉得异地多活的设计更多的是选择,至于对错我总体认为还好,所以这里我就讲一些异地多活设计上为了解决核心问题所面临的一些权衡选择,而这也是架构师在做设计上非常重要的一个部分,如何去根据各种约束来做一些方案的权衡选择。

异地多活在核心问题上要解决的是请求封闭、数据一致性这两个关键问题,在为了解决这两个问题的设计上,参考了工程界的一些情况,最后发现我们所面临的状况还是很不一样。

在这里就抛出一些异地多活设计上所面临的选择,我就不去讲我的选择逻辑之类的了,方便大家思考,以及交流探讨。

  1. 流量/数据拆分的规则到底按什么好?买家/卖家/商品?
  2. 分流的规则和数据库分库分表的规则的关系:松耦合 Vs 强绑定?
  3. 数据同步策略的选择:部分 Vs 全量?
  4. 数据一致性的保障,在哪些层面做,CAP?
  5. 部署的选择:两地 Vs 三地,地域的分布选择?
  6. 落地节奏,一年?两年?三年?

架构师应具备的能力总结

最后根据目的、可衡量的目标、核心问题提炼、解决核心问题的设计这些环节,总结提炼下我觉得架构师需要具备的能力:

  1. 对业务所面临的挑战的理解,从业务挑战到技术挑战映射的能力,或者说技术抽象的能力;
  2. 知识储备以及考虑的全面性,从开发、部署、运行、维护态;
  3. 技术选型能力,极厚的技术功底,开阔的技术视野;
  4. 在各种约束条件下权衡选择的能力,原则。

所以架构师我觉得绝对不是烂大街的头衔,要做到一个合格的架构师还是相当难的,尤其是工程类型的架构师,需要长期的实战、经验积累。

系统设计一直是我认为最难讲的内容,主要还是因为我在内部尝试做的一个系统设计的培训,非常感谢一帮同学支持了我做这个培训,要不是他们的参与,我觉得不可能写这篇文章,也不可能较为体系化的说说系统设计,并且更重要的是让我觉得系统设计这个东西其实还是可以不讲的那么虚的,以及系统设计的技能一定程度上确实也是可以培养的。

转载自公众号 HelloJava,作者 bluedavy

为什么说优秀架构师往往是一个悲观主义者?

阿里妹导读:18年前,200家企业由于在事故中信息系统遭到严重破坏而永远地关闭了。这样的事故引发了后人深思,对于工程师而言,不仅要求设计的系统足够强壮,还需要具备考虑失败的能力,当失败场景悉数被考虑周全、并且结合充分的演练,一切会不会不一样?我们熟知面向对象设计和面向程序设计,阿里巴巴资深技术专家游骥洞悉行业现状,抛出了一个新模式——面向失败设计。今天,听他娓娓道来,如何在一开始的系统设计阶段就考虑到各种失败场景,把面向失败当成是系统设计的一部分,准备好从失败中恢复的策略。

引言

一个优秀的架构师通常都是一个悲观主义者,除了设计好能够支撑业务持续发展的优雅架构,另一个容易被忽略的重要能力在于充分考虑失败场景。如果对失败场景考虑不够充分,轻则出现业务不可用,影响用户体验和企业声誉;重则导致数据永久丢失、业务再无恢复可能。

2001 年 9 月 11 日,美国世贸中心双子大厦遭受了谁也无法预料的恐怖打击,灾难发生前约有 350 家企业在世贸大厦中工作,事故发生一年后,重返世贸大厦的企业变成了 150 家,有200 家企业由于重要信息系统的破坏,关键数据的丢失而永远关闭、消失了,其中的一家公司声称自己要恢复到灾难前的状态需要 50 年的时间。

“Everything fails, all the time”,无论是在传统软件时代还是在互联网、云时代,系统终究会在某个时间点失败,面向失败的设计理念数十年来并没有多大的变化,不同的是在分布式、云架构的互联网时代:失败将由小概率偶发事件变成常态,同时应对和处理失败的具体实现方式也大相径庭。

无所不在的失败场景

单个技术点在绝大部分时间都能按照设想正常工作,但是当规模和复杂度到达一定程度,失败其实无所不在。当你的业务场景从服务企业内部的几百号员工变成面向上亿的外部用户,你不确定你的用户群会有些什么样的角色,也不知道他们会在你的系统平台上创造出什么样的业务行为;当你的技术框架从单机、一体机演进到分布式的多层、多组件架构,原本5个以内的技术组件可能变成了今天的500个,并且为了用较低的成本保持服务能力的扩展能力,你可能放弃了稳定性更好但也昂贵的商业技术、转而用开源自建来替代。

互联网业务快速发展不仅直接带来了流量、安全等不确定性,同时促使了技术架构的快速演进,使架构变得越来越复杂,这些因素都将导致失败发生的概率大幅提升。当人类的工作、生活越来越依赖互联网,一旦出现失败,造成的影响和损失将是空前巨大的。在远古时代,人类没有自来水也没有电,一切都很好;今天如果停电停水一段时间,相信很多人都会无法适应,而互联网正在逐步演变成跟水和电一样的基础设施。失败的原因多种多样,抽象来看可以分为以下几类:

硬件问题

首先,硬件是有生命周期的,它一定会老化,并且你不知道它会在什么时候坏;其次,硬件是一个实体,它存在于客观环境当中,它的状态会受外部环境干扰,比如火灾、地震等外力因素都可能导致硬件损坏;最后,所有硬件都会存在残次品,你很可能就是那个不幸者。通常情况下单个硬件出问题的概率不高,但是当有几十万的硬件设备,硬件的失败问题每天都会发生。

软件bug

即便是最优秀程序员写出来的程序,经过最优秀测试同学的严格测试后的代码,上线依然无法做到完全没有bug。互联网业务迭代往往讲究一个“快”字,以往几个月或者几年升级一次的软件程序,现在一周就需要升级一次或者多次,这大幅提升了软件出错的可能性。

配置变更错误

系统运行态的日常运维过程当中,难免会因为疏忽或者考虑不周全导致灾难。当上万名技术同学跟上百个变更系统做笛卡尔积,哪怕是6个9的可靠性,依旧无法做到万无一失。全局的流量入口、权限与安全验证体系、统一网关与接口平台等技术环节是可能促发全站不可用的重要风险点,对于影响面大的配置的变更需要尤为谨慎。

系统恶化

原本工作得很好的程序随着时间的推移可能有一天不再正常工作,举几个常见的例子:自增变量运行了很长一段时间后出现越界、缓存随着数据量的逐渐变大而出现空间不足、数据库连接池随着机器的扩容而不够用等等。千万不要认为运行良好的系统是不会出问题的,它的代码里面可能藏了定时炸弹,只是你不知道会在什么时间点爆炸。

超预期流量

某一天你的系统可能突然会承受远超过预期的每秒请求数,特别是在“中国特色”的互联网场景之下,你很难精确预估系统各个时间点的业务访问量。

外部攻击

你需要考虑各种攻击行为,包含流量攻击和安全攻击。你的系统可能随时会面临着DDOS和CC类攻击,你传输的数据可能会被盗取或者篡改。

依赖库问题

你的系统很可能会用大量的二方库或者三方库,它们对你来说是黑盒子,你不了解它们存在哪些风险,并且你无法掌控。这些库可能会存在漏洞、可能会有bug,可能会大量消耗你的系统资源,总之不要太信任它们。

依赖服务问题

你依赖的服务也一定不会100%可用,它们可能会超时,可能会失败。当依赖服务超时的时候,如果你没有很好地处理,可能会导致你自己的系统无法工作,在分布式场景下,这种失败状态会持续辐射,最终导致大面积的不可用。

如何面向失败设计

作为一个悲观主义者,你需要在一开始的系统设计阶段就考虑到以上各种失败场景,把面向失败当成系统设计的一部分,并且准备好从失败中恢复的策略,这有助于更好地提升整个系统的可用性。只有你意识到事情会随着时间的推移而失败,并将这种思想融入到体系结构中,那么在失败发生的时候你才能完全不受影响或者将失败损失降到最低。面向失败的设计理念数十年来并没有多大的变化,一些好的经典原则在今天依旧被广泛运用。

冗余设计避免单点故障

硬件和软件都不可靠,环境和人都存在极大的不确定性,虽然无法避免失败场景的发生,但是可以通过冗余设计来规避局部失败对系统的影响。冗余设计避免单点故障这一策略在互联网技术架构中处处可见,比如重要的服务通常都会部署多个、数据库的主备结构、服务调用的重试机制、存储的多副本等概念都属于这一范畴。

面向失败的宏观多活架构

除了局部失败场景,你的系统可能还面临着大范围的失败场景。大范围的原因有两个:天灾,比如火灾、地震、台风、雷电等大的自然灾害可能导致大面积的基础设备被毁坏;人祸:人的失误或者刻意破坏行为有时候也会酿成大祸,如操作错误、破坏、植入有害代码和恐怖袭击。“面向失败的宏观多活架构”从宏观架构的高可用层面来解决系统的整体可用性问题,随着技术的演进,冷备、热备、两地三中心、异地多活等应对大范围失败场景的技术体系这些年频频被提起。

服务能力与依赖调用自我保护

如何来衡量一个软件系统的设计是否优良?一条很重要的衡量标准——在任何情况之下你的软件系统都应该工作在当前环境的最优状态。每个人都知道机翼是飞机的重要部件,一旦机翼出现问题,飞机很可能就会坠落。然而在二战当中,许多战斗机即便机翼千疮百孔了,依然保持着最佳战斗能力;甚至还有更夸张的情况:1983年的一次战斗机演习当中,一架飞机由于事故损失了一个机翼,这架缺少一个机翼的飞机依然保持了飞行能力、最终完成安全着陆。

软件系统由两部分构成:系统自身的代码和依赖的库以及服务。“服务能力与依赖调用自我保护”需要从这两块分别切入构建系统在任意情况都始终工作在最佳状态的能力。服务限流、系统负载保护、给依赖的服务设置超时或者资源限制等都是相应的应对策略。

为一切不可预料的情况备好预案

能够抵抗失败和从失败中快速恢复是面向失败设计的核心思想,然而即便已经做了万全的设计,也并非所有的失败场景都是系统能够自动抵御的。你需要考虑到所有的失败场景,并准备好相应的应对预案。为一切不可预料的情况备好预案才能在失败场景真正发生时做到有条不紊。做好预案需要对失败场景有全面的考虑:会发生哪些失败?失败会带来什么问题?应对策略是什么?预期的恢复时间多久?恢复后的影响面有多大?需要通知到哪些角色?等这一系列的因子构成了一个完整的预案体系。

自动化运维管控

大量的系统故障是因为人的失误造成的,即便让一个优秀的运维工程师进行一万次同样的运维操作也难免不出错。唯一的解决办法便是在运维过程当中尽可能降低人为操作的比重。系统化、白屏化是第一个阶段——将人为的操作步骤固化成系统程序,避免操作失误;自动化以及智能化是第二个阶段——将正确的决策过程也固化成智能程序,避免决策失误。同时所有的运维动作都需要遵循灰度原则,做到可灰度、可监测、可回滚,即便出现了失误也能控制好爆炸半径,并且做到快速恢复。

精细化的监控体系

面向失败设计不仅要求你的系统足够健壮,同时要求你能够在第一时间感知到失败的发生。无论是自动化的系统恢复,还是人为介入,如果你压根都不知道是哪里出问题了,一切都将束手无策。精细化的监控体系一方面能够在出现问题的时候以最快的速度将最准确的信息传递到人或者运维系统,同时它还能够展现趋势、进行提前预警。AI技术的结合使得监控领域在近几年得到了新的发展驱动力:智能监控报警、根因定位、智能预测、智能决策等能力都是学术界和工程界非常热衷的课题。

故障与攻防演练锤炼容灾应急能力

最后,即便以上工作都做好了,你也不能高枕无忧去等待失败到来。你的设计、系统、流程、技术人员等需要通过不断演练,来保障能力和进化升级。对于代价非常巨大的事件,做好前期的充分演练是非常有必要的,比如军事演练、消防演练等都属于这一范畴。而系统不可用的代价对于企业来讲很可能是无法承受的,因此需要在平时做好充分的演练:通过故障与攻防演练锤炼容灾应急能力,对面向失败的设计做好充分验证。只有当所有的失败场景都被提前演练过,当失败真正来临时才能做到胸有成竹。

作者: 游骥

更多云计算干货敬请关注阿里云官网知乎机构号:阿里云官网 - 知乎