微服务失败的11个缘由

在过去的几年里,我对进行数字化转型的多家产品团队进行了架构审查。大多数团队都是遵循微服务架构来构建产品。他们彻底有理由使用基于微服务的架构:更快的开发、更好的可扩展性、更小的独立团队、独立的部署、使用正确的技术来完成工做,等等。可是,我常常发现,团队在微服务方面举步维艰。他们未能充分利用微服务的优点。在本文中,我将分享个人观点,阐述团队在微服务方面为什么举步维艰的缘由。react


对于刚接触微服务的新手来讲,我推荐阅读 Martin Fowler 关于微服务的文章。我很喜欢这篇文章中提到的微服务架构定义。docker


微服务架构风格就是一种将单个应用程序开发成一套小型服务的方法,每一个应用程序都在本身的进程中运行,并与轻量级机制(一般是 HTTP 资源 API)进行通讯。这些服务是围绕业务功能而构建的,而且能够由彻底自动化的部署机制来独立部署。这些服务只有最低限度的集中管理,能够用不一样的编程语言编写,并使用不一样的数据存储技术。数据库


01编程

管理层低估了开发微服务的复杂性后端


我曾与许多很是看好微服务的客户一块儿合做过。对他们来讲,微服务就是解决他们全部问题的灵丹妙药。在与我讨论中,我发现大多数团队及其管理层低估了微服务开发的复杂性。数组


要开发微服务,开发人员须要一个高效的本地环境设置。缓存


随着系统中的服务数量开始增长,在一台机器上运行应用程序的子集变得愈来愈困难。特别是当你使用消耗较多内存的语言(如 Java)构建应用程序时,更是如此。安全


下面是与本地开发设置相关的要点。服务器


(1)本地开发的第一个重要方面是要有一个好的开发机器。我发现,大多数组织想要使用全部最新的、最早进的技术,但却不想替换掉糟糕的 Windows 开发机器。开发人员受限于他们的开发机器。我曾见过开发人员使用 VDI 映像或配置交叉的机器来构建基于微服务的系统。这下降了他们的工做效率,使他们没法彻底投入工做。使用糟糕的开发机器带来的反作用就是,开发人员没法获得快速的反馈。若是你知道必须等待数分钟才能运行集成测试套件,那么你就不会编写更多的集成测试套件,省得给你带来痛苦。糟糕的开发机器将会致使糟糕的开发实践。微信


(2)一旦你为开发人员配备了合适的开发机器,那么下一步就是确保全部服务都使用构建工具。你应该可以在一台新机器上构建整个应用程序,而不须要进行太多配置。根据我在微服务方面的经验,使用可以构建整个应用程序的根构建脚本也会有所帮助。


(3)下一个要点就是要让开发人员可以轻松地在他们的系统上运行应用程序的各个部分。在配置了全部端口和卷的状况下,你应该使用多个 docker-compose 文件来提供不一样的服务。


(4)接下来,若是你使用的是相似于 Kubernetes 的容器编排工具,那么你应该投资相似于 Telepresence 这样的工具,以便轻松调试 Kubernetes 集群中的应用程序。


若是组织对微服务开发的复杂性缺少理解,那么团队的速度将会随着时间的推移而降低。


02

未将库和工具更新到最新版本


在我看来,我发现一个新的平台已经成为一种遗产。团队没有确保依赖项是最新的版本,或者将像数据库这样的工具升级到最新版本。所以,两年前开始的现代化改造,现在已经背负了长达数月的技术债务。


几年前,许多团队开始将 Spring Cloud Netflix OSS 项目用于微服务。他们使用像 Kubernetes 这样的容器编排工具,可是由于他们是从 Netflix OSS 开始的,因此他们没有使用 Kubernetes 提供的全部功能。当 Kubernetes 内置了服务发现时,他们仍然使用 Eureka 做为服务发现。此外,经过相似 Lstio 这样的服务网格,你就能够摆脱 Netflix OSS 提供的大部分服务。这有助于下降复杂性,并将许多“横向关注点”(cross cutting concerns)转移到平台上。


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


咱们的建议是,组织应该在其积压中为这些升级建立技术债务项。这些技术债务项应做为架构委员会会议的一部分加以讨论,并按期予以解决。在个人上一个项目中,咱们每三个月设置为期一周的 sprint,将全部依赖项更新到最新版本。


03

利用共享服务促进本地开发


因为本地开发情况不佳,大多数团队开始依赖于共享环境来得到关键服务。开发人员机器中的第一件事就是数据库。大多数年轻的开发人员并无意识到基于共享数据库的开发是“邪恶的”。下面,是我在共享数据库中看到的主要问题:


(1)团队成员必须创建一个工做的社会契约,以免最后写入者胜出(Last write wins,LWW)问题。一个开发人员能够删除其余开发人员为他们的工做编写的数据。这种工做方式既痛苦又容易失败,早晚会影响整个团队。


(2)开发人员惧怕实验,由于他们的工做会影响其余团队成员。咱们都知道,更好的学习方法是实验和快速反馈。有了共享数据库,就能够进行实验了。咱们须要进行实验,以提出数据库模式,并执行任务,如性能调优之类。


(3)另外一个反作用就是,很难单独测试更改。你的集成测试将变得不可靠,从而进一步下降了开发速度。


(4)共享数据库必须像宠物同样对待,由于你不但愿它出现不一致和不可预测的状态。你可能会遇到这样一种场景,开发人员但愿在表是空的时候测试边缘状况,但其余开发人员须要一个表来记录。


(5)只有共享数据库拥有系统工做所需的全部数据。随着时间的推移,团队成员失去了更改的可追溯性,所以没有人知道,他们该如何在他们的机器上复制相同的设置。惟一的方法是获取完整的数据库转储并使用它。


(6)若是未链接到网络,就很难开展工做。这种状况一般发生在你通勤时间过长或乘飞机的时候。


数据库只是共享服务的一个示例,但它也能够是消息队列、集中缓存(如 Redis)或任何其余服务能够发生改变的服务。


解决这一问题的最好方法是,让开发人员能够轻松地在他们的机器上运行数据库(做为 Docker 容器),并投资建立 SQL 脚原本设置模式和初始主数据。这些 SQL 脚本应该保存在版本控制中,并像维护任何其余代码同样进行维护。


04

版本控制托管平台缺少可见性


我曾与一个客户进行合做,当时,他们的版本控制系统中有 1000 多个仓库。他们使用的是 Gitlab 版本控制平台。他们有 5 个产品,每一个产品都由多个微服务组成。我问他们的第一个问题是帮助咱们了解哪些服务及其各自代码库是产品 A 的一部分。他们的首席架构师不得不花一天时间弄清楚全部产品 A 的仓库。一天过去了,她仍是不能肯定是否弄清楚了全部的服务。


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


我我的更喜欢 mono repos,由于我发现他们真的很方便。我遇到的大多数开发人员都认为它是一种反模式。我赞成 Dan Lua 的观点,他提到了 mono repo 的如下好处:

· 简化的组织

· 简化的依赖关系

· 工具

· 跨项目变动


05

服务没有明确的定义


大多数团队并不知道什么应该被视为服务。关于到底是什么构成一个单一的微服务,人们对此存在不少混淆的认识和困惑的概念。让咱们举一个例子,假设你的应用程序具备相似插件的架构,在这个架构中,你集成了多个第三方服务。每一个集成应该是一个微服务吗?我看到不少团队,都在为每一个集成建立一个微服务。随着集成数量的增长,这种状况很快就会失控,以致于没法管理。这些服务一般过小,以致于将它们做为单独的进程运行,会增长更多的开销。


我认为,哪怕只拥有少许的大型服务,总比提供太多的小型服务要好得多。我将从建立一个服务开始,该服务对业务组织中的整个部门进行建模。这也符合 DDD(领域驱动设计, Domain Driven Design)。我将一个域分为子域和有界上下文。有界上下文表示公司内部的一个部门,如财务部门和营销部门。你可能认为,这会致使大型服务的出现,你是对的。可是,以个人经验来看,将总体重构为微服务总之比反之更容易。


随着你的知识经验愈来愈多,你能够转向表明更小问题的细粒度微服务。你能够应用单一责任原则(Single Responsibility Principle)来了解你的微服务是否变得过大,作的事情是否过多。而后,你能够将它分解成更小的独立服务。任何服务都不该该直接与其余服务的数据库通讯。他们应该只经过已发布的合同进行沟通。你能够在 Microservices.io 网站上阅读更多关于按子域模式分解 的内容。


我也遵循了 Backendlore 文档中提到的建议。这个建议能够帮助将服务限制在服务通讯上,而服务通讯是微服务系统性能低下的首要缘由。若是两条信息相互依赖,那么它们应该属于同一个服务器。换句话说,服务的天然边界应该是其数据的天然边界。


06

代码重用策略不明确


我曾经和一个客户合做,该客户在他们全部基于 Java 的微服务复制了四个与特定问题相关的 Java 文件。所以,若是在该代码中发现 bug 的话,就须要将其修复应用到全部地方。咱们都知道,在时间紧迫的状况下,咱们会错过将更改应用于一个或多个服务。这样会浪费更多的时间,增长挫败感。


这并不是说开发团队不懂正确的事情。可是,按照组织结构,人们老是默认采用简单且容易出错的作事方式。


正确的方法是,使用像 Bintray 或 Nexus 这样的工件管理器,并在那里发布依赖项。而后,每一个微服务都应该依赖于这个库。你须要构建工具,以便在发布新版本的库时,全部的微服务都应该进行更新和从新部署。


使用微服务并不意味着你不该该使用迄今为止对咱们有用的最佳实践。你须要对工具进行投资,使微服务的升级变得更容易,这样人们就没必要这样作了。


在没有适合的工具和自动化的状况下,使用微服务会致使灾难。


07

多语言编程设计


我发现团队使用多种编程语言、多种数据库、多种缓存,并以最佳工具的名义进行工做。全部这些都在项目的初始阶段起做用,但随着你的产品投入生产,这些选择开始显露出它们的本色。缘由就像咱们在构建 JavaSpringBoot 应用程序,可是咱们意识到 Java 占用了更多的内存,且性能也不好,因此咱们决定改用 Node.js。在我上一次任务中,我向团队解释说他们的推理能力很弱。


Note.js 比 Java 性能更好。 若是你的工做负载是基于 I/O 的,Node.js 一般会表现的更好。但在任何计算密集型的工做负载上,Java 都赛过 Node.js。经过响应式范式(reactive paradigm),你可使用 Java 为 I/O 工做负载提供更好的性能。在 I/O 工做负载方面,Spring Boot Reactor 的性能至关于 Node.js。


Node.js 比 Java 消耗更少的内存。 这在必定程度上是正确的说法。Java Spring Boot 应用程序并不像大多数想象的那么糟糕。我在一个 Spring Boot Java 微服务上运行了负载测试,内存消耗仍然没超过 1 GB。你能够经过 OpenJ9 JVN,限制对类路径的依赖,并经过调优默认的 JVM 参数来优化 Java 内存利用率。此外,在 Java 中还有 Spring Boot 的新替代品,如 Micronaut 和 Quarkus,它们消耗的内存至关于 Node.js。


Node.js 比 Java 效率更高。 这取决于编写代码的开发人员。使用静态类型和静态分析工具的 Java 能够帮助在开发生命周期的早期发现问题。


大多数状况下,这彻底取决于上下文。若是你的开发人员还不够成熟的话,那么不管你使用什么编程语言,你开发的都将是糟糕的产品。


我建议一家组织要发布一个团队可使用的语言列表。我认为 2~3 就是个很不错的数字。另外,要列出一种语言比另外一种语言更适合的理由。


在选择一门语言以前,你应该考虑如下一些问题:


(1)找到成熟的企业软件开发人员有多容易?

(2)从新培训开发人员掌握新技术有多容易?咱们发现 Java 开发人员能够相对容易地学习 Golang。

(3)初始团队以外的开发人员贡献、转移和维护其余人编写的代码有多容易?

(4)就工具和库的方面而言,生态系统有多成熟?


这不只仅局限于编程语言,也适用于数据库领域。若是你的系统中已经有了 MongoDB,那么你为何要在生态系统中使用 ArangoDB 呢?它们都主要是文档数据库。


要始终考虑使用多种技术的维护和操做方面。


08

人员的依赖性


这并不是微服务特有的现象,但在微服务生态系统中却变得更加广泛。缘由是,大多数团队专一于他们的特定服务,所以他们并不了解完整的生态系统。在我与不一样客户的工做中,我发现,只有一群架构师了解总体状况。可是,这些架构师的问题在于,他们并不积极参与平常活动,所以他们对开发的影响力是有限的。


我认为最好的办法是,确保全部团队都有一个架构团队的表明,这样他们就可使他们的团队与整个架构团队的路线图和目标保持一致了。要成为一个成熟的组织,你须要投资于创建一个轻量级的治理。


09

文档的缺少


在过去几年里,咱们接触过的大多数组织都在文档方面遇到了困难。大多数开发人员和架构师要么不去编写文档,要么编写的文档毫无用处。即便他们想写,他们也不知道应该如何记录他们的架构。


咱们至少应该记录如下内容:


· 设计文档。

· C4 模型中的上下文和容器图。

· 以架构决策记录的形式跟踪关键架构决策。

· 开发人员入门指南。


我建议在版本控制系统中维护全部的文档。


10

功能超过平台成熟度


我已经在其余观点中简要地提到了这个缘由,但我认为,它值得做为一个顶级缘由来说起。微服务要比传统的单体式应用(monolithic application)更为复杂,由于你正在构建一个包含许多活动部件的分布式系统。大多数开发人员还不了解系统的不一样故障模式。大多数微服务在构建时都考虑了使人快乐的路径。所以,若是你的管理层只想仅仅关注功能,那么你注定会失败。由于在薄弱平台上构建的功能是没法提供价值的。


组织须要有平台思惟。平台思惟可不只仅意味着使用容器和 Kubernetes。它们是解决方案的一部分,但自己并不是完整的解决方案。你还须要考虑分布式跟踪、可观察性、混沌测试、函数调用与网络调用、服务间通讯的安全服务、可调试性等等。这须要在构建正确的平台和工具团队方面付出认真的努力和投资。


若是你是一家资源有限的初创公司,个人建议是,你要从新考虑微服务战略。了解你所面临的问题是什么。


11

缺少自动化测试


大多数团队都知道自动化测试对产品的总体质量有多重要,可是他们仍然没有作到。微服务架构为测试地点和测试方式提供了更多的选择。若是不进行完全的自动化测试,那么你将会失败得很惨。


关于这一点,我不会再赘述,由于网上不少人都写过这方面的内容了。下图是我从微服务测试的文章找到的,这篇文章来自 Martin Fowler 的网站,讨论了基于微服务的系统的测试金字塔。


(微服务测试金字塔)



- END -


往期回顾

单元测试的五个主要准则

实践微服务六年,我得到了这些心得体会

业务百倍增加,得物如何在三个月完成交易平台重构?


点一下在看再走吧


本文分享自微信公众号 - 互联网后端架构(fullstack888)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索