禅与计算机程序设计艺术

怎样成为一个优秀的架构师?

2019-10-08  本文已影响0人  光剑书架上的书

怎样才算是架构师?

架构师是一个既能掌控整体又能洞悉局部瓶颈并依据具体的业务场景给出解决方案的团队领导型人物。看似完美的“人格模型”背后,是艰辛的探索。

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

架构师的分类

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

架构师的职责

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

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

从项目视图看:

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

架构原则

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

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

设计原则

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

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

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

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

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

Rectangle r = ... 
r.setW(5); 
r.setH(2); 
assert(r.area() == 10);

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 架构进行介绍,以便于我们在实际运用中进行参考使用。

具备架构师的思维

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

1、自顶向下构建架构

要点主要如下:

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

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

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

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

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

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

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

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

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

2、自底向上推导应用架构

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

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

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

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

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

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

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

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

3、领域驱动设计架构

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

领域划分设计步骤:

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

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

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

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

4、基于数据驱动设计架构

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

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

单元化架构,微服务架构以及 Serveless 架构

单元化架构

1. 单元化是什么

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

单元化架构,为什么要用以及我们如何做到

图 1 :洋葱细胞的显微镜截图,单元化要达到的目的就是让每个单元像细胞一样独立工作

在传统的服务化架构下(如下图),服务是分层的,每一层使用不同的分区算法,每一层都有不同数量的节点,上层节点随机选择下层节点。当然这个随机是比较而言的。

单元化架构,为什么要用以及我们如何做到

图 2 :传统的服务化架构,为伸缩性设计,上层节点随机选择下层节点

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

单元化架构,为什么要用以及我们如何做到

图 3 :单元化架构,为性能和隔离性而设计,上层节点访问指定下层节点

2. 为什么要用单元化

在性能追求和成本限制的情况下,我们需要找到一种合适的方法来满足服务需求。在传统的分布式服务设计,我们考虑的更多是每个服务的可伸缩性,当各个服务独立设计时你就要在每一层进行伸缩性的考虑。这是服务化设计(SOA)流行的原因,我们需要每个服务能够单独水平扩展。

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

总体而言,更高性能更低成本是我们的主要目标,而经过单元化改造,我们得以用更少(约二分之一)的机器,获得了比原来更高(接近百倍)的性能。性能的提升很大部分原因在于服务的本地化,而服务的集成部署又进一步降低了资源的使用。

当然除了性能收益,如果你做到了,你会发现还有很多收益,比如更好的隔离性,包括请求隔离和资源隔离,比如更友好的升级,产品可以灰度发布等。单元化改造后对高峰的应对以及扩容方式等问题,各位可以参考#微博春节技术保障系列#中的单元化架构文章,也不在此一一赘述。

3. 我们如何做到

此次单元化改造基于微博现有的业务,因此这里也先行介绍一下。粉丝服务平台是微博的内容推送系统(代号 Castalia),可为 V 用户提供向其粉丝推送高质量内容的高速通道(单元化之后已到达百万条每秒)。整个服务涉及用户筛选、发送计费、屏蔽检查、限流控制和消息群发等多个子服务。由于改造思想相通,这里以用户筛选和消息群发两个服务为例,下面两图分别为商业群发在服务化思想和单元化思想下不同的架构。

单元化架构,为什么要用以及我们如何做到

图 4: 服务化思想下的商业群发架构设计(旧版)

单元化架构,为什么要用以及我们如何做到

图 5 :商业群发在单元化思想下的架构设计(新版)

对于筛选服务,在服务化架构里,需要去粉丝服务获取粉丝关系,然后去特征服务进行用户特征筛选,最后将筛选结果传输到群发服务器上;而在单元化架构里,粉丝关系直接就在本地文件中,用户特征服务也在本地,最后的筛选结果再不需要传输。服务本地化(粉丝关系和用户特征存储)减去了网络开销,降低了服务延时,还同时提高了访问速度和稳定性,而筛选结果本地存储又进一步节省了带宽并降低了延迟。以百万粉丝为例,每次网络操作的减少节省带宽 8M 左右,延时也从 400ms 降为 0。

群发服务同样如此。由于在服务化架构里,我们使用 MySQL 和 Memcache 的方案,由于关系数据库的写入性能问题,中间还有队列以及相应的队列处理机,所有四个模块都有单独的机器提供服务,而在单元化架构里,四合一之后,只需要一套机器。当然机器的配置可能会有所提升,但真正计算之后你就会发现其实影响微乎其微。原因除了前面介绍的硬件增长空间外,上架机器的基本配置变高也是一个原因。而且,在单元化方案里,当我们把缓存部署在本地之后,其性能还有了额外的 20% 提升。

一些业务特有问题

不过群发这个场景,我们也遇到了一些特定的问题,一是分区问题,一是作业管理。这里也与各位分享下我们的解决方法。

  1. 分区问题

    分区问题其实是每个服务都会遇到的,但单元化后的挑战在于让所有服务都适配同一分区算法,在我们的场景下,我们按照接收者进行了分区,即从底层往上,每一层都来适配此分区算法。

    这里有特例的是用户特征和屏蔽服务,由于总体容量都很小,我们就没有对数据进行分区,所有单元内都是同一套全量数据,都是一个外部全量库的从库。不过由于本单元内的上层服务的关系,只有属于本分区的用户数据被访问到。所以,适配同一分区算法在某种程度上讲,可以兼容即可。

  2. 作业管理

    按照前面的分区方式,将群发服务的整体架构变成了一个类似 Scatter-Gather+CQRS 的方案,因为 Gather 不是一个请求处理的必须要素。也就是说,一个群发请求会被扩散到所有单元中,每个单元都要针对自己分区内的用户处理这个群发请求。

    广播方式的引入,使得我们首先需要在前端机进行分单元作业的处理监控,我们在此增加了持久化队列来解决。同时,由于单元内每个服务也都是单独维护的,作业可能在任何时间中断,因此每个作业在单元内的状态也都是有记录的,以此来达到作业的可重入和幂等性,也就可以保证每个作业都可以在任何时间重做,但不会重复执行。

除此之外,我们还对服务器进行了更为精细的控制,使用 CPU 绑定提高多服务集成部署时的整体效率,使用多硬盘设计保证每个服务的 IO 性能,通过主从单元的读写分离来提高整体服务等等。

参考文章:https://www.infoq.cn/article/how-weibo-do-unit-architecture/

SOA架构

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

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

为了实现 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 等所有逻辑。

1、优点:

2、缺点:

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

4、基于微服务架构的设计:

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

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

5、SOA和微服务的区别:

Serverless架构

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

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

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

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

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

三个less感觉很好:

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

架构师管理架构双赢模型

架构结果管理


Kotlin 开发者社区

国内第一Kotlin 开发者社区公众号,主要分享、交流 Kotlin 编程语言、Spring Boot、Android、React.js/Node.js、函数式编程、编程思想等相关主题。

越是喧嚣的世界,越需要宁静的思考。

合抱之木,生于毫末;
九层之台,起于垒土;
千里之行,始于足下。
积土成山,风雨兴焉;
积水成渊,蛟龙生焉;
积善成德,而神明自得,圣心备焉。
故不积跬步,无以至千里;
不积小流,无以成江海。
骐骥一跃,不能十步;
驽马十驾,功在不舍。
锲而舍之,朽木不折;
锲而不舍,金石可镂。
蚓无爪牙之利,筋骨之强,上食埃土,下饮黄泉,用心一也。
蟹六跪而二螯,非蛇鳝之穴无可寄托者,用心躁也。

上一篇下一篇

猜你喜欢

热点阅读