13.1 思考微服务

13.1 思考微服务

到目前为止,我们都是将Taco Cloud开发为单个应用程序,它会构建为一个可部署的JAR或WAR。单个可部署的文件似乎是一种很自然的选择。毕竟,几十年来,大多数的应用程序都是这样部署的。即便可能会将应用程序拆分为多个模块进行构建,但最终我们还是形成一个JAR或WAR,并将其投入到生产环境之中。

在构建小型、简单应用程序的时候,这当然是显而易见的方式。有意思的是,小型应用程序往往会不断增长。当需要新特性的时候,我们能够轻而易举地向项目中添加更多的代码。在我们发觉之前,它已经变成了一个复杂的单体应用,甚至有自己的思想。就像电影《小鬼怪》(Gremlins)里的Mogwai一样,如果你一直喂它,它最终会变成一个与你作对的怪物^1

单体应用看似简单,但是它会面临各种挑战,如下所示。

  • 单体应用难以理解:代码库越大,理解每个组件在整个应用程序中所担任的角色就越困难。
  • 单体应用难以测试:随着应用的不断增长,全面的集成和验收测试会变得更加复杂。
  • 单体应用更容易出现库冲突:实现某个特性所需要的依赖可能会与其他特定的依赖不兼容。
  • 单体应用的扩展较为低效:如果处于扩展的目的要将应用程序部署到更多的硬件上,那么我们必须要将整个应用部署到更多的服务器上,即便应用程序中很小的一部分需要扩展也同样如此。
  • 单体应用中的技术决策是针对整个单体应用的:当为应用程序选择语言、运行时平台、框架和库的时候,整个应用程序都会遵循我们的选择,即便我们所做的选择只是为了支持某个单独的用户场景时同样如此。
  • 单体应用需要大量的操作过程才能投入生产环境:当应用程序只有一个部署单元时,似乎更容易将其投入生产环境。事实上并非如此,单体应用程序的规模和复杂性通常需要更严格的开发过程和更周全的测试周期,这样才能保证所部署的应用程序是高质量的,才能避免引入bug。

在过去的几年间,微服务架构的出现致力于解决这些挑战。简而言之,微服务架构是将应用程序分解为可独立开发和部署的小规模、微型应用的一种方式。这些微服务之间互相协作,以实现更大的应用程序的功能。与单体应用程序架构相比,微服务架构有以下特点。

  • 微服务易于理解:每个微服务与应用程序的其他微服务之间有一个很小且有限的契约。因此,微服务更加专注于目标,作为一个单元,微服务更易于理解。
  • 微服务易于测试:事情越小,就越便于测试。当你思考单元测试、集成测试和验收测试的时候,这一点非常明显。它也适用于微服务与单体应用之间的测试。
  • 微服务较少受到库不兼容的影响:因为每个微服务都有自己的构建依赖项的集合,而这些依赖项不会与其他的微服务共享,所以不太可能会出现库冲突的现象。
  • 微服务能够独立扩展:如果指定的微服务需要更多的处理能力,那么内存分配和/或实例数量可以按比例增加,而不会影响整体应用中其他微服务的内存和实例数量。
  • 每个微服务可以选择不同的技术:每个微服务可以选择完全不同的语言、平台、框架和库。实际上,某个使用Java编写的微服务与另一个使用C#编写的微服务进行协作是完全合理的[^2]。
  • 微服务可以更加频繁地发布到生产环境中:尽管微服务架构的应用是由许多微服务组成的,但是部署每个微服务的时候,并不需要其他的微服务都已经部署就绪。而且,因为它们更小、更集中、更易于测试,所以将微服务投入到生产环境不需要那么多的繁文缛节。从产生想法到将其投入生产的耗时可以用分钟和小时计量,而不是用周和月。

显然,微服务能够让事情变得更简单。但是公平地讲,微服务架构并不是免费的午餐。微服务架构是一种分布式架构,有自己需要应对的挑战,包括网络延迟。在迁移至微服务架构时,我们需要记住这一点,因为很多的远程调用会累积并降低应用的速度。

你还要考虑是否应该将应用构建为微服务,因为并不是所有的应用程序都需要这种架构,或者说能从这种架构中受益。如果你的应用相对比较小或者比较简单,那么最初最好依然采用单体架构。随着它的不断发展,再考虑将其拆分为微服务。

在开发云原生、微服务架构的应用时,要考虑很多因素。本章和接下来的几章主要关注Spring Cloud所提供的技术,以开发由微服务组成的应用程序。如果你对深入研究云原生应用程序的设计和思想过程感兴趣,那么建议阅读Cornelia Davis的Cloud Native(Manning,2019)。

微服务架构所面临的另外一个常见挑战就是每个服务该如何知道它要协作的其他服务在哪里。这恰好是本章的主题。事不宜迟,我们马上看一下如何使用SpringCloud搭建一个服务注册中心。

[^2]: 在这里,我们会关注如何使用Java和Spring编写微服务。如果你对如何使用.NET编写微服务并与Spring Cloud服务交互感兴趣,那么可以参考一下Steeltoe。