目录
1.4 对比单体系统、分布式系统和微服务系统
1.4.1 单体系统之痛
1、什么是单体系统
2、单体系统面临的问题
1.4.2 高并发系统之分布式架构
1.4.3 高并发系统之微服务架构
接下来从企业真实场景出发,对比单体系统、分布式系统和微服务系统
单体系统并非一无是处。在某些场景中,单体系统是最佳的选择,可以帮助企业业务快速发展。在企业初创期,投入较少的研发资源构建出适应当前业务的单体应用,从而达到抢占市场和技术试错的目的。
单体系统即一个应用程序,所有的业务代码都在这一个应用程序中,所有的表也都在一个数据库中,所涉及的
相关文件都在同一个服务器上。在企业初创期,为了快速进入市场,一般企业都采用单体系统。淘宝等电商平台在初创期也都采用的是单体系统。
在企业初创期,用户量不多,业务场景也不复杂,这正是验证技术和业务模式可行性之时,系统越简单越好搭建过程越快越好。于是,可以在同一台服务器上构建“1个应用程序+1个数据库+1个文件服务器”的单体系统,如下图所示。
●企业快速发展后,单体系统可能会面临以下问题
●需要频繁地合并代码分支,影响项目的迭代进度
●多人协作耦合度高,测试效率低下
●开发节奏混乱,代码冲突频繁。
●代码模块层次越来越复杂,业务边界变得不清晰
●项目越来越庞大,技术架构升级变得困难。
此时如果需要迭代系统版本或上新一个业务板块,则会涉及系统的多个业务。
例如,对于一个产品需求,可能会拉取多个分支(feature/A和feature/B)进行开发。在开发feature/B 过程中,由于业务需要修改了部分功能,这部分功能涉及 feature/A的功能,然后进行测试,也通过测试了。在之后开发feature/A过程中进行代码拉取及合并时,可能会出现各种代码冲突,这就需要花费大量的沟通成本去解决。
项目的参与人员越多,则代码冲突的概率就越大。如果没有版本规划管理,那么这种低效的开发方式会严重阻塞产品版本迭代。下图是一个比较良好的分支开发建议。
如上图所示,多人同时开发同一个项目,可能会出现随意修改彼此代码的风险,往往需要花费大量的沟通成本去解决问题。而且这会增加测试的复杂度。
因为,每次版本迭代都需要对整个系统进行回归测试,之后才能将其上线。而这是一个非常漫长的过程,会浪费测试人员很多的时间,并且,在测试通过的版本中也可能潜藏着一些测试人员没留意的问题。
单体系统般采用三层架构,如下图所示。
(1)用户展示层:负责用户端的展现和体验2)业务层:负责业务的所有逻辑操作。
(3)数据访问层:负责操作数据库,如读写数据库等。
单体系统架构分层也有弊端·
●从水平方向来看,的确降低了业务的深度复杂性
●从垂直方向来看,单体的业务边界不够清晰,因为在各层之间会进行网状的调用,比如,用户展现层的某个模块会调用业务层的多个模块(甚至所有的模块):业务层的模块同样会调用数据访问层的多个模块等。
在业务稍微复杂的场景下,单体架构的模块只是在逻辑上是独立的,在物理上并没有分开,模块之间的依赖关系比较混包
通过上面的内容我们可以知道,单体项目当前的代码量已经非常庞大了,而且开发沟通成本也很大,业务模块的调用也异常复杂。
如果单体应用使用了很多的技术,且其中部分技术比较落后,则我们一般不愿意升级该单体应用,因为升级的成本很大:要考虑整个系统的所有引用方是否有问题;要验证当前架构的适应度:即使强制升级了,还需要花费大量的时间进行全盘测试验证,灰度发布一段时间后才能正常使用。
正因为单体系统存在14.1节所述的诸多问题,所以,企业在发展到一定阶段后需要对应用进行拆分,即将单体架构改为分布式架构。
1、理解分布式架构
分布式架构是指,将相同或者相关的应用放在多台计算机上运行,以达到分布式计算的目的
通俗来讲,分布式架构就是将一个系统拆分为多个独立的应用,然后它们互相协作组成一个整体,共同完成任务.
分布式架构用来解决单体系统复杂的痛点。
早期的分布式架构就是将应用程序、文件及数据库从原有的单台服务器部署到多台不同的服务器,如下图。
分布式架构是包含了多个应用的架构,其中每个应用对应一个独立的业务线。如果其中一个应用需要和另一个应用进行交互,则可以通过对方提供的 API来进行交瓦。
通过下图可知,分布式架构就是将单体系统按照公司业务线进行横向划分,将复杂的逻辑拆分成了一个个的小问题。这样业务边界更清晰,可以解决开发团队的协作问题。
2、分布式架构局限性
分布式架构也有局限性,例如:
●开发者在开发应用时,需要考虑当前应用的API模块。因为如果因为业务需要更改了相关底层逻辑,则这种修改会影响API模块,所以需要对 AP 模块也进行对应逻辑的修改,否则已经在调用的服务会出现调用错误,影响线上产品。
●外部的服务需要依据自己的业务向服务提供方提出相应的小需求。服务提供方可能只是改动了 API 模块,但是从整体来说则需要测试并重新部署一遍,影响服务的稳定性
在分布式架构下,很可能出现很多业务功能的重复开发,即所谓的“重复造轮子”造成开发资源的浪费。
由此可见,分布式架构系统更适合与业务关联性低耦合少的业务系统。例如,企业的内部管理系统(如进销存系统和CRM系统)适合搭建成分布式架构
正因为分布式架构存在142节所述的局限性,所以,需要对分布式架构进行审视和优化,对务边界进行更细颗粒度的划分(即微服务架构)。
1、理解微服务架构
微服务是一种流行的架构设计风格。微服务的概念最早在2014年由 Martin Fowler和James Lewis共同提出。他们定义了以下内容
●微服务是由单一应用构成的小型服务,拥有自己的进程与轻量化处理。
●微服务依据业务功能设计,以全自动的方式部署,与其他微服务使用 HTTPAPI进行通信。
●微服务会使用最小规模的集中管理技术,例如 Docker。
●微服务可以使用不同的编程语言和数据库
“微服务系统”是相对“单体系统”而言的,因为单体系统在面临复杂业务时显得有点“无力”
微服务系统就是将复杂的单体系统中的模块按照某种规则进行拆分,这些被拆分出来的模块被独立部署在相对较小的服务器集群上。独立部署的模块彼此之间使用远程调用的方式来完成整个业务的处理。这些被独立部署的模块就是微服务,而这样的应用架构就是微服务架构。
下图展示了微服务架构和单体架构在扩展及模块划分方面的区别
●外部的边框代表应用的边界,不同的形状代表不同的模块
●左侧为单体系统架构,所有的模块都在一个应用内。
●右侧为微服务架构,每个模块在自己的应用边界内
左侧的单体应用在扩展时,是采用整体应用扩展的方式;右侧的微服务架构在扩展时,采用的是以服务为单位的扩展方式。
通过上图我们能清晰地感受到微服务架构的灵活性这样我们就可以将系统内的核心业务辑和数据拆分出来整理为微服务。
例如,将系统底层的基础业务封装为共享微服务,将对外的逻辑编排封装成聚合微服务,将具体业务处理的部分封装成应用微服务,将基础中间件(如消息队列、缓存及消息推送等)封装成础服务。
这些微服务在具体落地时,需要采用去中心化的方式,并且使用轻量级的通信框架最后将它们打造成技术上轻量级、功能上细分的独立微服务
基于以上思路,我们能很容易地构建出微服务,并在此基础上像搭积木那样来搭建各种微服多形成一个大系统。这样搭建出来的系统更具有弹性且扩展性更强。
我们需要对微服务的依赖关系进行有效的管理,打造一个有序的微服务体系。否则,微服务会杂乱无章,整个系统会无规律且不清晰,难以维护和扩展。一个有序的服务体系应该是如下图这样的一一依赖关系清晰有序。
2 微服务架构特征
对于微服务架构,不同的人可能会有不同的理解,并没有统一的定义。那我们应该依据什么搭建微服务架构呢?应该去观察微服务架构所具备的特征,这些特征可以帮助我们确定选用何种服务架构。
(1)通过服务实现组件化。
软件行业一直期望通过拼插第三方组件的方式来搭建软件系统,即实现组件可插拔。在 Java应用开发中这种第三方组件通常是以 JAR 文件的形式出现的,Maven 仓库就提供了海量的第三方组件。
这种组件化的软件搭建方式就是将完整的系统分解为服务。这些服务独立运行在进程中,它们通过类似于HTTP这样的进程间的通信方式或者通过远程调用通信的方式进行通信。
把服务当作组件来使用的一个主要原因是服务能够独立部署。在使用API 规范来描述服务接口后,一个服务只能通过API的方式来访问另一个服务,但是无法访问服务的内部函数。服务之间的调用关系如下图所示,调用方式可以是 REST 方式,也可以是 RPC 方式。
(2)围绕业务能力来组织开发团队。
单体系统的开发团队通常是按照技能标准来划分的例如,分为前端组、服务端组及数据库组等,这样的组是被垂直分割的。即使一个小需求的改动都需要进行跨组的沟通.这是不利于项目正常发展的。
微服务系统开发团队是围绕着业务能力来划分的,个服务相当于一个小应用,对应着一个特定的业务需求
开发团队规模较小,包含开发人员、测试人员及运维人员等,如下图所示。负责该微服务的团队全权负责该微服务的所有需求实现与改动。这样的好处是:沟通成本小,开发效率高。
这种划分方式主要依据业务需求,且技术栈灵活,包括用户界面、持续存储,以及任意的外部协作。因此,这些组之间是跨功能的,包括开发所要求的所有技能,例如前端用户体验、数据库和项目管理。
(3)去中心化管理。
单体系统的开发团队一般采用统一的技术栈,这种单一的技术栈在具体实施过程中会有很多限性,因为没有一种技术栈能解决企业所有的问题。在特定场景下,单体应用能够发挥不同语言的优势,但这并不常见。
在微服务架构中,服务都是独立开发且独立部署的所以在开发时,对于每个服务都可以选择最适合的技术栈只需要定义好 API 契约即可。例如使用 Node.is 搭建单个报告页面;使用C++ 做一些基础服务(如人脸识别AI 工程服务等)。
Netflix 就是这种理念的践行者。它通过库的形式共享有用的、经过时间考验的代码,鼓励其他开发者以相似方法解决相似问题。这种共享库的形式通常可以解决一些通用的问题,比如数据存储进程间通信,以及基础设施自动化。
每个团队负责自己团队内部的服务:负责服务开发服务构建、服务测试及服务部署运维等工作,这在无形中提高了团队的主观能动性,开发人员归属感更强,也降低了管理的成本。
(4)去中心化数据存储
单体架构通常采用单一逻辑的数据库进行集中的数据存储,通常这种单一逻辑的数据库(例如 Oracle)的成本较高,且在面对复杂业务时很难进行弹性伸缩。
而微服务架构倾向于让每个服务管理自己的数据库(如下图所示),利用多个较低成本的数据库达到去中心化数据存储的目的,可以更好地应对复杂业务。
(5)基础设施自动化
单体应用由于所有代码都在一个项目中,部署时只有一个部署包,部署很方便,所以,它对自动化的要求并不是很高。
在微服务架构中,所有的服务都是独立部署的。当服务数量很大时,如果还是像单体应用那样进行人工部署则效率会非常低,所以,微服务架构必须要通过自动化的方式来部署。现在,持续集成及持续部署都是最通用的实践了。
(6)充分考虑故障。
微服务之间是通过进程间通信进行交互的,这样的交互方式天生就容易失败。因此,微服务入定就是“银弹”微服务架构也不是解决所有应用问题的万能钥匙。
3微服务架构的问题
总的来说,微服务架构会带来以下问题
(1)增加了复杂度
从单个应用演变到多个应用,不仅会带来服务数量的增加,也会带来交互模式的变更(2)服务间的通信会变得复杂。
引入微服务架构后,系统变成了一个分布式系统。在分布式系统中,应用之间是通过进程进行通信的,例如采用REST或RPC框架调用的方式进行通信。这种通信方式要求调用端需要有完的策略以应对服务调用不成功的情况,因为服务调用者先得明确知道服务提供者具体部署在哪台机器,然后找到对应的机器进行通信。同时,如果服务提供者出现异常或宕机,则服务调用者如何实时感知等是很复杂的。
(3)在落地微服务时,微服务边界的划分增加了实现的复杂度。
微服务边界划分是微服务设计的难点,划分的好坏直接决定着整个微服务架构的好坏,会影响整个产品的推进度:
如果服务颗粒度划分得过粗,则随着业务复杂度的上升,系
统又会是一个庞大的单体应用。
如果服务颗粒度划分得过细,则会出现数量很多的服务,这样会增加服务运维及监控的本,过多的服务会使得调用链变得复杂,直接影响整个系统的性能。在划分完一段时间后如果觉得不合适,则重组服务的工作量巨大且风险很大(4)保持数据一致性非常复杂。
微服务架构中的服务可能使用的是不同的数据库,包括关系型数据库和非关系型数据库,要在这些数据库之间保持数据一致性是非常复杂的。
在单体应用中,需要通过数据库事务的 ACID 特性来保证数据一致性;而在微服务架构中,通常只需要保证数据的最终一致性。
(5)对运维团队和开发团队都提出了更高的要求因为每个服务都有自己的实现方式,且服务数量很多,所以,运维团队不仅需要维护种类繁的数据库和消息中间件,还需要应对持续集成和持续部署的挑战,开发团队需要具备微服务开发经验,开发人员的成本会随之提高。
现在出现了很多的云平台,以及Kubernetes 和 Docker等技术,运维团队可以更好地管理微服务
(6)开发流程复杂。
建议将微服务架构的团队按照服务来划分团队,每个团队负责一个或多个微服务的开发、测试构建及运维等但是在开发流程上,瀑布式的开发流程并不适用于微服务开发,微服务开发应该采用敏捷软件开发流程。
如上,我们知道了微服务架构会给开发带来一些复杂性。所以,我们要真正地理解微服务的各种特征,只有理解透了,在落地时才不会出现各种错误。