Skip to content

第01讲:什么是微服务架构


本课时我们将介绍微服务架构。


相信你也听说过微服务(MicroService)这个词,微服务架构是一种流行的架构设计风格,通常用作单体(Monolith)架构的一种替代方案。市面上关于微服务的图书、教程和资料层出不穷,这在很大程度归功于大厂商的市场推广,微服务和很多年之前的 SOA 一样,注定会成为一个广泛讨论的话题。从好的方面来说,这保证了微服务相关的技术有足够大的市场,不会被轻易淘汰;从坏的方面来说,错综复杂的信息会让人无所适从,不知道从何入手开始学习微服务架构中与技术相关的内容。


目前,我们开发的大部分应用都是单体应用。当单体应用的复杂度增加时,会出现一系列的问题。微服务架构吸引人的地方在于它对复杂应用的开发提供了一种新的解决方法。微服务架构的核心思想是把应用按照功能划分成多个独立的服务,每个服务都是独立运行的应用。


如下图所示,外部的边框是应用的边界,不同的形状表示不同的单元。图中左侧表示的是单体应用,所有单元在同一个应用的边界内。在进行扩展时,单体应用只能整体扩展;右侧表示的是微服务架构的应用,每个单元在自己的应用边界内。在进行扩展时,微服务架构的应用以服务为单位进行扩展。不同服务在运行时的实例数量可以根据负载动态进行调整。



在介绍微服务架构之前,有必要先看一下单体应用存在的问题,从而了解微服务架构要解决的问题。

单体应用的问题

初学 Java,我们就知道 main() 方法的作用,以及如何使用 Java 虚拟机来运行一个应用。这样的应用就是单体应用。单体应用是我们最熟悉的开发应用方式,从开发、测试、部署到维护的全过程,积累了很多经验。但是当单体应用不断更新升级和扩充功能时,一些问题就会出现。


**第 1 个问题是单体应用过于复杂,超出了单个开发人员的理解能力。**虽然可以通过组件化把单体应用划分成多个单元,降低每个单元的复杂度,但在实际开发中,组件化的效果并不是很好。一方面是因为公共代码的存在,这些共享的代码由于被多个组件使用,难以高效更新;另一方面由于文档缺失和开发过程中的各种不规范行为,造成组件之间的接口不清晰。在 Java 代码中,我们可以很容易地调用其他组件中的接口和类的方法,从而在不同组件之间有意或无意的引入依赖。所产生的结果就是单体应用中不同组件之间的依赖关系非常复杂。


**第 2 个问题是缓慢的开发速度。**当应用变得复杂时,开发人员则需要花费更多的时间来理解所做的改动对已有代码的潜在影响。经常遇到的情况有,一个看似很小的改动,会对应用造成很大的影响。当这样的情况出现多次之后,开发人员由于怕承担责任,变得不情愿改进代码的质量,同时,在本地开发环境上进行开发和调试变得更加耗时。在 IDE 中修改代码之后,编译和重启应用的时间过长,本地单元测试也需要更长的时间来运行。所产生的结果就是开发人员的宝贵时间耗费在无意义的等待上。


第 3 个问题是应用的扩展变得很困难**。**当应用的处理能力不能满足业务需求时,需要进行扩展,扩展分为垂直扩展和水平扩展两种。垂直扩展的做法是增加单个应用实例所能使用的资源,这导致的问题是不同组件对资源需求的冲突,有些组件对内存的消耗较大,而另外一些组件对 CPU 的要求高,当无法同时满足两者时,则需要作出取舍。水平扩展的做法是增加应用的实例数量,水平扩展只能以应用为单位来进行,如前面的图片所示,应用中不同组件的负载程度是不同的,以电子商务应用为例,商品展示和订单处理相关组件的负载要大于客户服务相关的组件,以应用为单元的扩展方式无法有效的分配资源。


第 4 个问题是新版本更新上线的速度变慢**。**现在的应用都要求能够及时响应用户的需求,以最快的速度添加新功能和修复问题,这意味着一天可能要进行很多次的产品更新。单体应用由于复杂度高,每一次代码提交之后的持续集成所花费的时间过长。由于需要运行全部的测试用例,限制了更新的频率。


**第 5 个问题是整个应用的稳定性变差。**由于整个应用只有一个进程,组件之间缺少必要的隔离性,任何一个组件中出现的问题,比如内存泄漏,都会导致整个应用的崩溃。当某个组件占用大量的 CPU 和内存资源时,会导致其他组件由于资源不足而无法正常工作。


第 6 个问题是技术栈的选型和更新变得困难**。**单体应用通常只使用单一的技术栈,包括编程语言、所用框架、第三方库,以及数据库和消息中间件等,这就要求所有的开发人员都掌握相同的技术栈,事实上,不同组件由于需求的不同,有它最适合的技术栈。强制使用单一技术栈,无疑会对开发效率产生影响,一旦技术栈选型确定之后,要对它进行更新是一件非常困难的事情,整个应用都可能受到影响。所产生的效果就是应用的技术栈不断的老化,带来更多的问题,形成恶性循环。


如果你的单体应用遇到了上述问题,则说明该应用的架构到了需要调整的时候,微服务架构是一个值得考虑的解决方案。下面我们来看一下微服务架构到底是什么。

微服务架构的特征

对于微服务架构,并没有一种统一的定义,不同的人有不同的表达形式。Martin Fowler 是这样表述的:微服务架构风格把应用划分成若干个服务,每个服务有自己独立的进程,服务之间通过轻量级传输机制进行交互。这个表述包含了微服务架构的一些重要特征。事实上,我们关注更多的并不是微服务架构的定义,而是它所具备的特征,这些特征可以帮助我们确定是否应该选用微服务架构。


微服务架构使用服务作为组件化的单元**。**组件化是软件开发中的基本实践,在 Java 应用开发中,组件通常以 JAR 文件的形式出现,Maven 仓库中包含了海量的第三方库可供使用,Java 开发人员都熟悉这种使用组件的方式。在微服务架构中,组件的单元变成了服务,服务运行在独立的进程中,不能通过直接的方法调用来访问,而需要使用类似 HTTP 这样的进程间通信方式,每个服务可以独立部署,使用 API 规范来描述其公开接口。一个微服务只能通过 API 访问另外的微服务,并不能访问内部的实现代码。


如下图所示,不同服务之间存在调用关系,调用的方式可以是 REST 或 gRPC。



微服务架构的开发团队围绕业务能力来组织**。**单体应用的开发团队通常按照技能来划分,一个典型的 3 层应用开发团队可能分成前端开发、后端开发和数据库管理等小组。微服务架构的开发团队以服务为单元来组织,每个服务与特定的业务需求相对应。服务的开发团队规模较小,包含开发、测试和 DevOps 相关的全部人员,负责该微服务的团队对该微服务的实现可以全权负责。较小的开发团队意味着更少的沟通成本和更高的开发效率。


微服务架构使用去中心化的管理模式**。**单体应用的开发团队通常会对使用的技术栈做出限制,要求整个团队使用统一的技术栈。这种方式的弊端在于,没有一种技术栈适用于解决所有的问题。微服务架构中的服务都可以独立部署,这就意味着每个服务在实现时可以选择最适合的技术栈,只需要满足服务的 API 契约即可。每个团队自主管理所负责的服务,不但负责构建,还同样负责运行和维护,这在无形中提高了团队的主观能动性,同时降低了管理的开销。


如下图所示,每个微服务都有对应的团队,而每个团队中都有各种角色的人员。



微服务架构使用去中心的数据存储**。**单体应用通常使用单一数据库来存储数据,微服务架构中的服务通常有自己专有的数据存储,如下图所示。这些存储方式的实现可能各不相同,只包含服务所需的数据。



微服务架构强调基础设施的自动化**。**持续集成和持续部署都是通用的实践,单体应用由于只有一个部署单元,对自动化的要求并不高。微服务架构中的服务可以独立部署,但当服务的数量较多时,必须通过自动化的流程来完成。


**微服务架构在设计时充分考虑到失败的情况。**这是因为服务之间通过进程间通讯方式进行交互,这样的交互方式天生就容易失败,调用时的目标服务可能失败或负载过大。


正如《人月神话》中提到的"没有银弹"一样,微服务架构也不是解决所有应用问题的万能钥匙。下面我们看一下微服务架构带来的问题有哪些。

微服务架构的问题

微服务架构最大的问题是其实现的复杂性**。**从单个应用对多个应用的变化,带来的不仅仅是数量上的增加,而是交互模式的变化。使用微服务架构之后,应用变成了一个分布式系统,在分布式系统中,服务通过进程间通讯方式来交互,如使用 REST API 或 gRPC 远程方法调用。这样的通讯方式,要求调用者使用更复杂的策略来处理可能出现的错误。当需要调用一个微服务时,首先需要通过服务发现机制找到微服务的实际地址,然后再发送调用请求。目标微服务有可能处于离线状态,或者因为负载过大而延迟过高,对于不同的情况,需要有相应的处理策略。


微服务架构的复杂性还体现在架构设计时。设计时最重要的问题是如何划分微服务,划分的好坏将直接影响整个应用的可维护性。如果划分的微服务粒度过大,则随着微服务的发展,它们的复杂度可能等同于传统的单体应用;如果划分的微服务粒度过小,则过多数量的微服务不但增大了运维的成本,也会影响系统性能,增加开发的复杂度。一旦微服务划分完成之后,在服务之间转移功能将变得非常困难。如果需要把一个服务的功能转移到另外一个服务上,不仅需要对这两个服务的 API 契约进行修改,还可能会影响其他服务。


微服务架构中的服务可能使用的是不同的数据存储,包括关系型数据库和 NoSQL 数据库,要在这些数据存储之间保持数据一致性非常复杂。在单体应用中,我们可以通过数据库事务的 ACID 特性来保证数据一致性,在微服务架构中,通常的选择是保证数据的最终一致性。


微服务架构对于系统运维也提出了更高的要求。每个微服务可能有不同的实现方式,运维团队不仅需要维护种类繁多的数据库和消息中间件,还需要应对持续集成和持续部署的挑战。幸运的是,随着阿里云这样的 PaaS 平台出现,以及 Kubernetes 和 Docker 等的流行,运维团队有了更好的技术和工具来管理微服务。


微服务对开发团队的组织也有影响。在传统的单一应用中,开发团队按照组件划分成多个小团队,由于组件之间的联系比较紧密,小团队之间沟通比较多,有些改动甚至需要全部团队参与,这使得团队之间的沟通成本变大,大量的时间都花在了沟通上,降低了开发效率。微服务架构强调团队的自主性,每个团队负责一个或多个微服务的开发、测试、部署和运维,他们的规模都比较小,方便内部沟通。在开发流程上,瀑布式的开发流程并不适用于微服务,微服务开发应该采用敏捷软件开发流程。


上述这些问题是由微服务架构的特征所带来的,这些问题在实际的开发中都有相应的解决方案,本专栏也会对这些方案进行介绍。

微服务架构的实现

微服务架构所涵盖的内容非常广泛,对不同角色的人员,其意义并不相同:

  • 从架构的角度来说,它是由多个独立服务组成的分布式系统;

  • 从人员管理的角度来说,它要求员工组成小而完备的自主团队;

  • 从项目管理的角度来说,它推荐使用敏捷软件开发流程,如 Scrum 或 Kanban;

  • 从开发的角度来说,每个服务有独立的业务逻辑实现和数据存储,使用开放 API 作为边界;

  • 从测试的角度来说,需要对服务的 API 契约进行测试;

  • 从运维的角度来说,持续集成和持续部署对微服务架构的成功至关重要。


微服务架构的实践是一项系统化的工程,需要很多人的协同合作。作为开发人员,我们更多关注的是如何完成服务的实现,但除了每个服务本身的实现之外,还包括与其他服务的协作。


从实现的角度来说,我们需要考虑表中的这些因素。



表中列出的关于服务实现的相关内容,在大部分微服务架构的应用中都会出现。但在实际的项目开发中,你并不会从零开始实现所有相关的内容,而是使用已有的平台、框架和技术,流行的技术选择包括 Netflix OSS 栈、Spring Cloud 和 Kubernetes 等。


Netflix 是微服务架构实践中的引领者,不仅在生产系统中成功应用了微服务架构,还把相关的库和工具以开放源代码的形式共享出来,形成了 Netflix OSS 栈。


Spring Cloud 是由多个开源项目组成的开发套件,用来实现分布式系统中的常见模式,如配置管理、服务发现和断路器等,可以用来实现微服务架构的应用。它的优势在于提供了一个抽象框架,可以避免供应商锁定的问题,对于同一个模式,它可以切换底层的实现方式。Spring Cloud 本身是基于 Spring 框架的,对于一直工作在 Spring 框架上的团队来说,Spring Cloud 是一个不错的选择。


Kubernetes 是管理容器化工作负载和服务的平台,同时也是容器编排平台,微服务及其依赖的其他服务通常以容器的形式运行。Kubernetes 对表中的很多需求都提供了原生的解决方案,对于另外的一些需求则有开源实现,是运行微服务架构应用的良好平台。

本专栏包含的内容

微服务架构所包含的内容非常多,不可能在一个专栏中涵盖所有相关的内容,本专栏的侧重点在于微服务的实现,即如何实现单个服务,以及服务之间如何协作,上表中列出的与微服务架构实现相关的因素都会涉及。在具体的实现技术方面,微服务架构的应用将使用 Kubernetes 来作为底层平台,并使用 Istio 来作为 服务网格 的实现。


除了微服务实现相关的内容之外,本专栏还包含了测试和 DevOps 相关内容的简要介绍,主要是因为测试和 DevOps 与开发的关系比较密切,当你了解了一些相关的内容后,才会更有助于团队之间的合作。

总结

作为微服务课程的第一课时,着重介绍了微服务架构的基本概念。首先从单体应用的问题来阐述引入微服务架构的必要性,接着介绍了微服务架构的应用所具备的特征。微服务架构也不是解决所有问题的万能钥匙,本课时还介绍了微服务架构的问题。最后,介绍了实现微服务架构时需要考虑的因素和相关的平台、框架和技术。