课程实录丨解密互联网架构设计(1)

本实录是根据“博文视点在线课堂”线上分享内容汇总整理而成,为方便大家学习,现以文字和音频的形式推出,音频可点击阅读原文跳转至“博文视点”喜马拉雅页面收听。

分享嘉宾:李业兵

相关图书:《架构探险:从零开始写分布式服务框架》


大家好,今天很高兴能够在这里和大家一起聊聊技术。

今天我要分享的是解密互联网时代系统架构设计,和大家一起聊聊这些年来做系统架构设计的一些常用的方法,以及这些方法论背后的技术设计原则,同时尝试解读一下推动这些方法论演变的现实原因。

我分了下面几个小的主题,一个是软件的复杂性,第二个是面向对象设计,第三是领域驱动设计,第四是面向服务架构,第五是微服务架构,我们一层一层地递进来完成这个分享。

下面开始聊一下关于软件复杂性的话题。

软件是复杂的,相信我们大多数人都认同这个观点,如果软件很简单,那我们也没有必要花这么多精力来学习各种秘籍成为这个软件开发界的高手了,更没有必要弄这么多的设计方法论出来,显得多此一举。

那么大家可以先想一想,都说软件很复杂,那这个软件到底复杂在哪里呢?我这里做了一个总结,列举了导致软件复杂性的部分原因。软件它承载了我们的业务,它帮我们解决现实生活中的实际问题,业务自身有可能是非常复杂的。因为一些复杂的业务领域,包含了很多的业务子域,每一个业务子域都有自己的业务逻辑,而这些业务子域环环相扣,组成一个大型的、复杂的业务域,譬如大家都非常熟悉的淘宝、京东,他们的业务大概就是这个样子。再就是与我们开发人员对业务的理解程度也有关系。所谓术业有专攻,毕竟开发人员并不是业务领域的专家,对业务的理解程度也会影响软件实现的复杂性。软件是用编程语言实现的,与业务语言存在较大的差异,它们之间存在阻抗不匹配的现象,而且变成语言自身也有复杂性需要克服,有一些软件成长的用户量非常大,由此会产生巨大的数据量,所谓量变产生质变,往往“多”本身就是一个非常复杂的事情,我们要花费巨大的精力来优化性能,存储用为海量的业务数据,其实这也是一件非常复杂的事情。

我们的软件上线之后需要维护服务,包括学习处理现场出现的故障。比如突然有一个秒杀活动,在某一时刻他的访问量非常高,导致机器的网卡打满了或者数据库连接池打满了,导致数据库获取不到连接了,或者应用依赖的某一个中间键突然挂了,这些类似的突发问题会非常多,业务需求的变更也会非常多。比如今天产品经理要搞一个立减的促销,改天可能要做一个满减的打包促销,这些都是一些灵活性、离散性非常高的事件,都是随时有可能发生的。再说一个方面,因为软件是由人来设计,由人来开发实现,由人来运维,人是世界上最复杂的动物,这句话应该大家都认同了。软件开发团队的管理因为其复杂性,成为一门很高深的学问,管理的成功与否直接关系到这个软件项目完成质量的好坏,甚至整个项目的成败,软件的复杂性远远不止体现在以上的这几个方面。

说了这么多就是想向大家证明一点,软件是非常复杂的,软件这么复杂,我们在设计软件和系统的时候要如何应对这种复杂性呢?软件的复杂性问题的解决之道在哪里呢?

大家可以看一下这些PPT,我画了这样一幅图,总结了软件架构设计方面方法论递进演变的过程,从面向过程到面向对象,从面向领域驱动设计到面向服务设计,再到现在比较火的微服务设计。最开始软件开发我们姑且称为远古时期,其实软件出现的历史并不长,也就几十年的时间,那个时候应该是没有设计一说的,初始的时候我们就统称为面向过程设计吧。面向过程说白了,实际上就是人思维的一种自然展现形式,按照事件发生的流程逐步完成整个软件的编写。培养过程编程,实际上它是不利于抽象和封装,也不利于隔离,很容易写出难以维护的代码,相信我们都很讨厌那种动不动就成千上百行像流水账一样的代码,这就是面向过程变成了一种不好的体现形式。

为了解决这个问题,后面就发展出了面向设计的这种设计理论,通过编程语言提供的以类的形式来对我们的业务进行一个拆解、封装、隔离,但是这种抽象的手段层次比较低,它是从语言层面入手来做抽象,无法应对太复杂的业务。大家想一想,如果一个复杂的业务,按照面向对象的理论来分解成类,3万个类都是有可能的,因为任何事物一旦多到一定程度,复杂性就出来了,所以随着业务复杂程度的上升,单单靠面向设计是搞不定的。

随着业务复杂程度的上升,面向对象设计就有点力不从心了,就轮到面向领域设计登场了。面向领域设计的抽象层次更高一点,它是按照初力度的业务模块的层次来做抽象。同样的道理,业务是可以复杂到面向领域抽象也搞不定的程度,因为从面向对象到面向领域,实际上还是在业务逻辑的层面进行一个抽象,缺乏一个物理上的隔离。

我们来看下一页PPT,这是某知名第三方支付公司的一张应用架构简图,我只是为了说明这个业务的复杂程度,担心泄密,所以画的特别简单。但是我们可以看到整个应用架构划分了十几个大的业务域,像渠道平台、工作平台等等。但如果我们在进行工程的具体实现的时候,要是只按照业务领域这个逻辑层面的抽象来做抽象、来做模块化的话,大家可以想一下,这最终是一个实现极其庞大的单体应用,这简直是不可能实现的,要实现我们后续的运维也是不可能做到的。

继续看上一页的PPT。为了解决巨大的单体应用这个问题,面向服务架构就粉墨登场了。面向服务架构提供了物理层面进行抽象、隔离的手段。服务化实际上提供了三大能力,第一,它提供了物理层面对业务分解抽象的能力,第二是它解决了系统的水平扩展的问题,第三点也是非常重要的一点,它能够让一家公司具备百人以上团队进行协作开发的能力。

关于这三点后面会有详细的说明,这张图的金字塔顶端就是现在很火的微服务,我理解的微服务本质上和服务化是一回事,只不过更加强调这个技术站的完整支枝,更加强调组建化,可以看作是服务化的延伸,我认为它和服务化本质上是没有什么太大的区别的。从整体上看,业务的复杂性驱使着我们寻找解决这个复杂性的设计方案。由金字塔从下向上看,随着业务复杂性的逐步升高,需要我们提供更高层次的抽象.为什么是个金字塔的形状呢?是因为这些设计方法论并不是一个排他关系,实际上它们是一个相互包含、相互继承的关系,后面会有详细的说明。

下面我们来逐个谈一谈上面介绍的这些方法论。

我们先聊一聊面向对象,面对象是一个比较老的技术了,比如说学Java的人或者学实业家的人,从学语言的第一天入门开始,就应该听过面向对象这四个字,你先思考一下面向过程遇到了什么问题,哪些问题导致了我们这个面向对象设计方法论的诞生。相信有经验的开发者一般都接受过流水账一样的代码,甚至写过这种代码,这样代码如果非常多的话,是很让人头疼的,因为它很难维护。比如说你要修改某个功能,因为这个流水账一样的代码,缺乏良好的一个封装,有可能你需要改系统里面大量的零散的一些点,改完之后测试的工作量也非常大,并且这个可读性也非常不好,这个时候你就非常头疼了,因为人脑是有限的,人脑的容量它是有限的,面向过程的代码因为缺乏一个良好的封装,导致这个大脑需要一次性地关注非常多的点,很有可能完成这个需求,维护好这个系统,为了解决这个问题,所以就出现了面向对象这个方法论。

面向对象的核心理念我大概总结了一下,像抽象、封装、模块化、分层、聚合、解耦,复用。需要注意的是面向对象并不是完全抛弃了面向过程,它是在面向过程的基础上进行了一个演化,我们使用面向对象,使用抽象、封装、模块化这些手段来抽取业务逻辑中独立的业务模型,使用类的方式来模拟业务模型的行为,最终完成业务细分的解耦,通过多个类的聚合,以类之间的复用来组合成更大块的业务模型,最终完成整个系统的分析、设计和开发。

但是实际上在这个过程中,面向对原子的逻辑某一个类的某个方法里面而言,面向过程开发这样一个方法。

这里以Java语言为例,总结了部分面向对象的技术要素,包括模型的建立、类模型的建立、类泛型的泛化、面向技术编程以及类之间的各种关系,包括依赖、关联、组合、继承等等,

UML又称为统一建模语言,它是一个作为面向对象设计发展的高潮产物,定义了三种图来表达软件设计中出现的各种行为以及它的一些构建。一共分为两大类,包括结构图与行为图。其中结构图包括对象图、主图、类图等等;行为图包括用例图、顺序图等等。其实这每一种图都能够从不同的方面来表达软件设计中的一种行为,或者是软件的某一种构成、构建。UML就是面向对象设计理论的一个产物,因为时间的原因就不做深入的介绍了,大家可以自行查阅相关的资料。

面向对象的另一个技术成就是设计模式,所谓的设计模式就是前人在面向对象实践中总结的一些最佳实践,最终形成了23种设计模式,也包括三大类——创建型、结构型、行为型。可以说,设计模式是面向对象设计里面最重要的一个技术沉淀之一,在面向对象设计的武器库里面,这可以直接拿来使用的,可以认为是一个最重要的武器。推荐大家平时在开发过程中多多使用,实际上如果你完全理解了面向对象设计的这种理念,能够达到心中不要刻意的去使用这个设计模式,心中没有这个设计模式,才能够信手拈来随意变化,最终达到无招胜有招的境界。打个比方,面向对象是武功心法,设计模式就是具体的武功招式。

讲到这里需要回答两个问题,分别是面向相对象为我们解决了什么?面向对象解决不了什么?大家可以自己先想一想。我这里总结了一下面向对象它的局限性在于,它的抽象层次还是比较低的,它是在语言组织的层面提供了一个抽象的层次,分层、模块化。它是通过语言的一个特性来实现的,在相对简单的业务或者一些小型系统建设方面,它是非常有用的,提供了很大的一个助力。但是如果我们面对非常复杂的业务,就显得有点力不从心了,所以面对更复杂的业务,我们需要一个更高层次的一个抽象,来进行整个系统的一个分层、模块化。

为了解决这个面向对象的局限性,我们的领域驱动设计就粉墨登场。

大家可以先想一想领域是什么,领域实际上就是一块边界清晰的业务,这里要强调一下边界清晰才能够构成领域这块业务。举个例子,比如说我们的那个电商交易系统,整个电商的交易链可以很清晰的划分为供应链、交易、结算等等,这样一个大的业务域,供应链就可以再细分为上单、库存等子领域,交易也可以再细分为下单、价格、订单、促销等这些子领域。每个领域的业务都比较独立的,并且有清晰的边界,比如说价格和促销之间很明显是两块不同的业务。

领域驱动设计的核心理念,就是抽象业务的领域模型,以业务为先,要提取这个业务概念,分解它的复杂性,最终完成整个业务的建模,形成一套统一的业务语言。

这里有一张简图,用来说明面向对象和领域驱动设计的一个区别。领域驱动设计在抽象层次上是超越了面向对象的,面向对象的业务促销力度是以类为单位,类代表了业务里面某一个小的实体。领域驱动的抽象力度是以业务模块为单位,一个领域可以包含多个实体类。其实领域驱动设计与面向对象设计是相辅相成的,因为单个领域内部的设计,一般也是使用面向对象设计来完成的。大家可以回想一下前面那个金字塔结构,它是一个继承关系,实际上它们是相辅相成的。

我们拿到一个需求文档做需求分析和设计的时候,一般有两种思路,第一种首先考虑的是数据,从需求中首先要把这个数据模型给梳理出来,然后根据这个模型来建表,建工程的时候以这个表为中心。首先我们可能会构建一个倒程,在倒程上面再构建我们的Service行为层,就是这个表有哪些行为方法,然后在Service行为层上面,组装这些Service来形成Business层,也就是业务层。这种思路我们叫做面向数据表编程,就是把整个业务切分为数据和行为,由数据的角度来构建系统,这是我们的第一种思路。

第二种思路也就是我刚发的PPT右边的这个图,拿到需求的第一步考虑如何抽取这个领域模型,就是以业务为先,首先考虑业务的领域。领域模型里面包含了领域模型的行为,至于数据表是我们最后考虑的事情,当我们这个领域需要做自序化的时候,我们再划分这个数据表。一个领域模型可能会对应多张表,这个时候也可以允许有倒程,实际上这个时候的倒就是一个适配层,它用了适配这个数据表与领域模型的实体,领域驱动设计是面向业务来做设计,而不是面向数据来做设计,这是最重要的一个、最需要关心的一个点。

说了这么多的理论概念,大家可能还是对怎么做领域模型驱动设计一头雾水。下面做一个具体的例子,这是一个促销系统的设计图,大家可以看一下,整个设计我分为促销计算引擎、促销活动管理这样两大领域。促销引擎和促销活动管理领域又切分为了好几个子的业务域,从这个业务领域的角度出现了整个系统模块的边界以及规范了系统的行为,这里面没有涉及到任何一张数据表,我们有的只是业务模块、业务领域,通过这样一个简单的例子来说明怎样进行一个面向领域设计。

同样这里也需要回答两个问题,就是领域驱动设计为我们解决了什么?领域驱动设计还有哪些问题解决不了的?大家也可以自己先想一想。其实领域驱动设计和面向对象是一样,它本质上还是属于一个逻辑范畴的抽象手段,后面还会提到面向服务架构和微服务架构,他们只是属于物理层面上的一个抽象手段。我们现在已经有了领域驱动设计这个武器了,那么是不是就能解决我们所有的问题了呢?我们是不是就从此就高枕无忧、天下无敌了呢?其实这是不够的,之前也提到过,比如说PPT第六页的那个电商交易系统,如果我们纯粹的按照领域驱动设计很完美的把系统设计出来了,其实还是有很多问题的。比如系统全部在一个单体的工程里面,整个开发团队能维护这一份代码,会导致我们的系统发布特别的频繁,进而造成我们的系统不稳定。再就是随着时间的推移,久而久之,原本清晰的这个领域模块边界会变得模糊不清,因为我们都在一个工程里面,以包结构做划分,缺乏一个强有力的一个分割手段。

第三就是代码量非常大的工程会导致我们编译打包时间很长,开发效率会变得低下,也无法通过一个强力的编程模型进行服务的复用,如果流量特别大的话,这个单体应用,由于承载的业务非常多,很可能会导致数据库的链接不够用,所以我们需要对这个大工程进行拆分,拆分成一个个小的应用服务,进行隔离部署,从物理层面进行更高层次的抽象,所谓物理层面就是通过隔离部署使其完全隔离开,不在一个工程里面。这种架构方式就是下面要介绍的面向服务的架构。



 

博文视点

您阅读的专业智库

喜欢请分享至朋友圈

了解更多本书详情请点击阅读原文

长按二维码轻松关注


点击阅读原文,即可快速抵达本书详情页!