购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

1.3 架构设计模式及其失效分析

到目前为止,我们已经了解了影响代码设计的底层编程模式以及各种选择的利弊和取舍,但如果应用程序的上下文发生变化,你可能依旧能接受对这些底层设计做对应的修改。下文将着重讨论架构设计模式:这些模式由于贯穿组成你的系统的多个服务,因此很难做变更。我们先要讨论的是微服务架构,这是当今创建软件系统最通用的模式之一。

微服务架构与单体系统(单体系统在创建时,所有的业务逻辑都需要在单一系统中实现)相比,有诸多的优势。不过,微服务架构也带来了不可忽略的维护开销以及日益增加的复杂性。我们先从微服务架构与单体系统的根本优势入手,了解二者的区别。

1.3.1 可扩展性与弹性

我们创建的系统需要有能力处理海量的数据,同时,它们还要能按需伸缩。如果系统的一个节点每秒可以处理 N 个请求,流量暴增时,微服务架构允许你快速横向扩展(水平扩展)来满足业务需求(见图1.3)。当然,系统需要以支持容易扩展的方式编码,也需要使用底层的组件。

图1.3 横向扩展意味着可以通过向资源池中添加更多的机器来满足增长的业务需求

举个例子,为了让你的系统有能力每秒处理2 N 个请求(这里的2表示服务的数量, N 表示单个服务可以处理的请求数),你可以增加一个原微服务的新实例。不过,要想达到期望的效果,底层的数据访问层需要具备向上扩展的能力。

当然,扩展性会有上限,当它达到上限之后,增加新节点也不一定能带来太大吞吐量的提升。性能瓶颈可能出现于数据库、消息队列、网络带宽等达到了扩展的上限时。

不过,就整体而言,与单体系统相比,微服务架构的扩展要容易得多。在单体系统中,一旦某些资源达到上限,几乎不可能做快速扩展。

你可以通过为计算实例添加更多处理器、更大的内存或者磁盘容量来垂直扩展(通常称为纵向扩展)你的应用,同样地,这种方式也存在一定限制,达到上限之后很难继续提升性能。举个例子,有个单体应用部署到云端,部署时选择更强劲的云计算实例类型(更多的处理器或者更大的内存)就可以纵向扩展,提升其性能。如果能够增加更多的资源,这种方式显然是有效的。然而,云计算实例也存在资源上限,某些时候,云计算提供商可能也无法提供更强劲的机器。这时,横向扩展的灵活性优势就体现出来了。如果你的应用在设计和实现时考虑了支持部署到 N 个实例,就可以通过部署更多的实例,为服务提供更高的总吞吐量。

1.3.2 开发速度

在微服务架构中,工作可以比较容易地拆分到多个团队。举例而言,团队A可以创建一个独立的微服务,专注于业务功能的开发。与此同时,团队B在业务领域的另一个部分开展工作。这两个团队能互不干扰地独立工作,并可由此进行快速的迭代开发。

采用微服务架构,团队之间不需要在代码库层面进行协作。各团队可以按照自己的需求选择技术栈,快速演进开发。加入团队的新成员也不需要掌握整个业务领域的内容,只需要了解其团队负责的部分领域,这样可以更容易地理解系统,快速开展工作。

由于每个团队都能独立地部署自己的代码库,部署流程会更健壮。所有这一切的结果是部署的频度会越来越高,风险也越来越小。即使团队偶然引入了某些缺陷,对部署的影响也比较小。因为变更的内容少,调试定位潜在问题也更快。调试过程中可能遇到困难,譬如多个细粒度的微服务集成时报错。在这种情况下,我们需要进行请求跟踪,收集多个微服务之间的请求调用关系。

与此相反,在单体系统中,多个团队的成员经常要共享同一个代码库。如果某应用的代码存放在某个代码库中,由于应用比较复杂,多个团队同时在这个代码库上开展工作。这种情况下,你很可能遇到代码冲突的情况。一旦出现这种情况,大量的开发时间都会消耗在解决这些冲突上。当然,如果产品代码能够以模块化的方式组织,在一定程度上可以降低冲突的概率。然而,随着越来越多开发者的加入,产品主分支上的变更会越来越频繁,你也不得不隔三岔五地做代码同步。对比单体系统和微服务架构,我们很容易发现固定业务领域的代码通常少得多。因此,采用微服务架构时出现代码冲突的概率小得多。

单体应用的部署一般不太频繁,主要原因是每次部署都需要向主分支合并大量的功能代码(因为有更多的人在其上开展工作)。功能越多,完成相关测试所需的时间越长。同一个版本包含的功能越多,引入缺陷的概率越大。值得一提的是,通过创建稳定的持续集成(或者持续部署)流水线,这些痛点在一定程度上可以得到缓解。我们可以通过更频繁地运行这些流水线,更快速地构建新应用,从而让每个新版本包含更少的新功能。如果新版本代码引入了缺陷,这样也将更易于分析和调试。版本中包含的新功能越少,定位潜在问题的速度越快。如果我们做一个对比,即在同样的时间内,在一个发布周期中将频繁构建新应用与不频繁构建新应用的方式做比较,会发现前一种情况下发布最终部署到生产的特性数比后一种情况要多得多。一个版本包含的功能越多,潜在的问题越多,也越难调试。

1.3.3 微服务的复杂性

微服务架构是一种复杂的设计,它包含多个组成部分。如果你有合适的负载均衡器,可以很容易地实现扩展;负载均衡器维护多个运行服务的列表,并为流量进行路由。底层服务可以纵向扩展,也可以收缩,这意味着服务可以按需创建和销毁。变化的跟踪是一个非常关键的问题。为了解决这一问题,我们引入了一个新的服务注册组件,如图1.4所示。

图1.4 微服务架构中的服务注册组件

每个微服务都需要一个运行的注册客户端,该注册客户端负责向服务注册表注册该服务。一旦完成注册,负载均衡器就开始向新的实例转发流量。服务注册表会对服务实例进行健康检测,若发现服务实例出现问题则执行解绑操作。这是微服务架构所面临的诸多技术挑战之一,也导致部署变得越来越复杂和困难。

了解了方案的优势及劣势,你还需要了解项目的实际情况,这对一个好的设计决策来说至关重要。如果你的项目对可扩展性没有很高的要求,同时团队规模也不大,采用单体系统可能是一个明智的决策。本书的每一章都会遵循相似的流程来评估设计决策:找到每种设计的优势及劣势,结合项目的上下文,解决困惑,即什么情况下哪种设计是更优的决策。

本章中,我们向你介绍了一个设计取舍的例子,在什么环境下如何做取舍是本书试图回答的核心问题。通过本章,你应该了解了如何为你的应用选择恰当的单元测试与集成测试比例,及其底层的利弊取舍。我们也讨论了像单例模式这样久经考验的解决方案不一定是最合适的选择,我们需要结合实际的使用场景进行判断。譬如,如果你的系统采用多线程的环境,采用单例模式时可能会由于线程竞争导致一定的性能问题。最后通过高层设计选择的例子,我们对微服务架构与单体系统设计模式进行了对比。

第2章中,我们会讨论代码重复与复用之间的取舍。我们认为代码重复不一定都是反模式的或者是坏事,同样,做判断时我们需要充分结合上下文。 DxLXz+EOlKAgyN5sOK/J23RhJcVZ3wCK0fcYIEwUBAIcJiFrw2E4upjKafnaQQlw

点击中间区域
呼出菜单
上一章
目录
下一章
×