转载

导致你的微服务走向失败的11个原因

在过去的几年里,我对多个正在进行数字化转型的产品团队进行了架构审查。发现大多数团队都会使用微服务架构来构建产品,他们使用微服务架构的意图都是正确的:更快的开发速度、更好的可扩展性、更小的独立团队、独立的部署、使用合适的技术来完成工作等等。但大多数时候,我发现团队在使用微服务时都很不顺利,他们没能利用微服务的优势。在这篇文章中,我将分享导致你的微服务走向失败的11个原因。

对于刚接触微服务的人来说,我推荐阅读 Martin Fowler 的 《微服务》 一文。文章中对微服务架构定义非常好。

微服务架构风格这种开发方法,是以开发一组小型服务的方式来开发一个独立的应用系统。其中每个小型服务都运行在自己的进程中,并经常采用 HTTP 资源 API 这样轻量的机制来相互通信。这些服务围绕业务功能进行构建,并能通过全自动的部署机制来进行独立部署。这些微服务可以使用不同的语言来编写,并且可以使用不同的数据存储技术。对这些微服务,我们仅做最低限度的集中管理。

原因一:管理层低估了微服务开发的复杂性

我曾与多个非常看好微服务客户合作过,对他们来说,微服务是解决他们所有问题的 银弹 。在我看来,大多数团队及其管理层都低估了微服务开发的复杂性。

要开发微服务,开发人员首先需要一个高效的本地开发环境。

当你的系统中的服务开始增加时,在一台机器上运行应用程序的多个子程序就会变得很困难。特别是当你使用像 Java 这样消耗相对较多内存的语言来构建应用程序时,这种情况就更容易发生。

以下是关于本地开发环境配置的相关要点:

docker-compose
Telepresence

如果一个团队不了解微服务开发的复杂性,那么随着时间的推移,团队的开发速度会明显下降。

原因二:没有将依赖和工具更新到最新版本的程序

在架构审查时我发现有些新的平台居然已经出现了遗留问题。团队没有确保依赖包版本最新,也没有确保数据库等工具处于最新版本。所以,两年前才开始的项目到今天发现居然已经欠了几个月的技术债了。

很多团队在几年前就开始使用 Spring Cloud Netflix OSS 项目构建微服务。而现在他们开始使用像 Kubernetes 这样的容器调度工具,但由于他们之前使用的是 Netflix OSS,所以他们并没有使用 Kubernetes 提供的所有功能。当 Kubernetes 内置了服务发现功能后,他们仍然在使用 Eureka 作为服务发现。另外,使用 Istio 这样的服务网格,使你可以摆脱 Netflix OSS 提供的大部分功能。这有助于降低代码复杂性,而将更多很复杂的问题交给基础设施平台来解决。

还有一点要记住,就是要保持所有服务的依赖版本同步。我最近在帮助一个使用 Spring Boot 构建微服务的客户,在过去两年中,他们已经构建了 20 多个 Spring Boot 服务。在他们的环境中,他们使用的 Spring Boot 版本从 1.5 到 2.1 不等。这意味着,当有人配置他们的机器时,他们必须下载多个版本的 Spring Boot。此外,他们还缺少了 Spring Boot 自 1.5 版本以来的许多改进。

我们的建议是,各团队应为这些升级问题设立专门的技术债务项目。这些技术债务项目应该作为架构委员会会议的一部分进行讨论并定期解决。在我的上一个项目中,我们每三个月进行一次一周的 sprint,来将所有的依赖项更新到最新版本。

同时,团队也应该投入时间将数据库、消息队列和缓存等工具升级到最新版本。

译者按:这里的最新版本应该是最新的稳定版本,即 stable 版本,而不是 latest 版本。

原因三:在本地开发中使用公共服务

由于本地开发效果的不理想,大多数团队开始依赖共享环境来提供关键服务。其中第一个就是数据库,大多数年轻的开发者并没有意识到基于数据库的共享开发并不好。以下是我认为共享数据库主要的问题:

  1. 团队成员需要建立一个约定,以免一个开发者把另一个开发者写的数据抹掉。这种工作方式是痛苦且容易失败的,迟早会拖累整个团队。
  2. 导致开发人员害怕实验性测试,因为他们的工作会影响到其他一些团队成员的工作。我们都知道,更好的学习方式是实验和快速反馈。有了公共数据库后,实验就会变得十分困难。
  3. 另一个副作用是,独立地测试变化变得困难。这使你的集成测试变得很不稳定,从而进一步降低了开发速度。
  4. 公共数据库要像对待宠物一样对待,因为你不希望它出现哪怕一个不一致的、不可预测的状态。你可能会有一个开发人员想在表是空的时候测试边缘情况,但其他人则需要这个表的记录。
  5. 只有公共数据库才有系统工作所需的所有数据。团队成员会随着时间的推移失去了更改的可追溯性,所以没有人知道他们是如何在自己的机器上复制同样的设置的。唯一的办法就是将完整的数据库转储,并与它一起工作。
  6. 当你没有连接到网络时,很难工作。这种情况一般发生在长时间的通勤或坐飞机时。

数据库只是公共服务的一个例子,它也可以是消息队列、像 Redis 这样的集中式缓存或者其他任何一个服务可能变化的服务。

解决这个问题的最好方法是让开发人员在自己的机器上运行数据库(使用 docker 运行),并创建 SQL 脚本来设置模式和初始化主数据。这些 SQL 脚本应该像其他代码一样被保存在版本控制中,并像其他代码一样进行维护。

原因四:版本控制托管平台缺乏可视性

我的一个客户,他们使用的是 Gitlab 版本控制平台,其中有 5 个产品,1000 多个版本库,每个产品都由多个微服务组成。而我问他们的第一个问题是:哪些服务和代码库是产品 A 的一部分。

解决这个问题的最好方法是在一开始就以某种方式对你的微服务进行分组,这样你就可以随时了解你的产品生态系统。Gitlab提供了创建组的方式,然后在其中创建项目仓库。Github 没有分组功能,你可以使用主题或命名惯例来实现。

我个人更喜欢单库,因为我觉得真的很方便。而大多数开发者都认为这是一种反模式。我同意 Dan Lua 的帖子 ,他在帖子中提到了以下好处:

  • 简化的组织结构
  • 简化了依赖性
  • 工具化
  • 跨项目变更

原因五:没有明确的服务定义

大多数团队都不知道如何划分微服务。围绕微服务的划分,产生了很多混乱和迷惑。让我们举个例子,你的应用程序有一个类似于插件的机制,将与多个第三方服务进行集成。每个集成是否应该是一个微服务?我见过多个团队走的是每个集成都要创建一个微服务的道路。随着集成数量的增加,这种做法很快就变得难以管理。这些服务通常都太小了,以至于它们作为一个单独的流程来运行会增加更多的开销。

我认为大服务少,总比小服务多好。在一个企业组织内创建一个部门的模型,按照 DDD 的要求,将一个域分为子域和有边界的内容。有边界的内容代表了公司内部的一个部门,比如财务和营销部门。你可能会认为这可能会导致出现大型的微服务,认为这样做是不对的。但是,根据我的经验,将单体重构到微服务总是比反过来更容易。随着你获得更多的信息,你可以进行更细粒度的微服务划分。你可以应用单一责任原则来了解你的微服务是否变得太大,做的事情太多,然后将其分解成更小的独立服务。任何服务都不应该直接与另一个服务的数据库进行连接,它们只应该通过公开的接口进行通信。你可以在 Microservices.io 网站上阅读更多关于 按子域模式分解 的内容。

我也遵循了 backendlore 文档中提到的建议。这个建议可以帮助限制服务与服务之间的通信,而这是导致基于微服务的系统中性能低下的首要原因。

如果两块信息是相互依存的,那么它们应该属于一个服务。换句话说,一个服务的自然边界应该是其数据的自然边界。

原因六:没有明确的代码复用策略

我的一个客户,他们在所有基于 Java 的微服务中都复制了四个与一个特定的功能有关 Java 文件。所以,如果在那个基础代码上发现了一个 bug,就需要修改所有的项目。而在时间压力下,会有些项目没有被修复。这样就会浪费更多的时间,同时也增加了挫折感。

并不是说开发团队都不知道怎么做是正确的,但组织架构总会让人们使用默认的容易出错的方式做事。

正确的方法是使用 Bintray 或 Nexus 这样的智能管理器,并在那里发布依赖关系。然后,每个微服务都应该依赖该库。当发布新版本的库时,所有的微服务都应该被更新和重新部署。

使用微服务并不意味着你要抛弃迄今为止对我们有效的最佳实践。你需要在构建工具方面投入更多精力,让微服务升级变得容易且自动化,这样就不需要人手工去做这些事情了。

在没有合适的工具和自动化的情况下使用微服务是灾难性的。

原因七:多语言方案

我发现有的团队以最佳实践的名义使用多种编程语言、多种数据库、多种缓存。这一切在项目的初始阶段都是可行的,但当你的产品投入生产后,弊端就会展露。就像我们在使用 Java 的 Spring Boot 框架构建应用,但当我们意识到 Java 消耗的内存比较多,性能也比较差时,就决定改用 Node.js。这个理由是站不住脚的:

  1. Node.js 的性能比 Java 更好。 如果你有基于 IO 的工作负载,Node.js 的性能通常会更好。但在任何计算密集型的工作负载上,Java 都能 击败 node.js。通过调整,也可以使 Java 对 IO 工作负载有更好的性能。Spring Boot Reactor 在 IO 工作负载方面的性能就相当于 Node.js。
  2. Node.js 的内存消耗比 Java 少。 这是部分事实,因为 Node.js 应用程序通常比 Java 消耗的内存少。Java 的 Spring Boot 应用程序并不像大多数人想象的那样糟糕。我在其中一个 Spring Boot Java Microservice 上运行了一个加载测试,内存消耗仍然不到 1GB。你可以通过 OpenJ9 JVM,限制类路径上的依赖性,以及调整默认的 JVM 参数来优化 Java 的内存利用率。另外,在 Java 中还有一些新的 Spring Boot 的替代品,如 Micronaut 和 Quarkus,其内存消耗相当于 Node.js。
  3. Node.js 比 Java 更有生产力。 这取决于写代码的开发人员。使用静态分析工具的 Java 可以帮助在开发生命周期的早期发现问题。

大多数时候,这一切都取决于具体情况。如果你的开发人员不成熟,那么无论你使用什么编程语言,你都会开发出糟糕的产品。

我建议一个公司公布一个团队可以使用的语言列表。我认为 2-3 种语言是个不错的数字。另外,请列举一下为什么应该使用一种语言而不是另一种语言的原因。

在选择语言之前,你应该考虑多种因素:

  1. 是否容易找到成熟的企业级软件开发者?
  2. 重新培训开发人员学习新技术是否容易?我们发现,Java 开发人员相对来说比较容易学习 Golang。
  3. 初始团队之外的开发人员是否能够轻松地维护他人编写的代码?
  4. 工具和依赖库方面的生态体系是否成熟?

这不仅限于编程语言,也适用于数据库。如果你的系统中已经有 MongoDB,那么你为什么要在你的系统中再引入 ArangoDB 呢?它们都主要是文档数据库。

原因八:人的依赖性

这不是微服务所特有的,但在微服务生态中却变得更加猖獗。原因是大多数团队都专注于自己的具体服务,所以他们并不了解整个系统。在与不同客户的合作中,我发现只有一小部分架构师了解整体情况。但这些架构师的问题是,他们在日常工作中并不活跃,所以他们对开发的影响也是有限的。

我认为最好的办法是确保所有的团队都有一个成员来自架构组,这样他们就可以使自己的团队与整个架构团队的路线图和目标保持一致,扁平化的管理有助于整个团队的成熟。

原因九:缺乏文档

过去的几年中,我接触的大多数团队都在文档方面挣扎。很多开发人员和架构师要么不写文档,要么他们写的文档没有用。即使他们想写,也不知道应该如何记录他们的架构。

我们至少应记录以下内容:

  1. 设计文件
  2. C4 模型 中的内容图和容器图
  3. 以架构决策记录的形式跟踪关键的架构决策
  4. 开发人员入职指南

我建议所有的文件都要在版本控制系统中进行维护。

原因十:功能盖过平台成熟度

这个原因我在其他地方已经简单地提到过,但我认为这个原因值得作为一个顶层原因来提及。微服务比传统的单体应用要复杂得多,因为你正在构建一个分布式系统,里面有很多组件,而大多数开发人员还不能理解系统的不同故障模式。大多数微服务在构建时都太过乐观,如果管理层过早的专注于业务功能,而忽略了系统平台本身的成熟度,那么必将失败。在一个薄弱的平台上构建的功能是无法提供价值的。

企业需要进入平台思维。平台思维并不只是指使用容器和 Kubernetes。它们是解决方案的一部分,但其本身不是完整的解决方案。你需要考虑分布式跟踪、可观察性、混沌测试、函数调用与网络调用、安全服务与服务之间的通信、可调试性等问题。这需要认真的努力和调试,建立成熟可靠的平台和工具团队。

如果你是一个资源有限的初创企业,我的建议是重新思考你的微服务战略。请你明白你正在进入的是什么。

原因十一:缺乏自动化测试

大多数团队都知道自动化测试对产品的整体质量有多重要,但他们仍然没有做。微服务架构为测试的地点和方式提供了更多的选择。如果你不做彻底的自动化测试,那么你将会失败得很惨。

关于这一点,我就不多写了,因为网上很多文章都有涉及该问题。下图是我从 Martin Fowler 网站上发表的 《微服务测试》 文章中摘录的,讲的是基于微服务系统的测试金字塔。

导致你的微服务走向失败的11个原因

原文地址: https://medium.com/xebia-engin ... 6268b

原文  http://dockone.io/article/10008
正文到此结束
Loading...