没问题制造问题
有一则颇为滑稽的场景,讲述了一位工程师向项目经理解释一个过于复杂的微服务系统是如何工作的,以便获取用户的生日,但最终仍然解释不清楚。
图片
这一场景准确地描述了当前IT文化的荒谬之处。然而在现实中,如果你跟面试官讲了这个笑话,你猜他会怎么看你,大概率你不会通过面试的。
为什么会变成这样呢?我们的目标本来应该是让系统正常的运行起来,满足业务的需要,但是现在却变成了要解决根本没有的问题(微服务带来的问题),结果就是熬了好多个夜,掉了好多根头发,给服务器运营商分了不少钱,结果呢?
完美风暴
近年来,有几件事可能导致了当前局势。
首先,大批使用 JavaScript 前端开发者开始自称为“全栈”,涉足服务器开发和异步代码,比如node,比如 Vercel 对 React 的魔幻操作。JavaScript就是JavaScript,使用它创建什么不重要, 用户界面、服务器、游戏还是嵌入式系统。对吧?
Node当时还只是一个人的学习项目,早期的JavaScript是服务器开发尽量要避免的,或者不可能选择的语言。但你如果和一些固执的 JavaScript、Node开发者来说,会遭到对方的鄙夷。毕竟,这是他们知道的全部,Node之外的世界实际上是不存在,Node就是唯一的选项,因此这是我们至今要处理的固执、教条主义思维的起源。
另外,还有一大批大厂向外输送的“人才”涌入初创公司或者小公司队伍中。用大厂的那一套模式和方法来指导团队,即使这套模式并不合适。那不是他要考虑的。“我不管,怎么没有单独的用户服务呢?将来怎么扩展呢,兄弟!”
还有一个不得不说的事实,在国内凡是面试,必定要问微服务,从来也不看看线上用户才多少,开发团队才几个人。这就导致很多没有接触过微服务的开发者,愿意在平时的项目中尝试微服务,因为能够积累一些经验,方面以后跳槽能拿到更多的钱。
在早些时候,分布式系统是受到尊重、畏惧和普遍尽量避免的,仅作为处理特别棘手问题的最后手段。一旦涉及分布式系统,就会变得更具挑战性、更加耗时,无论是开发、调试、部署、测试。
但是现在却变得超级容易了,因为有了各种各样的框架和工具,比如docker、k8s,以及像 Spring Cloud、Dubbo 等各式各样拿来即用的框架。
这里的摘要让我印象深刻,因为它充满了常识性的结论:有一份关于5年初创公司审计摘要:那些表现最好的初创公司,通常对工程学采取了一种竭尽所能“保持简单”的方法。纯粹为了耍聪明是被鄙视的。相反,那些让我们感到“哇,这些人真是聪明极了”的公司,大部分都已经消失了。
许多初创公司遇到了一种“冒牌综合症”的问题。在构建直截了当、简单、高性能系统时,那些一开始没有使用微服务的公司,常常会疑惑“只要一个Spring Boot单体维护就可以,由几个工程师维护,还有一个MySQL实例,难道系统不应该这么做吗?”。答案是“没有”。
同样,有经验的开发者在当今 IT 环境中常常会陷入自我怀疑和自我否定。好消息是,这可能不是你的问题。
在今天的技术世界中,一些团队或开发者往往假装他们在做高并发、超复杂的系统,但他们可能甚至不知道数据库索引的基础知识。我们正处在一个充满不合理的过度自信、浪费的海洋中,那么真正的骗子是谁呢?
单体架构没有错
如果你没有一个看起来像臭名昭著的阿富汗战争战略图式的系统架构图,你就无法成长,这种想法是一个笑话。
图片
Dropbox、Twitter、Netflix、Facebook、GitHub、Instagram、Shopify、StackOverflow ,这些公司和其他公司都以单一代码库开始。其中许多公司的核心仍然是单体。StackOverflow引以为傲的是他们运行庞大网站所需的硬件非常少。Shopify仍然是一个Rails单体,利用了经过验证的Resque来处理数十亿个任务。
WhatsApp是以Erlang单体和一个相对较小的团队开发而最终成功的。并一直将团队人员保持在较小规模,只有大约50名工程师。每个Team也很小,由1-3名工程师组成,每个Team都拥有很大的自主权。
在服务器方面,WhatsApp更倾向于使用较少数量的服务器,并将每台服务器纵向扩展到最大程度。纵向扩展就是加强单机配置,比如增加 CPU、内存、存储等,相对而言,增加多台服务器叫做横向扩展。
Instagram被以数十亿美元收购 ,团队只有12个人。
不要解决您没有的问题
这是一个简单的问题,您解决的是什么问题?是规模问题吗?
当你准备将系统做成复杂的微服务架构时,你就要面对下面众多的问题:
- 系统的拆分策略是什么,哪些功能要归结为一个服务,哪些要隔离开;
- 分布式系统是为高并发、稳定性而建立的。您的系统是否可以同时扩展性和稳定性?
- 如果其中一个服务崩溃或变慢会发生什么?会影响到其他服务吗?
- 你是否考虑了如果将会发生了变化,将如何应对?
- 要做压力测试,要有断容器,有队列,分布式事务等等。
- 每个接口都有合理的超时时间吗?
- 有没有绝对安全的保护措施,以确保简单的变更不会导致整个系统崩溃?
这是,需要了解和调整的配置和功能就是无穷无尽的,这样,我们在解决业务问题的同时,又增加许多本来不必要的问题,甚至这些不必要的问题超过了原本的业务问题。
事实上,大多数公司永远不会达到真正需要构建真正分布式系统的规模,这实际上可能只是在浪费金钱和时间。
比分布式系统更糟糕的是错误的分布式系统。
拆成微服务,每个团队都只维护自己的 API?
将问题分解为更小的部分,然后逐个解决,是一种常见方法。那么,是不是你将一个服务分解为多个服务,一切都会变得更容易呢?
这个说法是充满理想主义的,每个微服务都由一个专门的团队维护,围绕着一个漂亮的、向后兼容的、有版本的API。从此之后,你就很少甚至不必与这个团队沟通了, 就好像这个微服务是由一个第三方供应商维护的一样。
但事实往往是事与愿违的。事实上,公司各种群聊中充斥着来自团队的消息,这些消息涉及发布、错误、配置更新、破坏性更改等等。每个人都需要随时了解所有的内容,甚至比之前更多的内容。
多个微服务如何维护
构建微服务有很多陷阱,要么完全没有得到充分认识,要么被简单地忽视了。
很有可能是,团队花费很长时间编写高度定制的工具,并且得到了跟核心产品毫不相关的教训。这里只是一些经常被忽视的方面:
每个服务往往都充满了冗余的样板文件,样板代码。很多时候,这种样板的开销很大,而为了保持微服务的“微小”,这些冗余、通用的部分会被抽离出去,那么,这些抽离出去代码怎么维护呢?
弄一个通用库吗?如何更新通用库呢?还是到处保留不同的版本?又或者定期强制更新呢?还是允许每一个微服务有重复呢?
到最后就变成了,去你的,还是按照自己习惯的方式重新发明轮子吧。
在单体架构中,更新和打补丁很容易,因为一切都在一起。在微服务中,打个补丁可能意味着在整个复杂系统中蜂群一般的更改。如果有十个微服务依赖于同一项更改,怎么确定是哪十个微服务依赖了更改?如果不知道,那么这些更改如何处理?或者更糟糕的是,如果一个团队拒绝进行更改怎么办?那可能会在重新启动微服务时发现一个甚至多个诡异的问题。
返璞归真
最近,有不少的企业,还是比较大的企业将一些原本是微服务架构的系统回归到了单体架构。
亚马逊的 Prime Video 团队将微服务架构迁移到了单体,云成本降低了 90%。
图片
在Uber,正在将许多微服务迁移到所谓的 "宽松服务"(well-sized services)。确实,测试和维护数千个微服务不仅很困难,而且长期来看可能会比解决短期问题带来更多麻烦。
图片
打个比喻,要从北京到上海,是建造一艘复杂的宇宙飞船呢,还是买一张高铁票来的实在?
英文原文地址:https://renegadeotter.com/2023/09/10/death-by-a-thousand-microservices.html