作者 | Andrei Taranchenko
译者 | Sambodhi
策划 | Tina
本文探讨了软件开发领域中追求复杂分布式系统的趋势,以及可能引发的问题。作者提到了一些常见的挑战和解决方案,强调了在不必要的复杂性前要保持清晰思考,而不是盲目追求分布式架构。文章最后指出,当前行业趋势正在回归理性,企业开始认识到简单而实用的解决方案可能更为可行。
复杂性教义
有一个颇具知名度的短视频,展现了一位工程师向项目经理解释一个过于复杂的微服务“迷宫”是如何工作的,意在获取用户的生日,然而最终却以失败告终。这个场景生动地揭示了当前科技文化中的荒谬之处。我们或许为之发笑,但在正式场合讨论这个问题却是自寻烦恼,甚至可能因此失去工作。
怎么就搞成这样了?我们是如何从解决手头任务转变为把大量资源花费在解决根本不存在的问题上的?
完美风暴
近年来,一系列因素可能对当前状况产生了影响。首先,大批编写浏览器 JavaScript 的开发人员开始自认为是 “全栈”,涉足服务器开发和异步编程。毕竟,JavaScript 就是 JavaScript,对吧?无论是用它创建用户界面、服务器、游戏还是嵌入式系统,似乎都没有太大区别。而当时的 Node.js 仍然只是一个人的学习项目,早期的 JavaScript 在服务器开发领域存在诸多问题。试图向那些刚刚踏入服务器端开发领域的人指出这些问题通常会引发许多抱怨和反驳。毕竟,对他们来说,这是唯一熟悉的东西。在他们看来,Node.js 之外的世界似乎几乎不存在,而采用 Node.js 的方式是唯一的选择。因此,这是今天我们仍然面临的顽固、教条式思维的根源。
接着,大批来自 FAANG(译注:指 Facebook、亚马逊、苹果、Netflix 和谷歌,类似国内的 BAT)的资深人员纷纷加入初创公司,指导那些刚刚进入行业、易受影响的年轻 JavaScript 服务器端工程师。他们坚信 “他们在谷歌那边是怎么做事” 的方式是毫无疑问和正确的,即使在某些情况下没有道理。他们认为,没有一个单独的用户首选项服务简直就是缺乏可扩展性的表现!
然而,将所有责任都推给资深人员和新手是过于简单化了问题。除了资深人员的指导外,还有一个因素在起作用——资金的易得性。
一旦获得风险投资,你是否对盈利就漠不关心了呢?我不止一次地收到管理层的电子邮件,要求大家到办公室去整理自己的桌子,看上去非常忙碌,因为一群投资者即将在办公室游览。投资者渴望看到爆炸性增长,但并不在意盈利能力。他们只关心公司能够多快地雇佣昂贵的软件工程师来做某事。
既然你已经拥有这些开发人员,你会如何利用他们呢?是让他们构建一个更简单、更容易扩展和维护的系统,还是让他们创造一个庞大而神秘的 “微服务” 星座呢?微服务 —— 这是编写可扩展软件的新方式!我们是否应该假装 “分布式系统” 的概念从未存在过?(我们可以忽略有关微服务不是真正的分布式系统的微妙差异的解释吗?)
回到科技行业还没有变得如此庞大荒谬的时代,分布式系统备受尊敬,通常被视为最后的手段,仅用于解决非常棘手的问题。拥有分布式系统的一切都变得更具挑战性和耗时 —— 开发、调试、部署、测试、弹性等等。但我不知道 —— 也许现在一切都变得超级容易,因为有了太多的工具。
微服务开发没有标准工具,没有通用的框架。到了 2020 年代,处理分布式系统的难度只是略有改善。即使有了 Docker 和 Kubernetes 这样的工具,分布式设置的固有复杂性也没有神奇地消失。
我喜欢引用这份 5 年初创公司审计总结,因为它充满了基于确凿证据(和付费见解)得出的常识性结论:
“…… 我们审计的那些现在表现最出色的初创公司通常采用了一种几乎是毫不掩饰的‘保持简单’的工程方法。纯粹为了聪明而聪明往往遭受鄙视。相反,那些让我们感到‘哇,这些人聪明得不得了’的公司,大多数已经衰落了。” 一般来说,许多公司陷入困境的主要问题是过早采用了微服务、依赖分布式计算和消息传递密集型设计的架构。
简而言之:“复杂性会毁掉一切”。
审计揭示出一个有趣的趋势:许多初创公司在构建直观、简单、高性能的系统时,都陷入了一种群体冒充综合症。有一种教条观念认为,无论问题是什么,都不应该从第一天开始使用微服务。“每个人都在使用微服务,但我们只有一个由少数工程师维护的 Django 单体应用和一个 MySQL 实例 —— 我们做错了什么?” 答案几乎总是 “没有做错什么”。
同样,经验丰富的工程师在当今的技术世界中经常感到犹豫和不足。好消息是,这可能并不是他们的问题。团队通常会假装他们正在处理 “网站规模” 的工作,藏在库、ORM 和缓存背后 —— 对自己的专业知识充满信心(他们在 Leetcode 上表现非常出色!),然而,他们甚至可能不了解数据库索引的基本概念。你正在处于一个没有根据的过度自信、浪费和邓宁 - 克鲁格效应(译注:也称为邓克效应或达克效应,是一种认知偏差,能力不足的人错误地认为自己比实际情况更优越)的海洋中,那么真正的冒名顶替者是谁呢?
单体架构没有问题
认为只有那种看起来像阿富汗战争战略图一样复杂的系统才能够发展壮大,这种观念在很大程度上是一个误解。
Dropbox、Twitter、Facebook、Instagram、Shopify、Stack Overflow 等公司最初都是从单体代码库起步的,当中许多公司至今仍以单体架构为核心。举例来说,Stack Overflow 以其极少的硬件资源运行着庞大的网站,以此为豪;而 Shopify 仍是一个 基于 Rails 的单体应用,依赖经过验证的 Resque 处理数十亿个任务。
WhatsApp 凭借其 Erlang 单体应用和仅有 50 名工程师的团队取得了巨大的成功。背后的原因是什么呢?
WhatsApp 刻意保持其工程团队规模小巧,仅有约五十名工程师。 每个工程团队人数也不多,通常由一到三名工程师组成,但各团队都拥有极大的自主权。 在服务器方面,WhatsApp 更偏爱使用较少数量的服务器,并竭尽所能地使每台服务器发挥最大功效。
你是否曾以为 Threads 是一项涉及整个 Meta 园区的大型工程?其实不是的。他们采用了 Instagram 的模式,这就是整个 Threads 团队:
或许,宣称你的特定问题领域需要一个极度复杂的分布式系统和充满超级天才的开放办公室,只是一种妄自尊大的表现,而非明智之举?
不要解决你没有的问题
这是一个简单的问题 —— 你正在解决什么问题?是规模吗?你能否将问题分解,以实现规模和性能的双赢?你是否有足够的数据,来甄别什么需要成为一个独立的服务,以及背后的原因?分布式系统正是为应对规模与弹性而生。你的系统能否兼顾规模和弹性?如果其中一个服务宕机或变得缓慢,又当如何应对?仅凭扩展吗?那其他的服务又如何承受流量的冲击?你是否已经对各种可能的错误情况进行了周密的排列组合测试?是否考虑了反馈压力、断路器、队列、抖动等方面的影响?每个端点是否都有合理的超时时间?是否实施了防止简单改动导致系统崩溃的严密保护措施?你需要了解和调整的参数繁多且各异,皆因你的系统使用与负载特征而异。
实际上,大多数公司永远无法达到构建真正分布式系统所需的庞大规模。在缺乏相应规模、专业知识和无限资源的情况下,盲目模仿亚马逊和谷歌可能只会浪费大量资金和时间。遵循一篇名为《成功人士的十种早晨习惯》的文章,并不能确保你成为亿万富翁。
唯一比分布式系统更糟糕的,是一个糟糕的分布式系统。
Jason Warner: 全球百分之九十的公司或许只需依靠一个单体架构,运行于主数据库集群上,配备数据库备份、缓存和代理,即可满足需求。然而,那百分之十的公司却达到了全球规模(绝无戏言,Sam),解决此问题犹如一门艺术。
“但是每个团队…… 但是分开……但是 API”
将分布式拓扑结构强行嵌入公司组织结构,虽理想却往往适得其反。常见的解决之道是将问题分解成小块,逐一攻破。因此,若将一项服务拆分为多个微服务,一切似乎变得轻而易举。
这个理论看似美好而优雅 —— 每个微服务都有专门的团队精心维护,通过美丽、兼容、有版本的 API 进行隔离。实际上,这种可靠性之高,以至于你几乎无需与该团队沟通 —— 仿佛微服务是由第三方供应商维护一般。多么简单!
或许这听起来陌生,因为这种情况实属罕见。事实上,我们的 Slack 频道充斥着关于发布、错误、配置更新、重大变更和公告的消息,团队成员需时刻掌握一切。更常见的情况是,一个本就繁忙的团队分散精力于多个微服务,而非在单个微服务上取得卓越成绩,通常随着人员变动不断更换所有权。
为了赢得比赛,我们不是建造一辆出色的赛车,而是组建了一支糟糕的高尔夫球车队。
Kelsey Hightower:
这是一个很好的问题。这是我的回答:我敢断言,一个庞大的单体应用在性能上必将胜过每一个微服务架构。只需简单计算一下每个服务间的网络延迟以及每个请求的序列化和反序列化量,即可明显得出结论,毫无争议。
Eugene Atsu: 一个庞大的单体应用,通过自动扩展模型进行打包和部署,是否能在性能上与微服务架构相媲美?
你会失去什么
构建系统时,微服务固然诱人,却暗藏陷阱。这些问题或被轻视,或被忽略,导致团队耗费数月编写定制工具,学习与核心产品无关的教训。以下仅是常被忽视的几个方面。
告别 DRY 原则
多年以来,我们不断教导开发人员遵循 “不要重复自己”(Don't Repeat Yourself,DRY)的原则编写代码,然而如今,我们似乎已经彻底忽视了这个问题。微服务架构下,默认情况下并不符合 DRY 原则,每个服务都充斥着冗余的样板代码。这种 “管道” 的负担如此沉重,而微服务的规模如此之小,以至于服务的平均实例比 “产品” 更多。那么,我们能否将公共代码提取出来呢?
- 是否存在一个公共库?
- 公共库如何更新?在各处保留不同的版本?
- 强制定期更新,在所有仓库中创建数十个拉取请求?
- 把它们都放在一个单体代码库(monorepo)中?这也伴随着一系列问题。
- 是否允许一些代码的重复?
- 毕竟每家公司都有权在每次重复发明轮子。
每家公司都必须面对这些选择,而且没有一个 “人机工程学” 方案是完美的 —— 你必须选择你愿意承受的痛苦。
开发人员人机工程学将大幅下降
“开发人员人机工程学”指的是完成新功能或解决错误时所付出的努力和摩擦。
在使用微服务时,工程师需要构建整个系统的思维导图,了解执行特定任务所需的服务、团队合作、联系对象以及关注事项。这是 “在做任何事情之前,你必须了解一切” 的原则。Spotify 这样的市值数十亿美元的公司,投入了大量资源来构建 Backstage,用于编目其无尽系统和服务的软件。
这至少应让你明白这个游戏并不适合每个人,代价也相当高。对于工具方面,Spotify 之外的公司不得不自行解决问题,解决方案的稳定性和可移植性可想而知。
有多少团队实际上简化了启动“又一个愚蠢服务”的流程?这包括:
- GitHub/GitLab 中的开发者权限
- 默认环境变量和配置
- CI/CD
- 代码质量检查
- 代码审查设置
- 分支规则和保护
- 监控和可观测性
- 测试工具
- 基础设施即代码
当然,要将这种列表乘以公司内部使用的编程语言数量。也许你有一个可用的模板或操作手册?也许有一个无摩擦、一键启动新服务的系统?要消除这些自动化问题需要数月的时间。因此,你可以将重心放在产品上,或者致力于工具开发。
集成测试
仿佛日常微服务的磨难还不够,你竟然舍弃了稳定的集成测试带来的安心感。尽管你的单一服务和单元测试都通过了,但每次提交后,你是否仍能确保关键路径完整无缺?谁负责整个 Postman 或其他地方的集成测试套件呢?有答案吗?
在分布式系统中进行集成测试几乎成为无解的难题,因此我们不得不放弃,转而追求 “可观测性”。就像 “微服务” 是新的 “分布式系统”,而 “可观测性” 则是新的 “生产中调试”。若忽视可观测性,那写出的软件定然不够完美!
如今,可观测性已成为独立领域,你不仅需付出昂贵代价,还需消耗开发人员的时间。它无法即插即用 —— 你需要了解并应用金丝雀发布、功能标志等。谁来担当这个责任?难道要由一个已经忙碌不堪的工程师承担吗?
如你所见,将问题拆解并不意味着问题会变得更容易,相反,只会让你面临更加复杂的困境。
那么仅仅使用“服务”呢?
为什么非得将你的服务划分为“微服务”?仅使用“服务”有什么问题吗?有些初创公司甚至已经走到了为每个功能创建一个服务的地步,是的,这不就像使用 Lambda 吗?这是一个非常有见地的问题,让我们对这种无法控制的崇拜有了更深入的认识。
那么我们应该采取什么策略呢?从单体架构开始显然是一个明智的选择。在许多情况下,一种有效的模式是 “主干和分支”,其中主要的 “主干” 单体架构得到了 “分支” 服务的支持。这些 “分支” 服务可以处理明确定义、可独立扩展负载的任务。例如,一个需要大量 CPU 资源的图像调整服务可能比一个用户注册服务更有实际意义。或者说,你是否每秒都在进行大量用户注册,以至于需要进行独立的横向扩展呢?
副注:在版本控制的时代,我们很少使用 “master” 分支这个术语。相反,我们通常采用 “主干和分支” 的表达方式,因为它更易于理解 —— 犹如树形结构一般。然而,随着 GitHub 决定摆脱令人遗憾的命名约定,年轻的工程师们早已忘却了 “主干” 的概念,因此 “main” 便成为了默认的名称。
DHH: 除了壮观的单体之外,还应该编写成“城堡”的模式:一个单一的、壮观的单体,承载了应用程序的大部分主体,同时为高度专业化和不同需求的情况提供了一些辅助外围应用。
钟摆回荡,炒作渐息
风险投资的洪流退去,企业明智地回归市场,认清了过度投入 Web 规模架构可能带来的风险。或许,在网络问题不再成为困扰的情况下,理性决策才是可持续发展的关键。
Arjun Narayan: 然而,这并非为 Web 规模量身打造。当 Rails 单体应用达到极限容量,无法继续扩展公司规模……
除了坐拥 940 亿美元市值,你又将如何应对?
Milan Jovanović:
亚马逊 Prime Video 团队将一个系统从微服务迁移到了单体架构,并且成功将云成本降低了 90%。
以下是最重要的收获:
Gergel Orosz:
记录一下,在 Uber,我们正在将许多微服务迁移到所谓的 "宏服务",正如 @copyconstruct 所称的 "宏服务"(适度规模的服务)。这样做的原因是,测试和维护数以千计的微服务不仅费力,而且从长远来看,解决短期问题可能更加麻烦。
Cindy Sridharan:
- 微服务很难。
- 构建可靠且可测试的微服务远非易事。
- 高效地 "测试" 微服务需要大量的工具和远见。
- 许多(甚至大多数?)组织无需采用 Netflix/Uber 风格的微服务。
- 宏服务。
最终,当从纽约前往费城时,你面临两个选择。你可以构建一艘高度复杂的太空飞船,进行轨道下降抵达目的地,或者选择购买一张美国国家铁路运营商 Amtrak 的火车票,乘坐 90 分钟的列车。这就是抉择的难题。
原文链接:
https://renegadeotter.com/2023/09/10/death-by-a-thousand-microservices.html
声明:本文为 InfoQ 翻译,未经许可禁止转载。