“All in Cloud”之后,和你聊聊「云原生DevOps的Kubernetes技巧」 了解详情
写点什么

新项目别一上来就用微服务

  • 2021 年 12 月 24 日
  • 本文字数:4982 字

    阅读完需:约 16 分钟

新项目别一上来就用微服务

微服务变得越来越理所当然,似乎我们一直生活在微服务的世界中。很多时候,我们常常讨论微服务采用与否、如何选型等问题。但本文作者 Arnold Galovics 想讨论的是,为什么一个全新的项目从开始就使用微服务通常是个坏主意。很长一段时间以来,他都在思考这个问题。


最近,当他与其他开发人员交谈并询问他们如何启动一个全新项目时,答案几乎都是:这儿用一个微服务,那儿用一个微服务,用户管理用一个微服务,身份验证用一个微服务,鉴权用一个微服务,session 管理用一个微服务等等。因此,关于微服务,Arnold 想基于他过去工作项目的一手经验,讲讲别人没有讲过的一些东西,他撰写了一篇题为《不要从微服务开始,单体架构才是你的朋友》(Don’t Start With Microservices – Monoliths Are Your Friend)的文章。


Arnold 的文章很快在技术社区引发热议。有持赞同意见的人直言,微服务对于大多数普通需求来说是一种“矫枉过正”,还有人提出微服务有个 Arnold 没提到的更严重的缺点——将事情分成模块需要时间,并且涉及做出我们可能不知道答案的决定。“启动新产品时,最重要的是尽快启动并运行产品,以便人们可以试用并提供反馈。而根据收到的反馈,我们往往可能会意识到需要构建与现有的完全不同的东西。我见过很多工程劣质的成功产品,也见过很多设计精良的产品失败。产品的成功与其设计的好坏无关。速度往往是最重要的因素。”


另外,有条热门评论提出了个有意思的问题:为什么没有人谈论介于这两者之间的架构——模块化单体?


对此,有回复说道“因为没有新发明的架构称为‘模块化单体’——单体从一开始就应该是模块化的。”该回复进一步指出,微服务不是单体应用不好而诞生的答案,人们的理解在某些地方出现了问题,这也可能是因为很多人不知道应该在代码中构建模块,然后大量的单体最终成为“意大利面条式”代码,就像现在许多微服务架构那样。


有人对此表示赞同并表示,“模块化单体”只是“代码中关注点的适当分离”,其实从一开始就存在。但也有人提出反对意见,认为“模块化单体”要比“分离关注点”要更复杂,并不完全是一回事。


一千个人眼中有一千个哈姆雷特,我们将 Arnold Galovic 的这篇文章翻译出来,希望能为读者带来一些参考价值。以下是他的分享内容:

理想中的微服务

我们先来看看,多数文章提到的一些微服务的主要优势有哪些:


  • 故障隔离

  • 消除技术锁定

  • 更容易理解

  • 更快的部署

  • 可伸缩性是的,这些都不是书中的虚假承诺,但我也必须对你说实话,使用微服务的话,你的系统很难实现这些承诺。下面让我一一列举这些优势。


故障隔离。由于应用程序由多个服务组成,因此如果其中一个服务宕机或出现问题,则只会影响系统的那个部分。以 Netflix 为例,当你观看节目时,你并不关心推荐。因此,如果它们有一个服务来处理当前观众,为他们提供视频流;它们有另一个服务来处理个人用户推荐。如果推荐服务宕机,系统中最重要的功能(例如观看节目)不会受到影响。故障被隔离了。


消除技术锁定。想想单体应用。它是一个巨大的应用程序,有成百上千个 API,管理数百个数据库表。这个应用程序是用 Java 写的,团队花了 5 年时间开发它。一种奇特的新语言出现了,纸面上带来更好的性能、提供更好的安全性等等。这可能是 Go 或 Rust,团队想试验下该语言及其技术栈。他们如何在一个单体应用中做到这一点呢?他们做不到,因为这是一个单独的部署包。你可以将应用程序的一部分切换到不同的语言,但这并不容易做到。


使用微服务时,不同的服务可以使用不同的技术栈。服务 A 可以用 Java 写,服务 B 可以用 Go 写,服务 C 可以用Whitespace写,如果你有勇气这么做的话。


更容易理解。当你有多个服务负责整个功能的一小部分时,这个服务本质上会更小,因此更容易理解。


更快的部署。在常规的单体应用系统中,要么完全部署,要么根本不部署。你需要部署一个包,这是一个要么全有要么全无的场景。使用微服务,你有机会独立部署,这意味着,如果你想要部署推荐服务的一次升级(回到 Netflix 的例子),你可以部署单个服务并节省大量时间。


可伸缩性。我的最爱。你可以通过启动多个实例来增加特定功能的容量,从而扩展服务。像前面举的例子,如果人们在 Netflix 上查看大量推荐,它们可以很容易地启动多个推荐服务的实例来应对负载。在单体应用环境中,你要么扩展应用程序的每一个部分,要么什么都不扩展。

现实生活中的微服务

我要用残酷的事实打击你,我的朋友。我并不是说这些优势无法实现,但是你、你的项目、你的组织必须非常努力才有可能实现这些优势。

基础设施要求

下面让我从微服务的一个最大困难——基础设施开始讲起。


绝对是 10 r6g 的真实照片。在 K8S 上运行的单个登录应用程序的 16x 大型(64vCPU、512GB RAM)服务器。


你曾经部署过单体应用吗?当然,我们可以将其复杂化,但在常规情况下,如果将应用程序部署到云上,单体应用就是你所需要的形式。让我们以一个简单的在线商店应用程序为例:


  • 应用程序的一个负载均衡器

  • 运行应用程序的一个计算实例

  • 应用程序的一个(关系型)数据库

  • 用于日志聚合的 Kibana 如果你用的是微服务:

  • 一个 Kubernetes 集群

  • 一个负载均衡器

  • 运行应用程序和托管 K8S 集群的多个计算实例

  • 一个或多个(关系型)数据库,取决于你是否每个服务用一个数据库

  • 一个用于服务间通信的消息系统,例如 Kafka

  • 用于持续集成(持续部署)的 Jenkins

  • 用于日志聚合的 Kibana

  • 用于监控的 Prometheus

  • 用于分布式跟踪的 Jaeger/Zipkin 而且这只是一个高层级的概览。微服务确实可以产生价值,但问题是:代价是什么?


尽管这些承诺听起来很好,但你的架构中有更多活动的部件,这自然会导致更多的失败。如果你的消息系统挂了怎么办?如果你的 K8S 集群出现问题怎么办?如果 Jaeger 宕机,而你无法跟踪错误怎么办?如果指标没有进入 Prometheus 怎么办?


显然,你将花费更多时间(和金钱)来构建和运转这个复杂的系统。

更快的部署?

我将讲讲优势列表中的第一点:更快的部署。当你想到 Netflix、Facebook、Twitter,并且观看他们的会议演讲,他们描述他们正在运行的微服务数量,以及他们如何向 Git 提交内容,并在数小时内将其投入生产。这是不是好得难以置信?


在我看来,这绝对是可以实现的,但我承认我从未参与过这样的微服务项目。我并不是说这是不可能的,只是从稳定性、基础设施和团队文化角度来看,这真的很难实现。


让我分享一下我是如何从我的经历中得出这个结论的。在对一个全新的项目进行编码之前,你通常会先研究如何将产品转变成技术方案。你会设计系统,设计微服务,会有多少个微服务,每个微服务的职责等等。


有一个真正的教学项目,我们在这个项目中练手,我们最终做了 80+微服务,用了多长时间呢,4 个月?


这 80+微服务的现实意义是,与其将这 80+微服务一起组合成一个单体应用并部署这个单体应用,部署单个微服务绝对会更快,但是.......这 80+微服务太小了,以至于一个开发单元——敏捷领域的叙事——永远不可能只涉及一个服务。系统从根本上被破坏了,更快的部署的承诺立即消失了。我们不再拥有更快的部署,而是相反,更慢的部署。而且慢得多。


另外,我会反复思考这个问题。部署过程中活动的部件越多,意味着潜在故障越多。很多时候,基础实施不够稳定,部署会随机失败,因为:


  • 在下载/上传软件包时,Artifactory/Nexus/Docker 仓库短时间内不可用;

  • Jenkins 构建器随机卡住。这只是其中的一部分。产品必须分解为微服务。每个服务都必须对其自己的事情负责。例如,Netflix 中的一个推荐服务应该负责向用户提供推荐。


**不是谁都是 Netflix,也不是所有东西都容易分解成合适的大小和职责。**这是领域驱动设计(Domain Driven Design,DDD)和有界上下文可以提供帮助的地方,但这实践起来并不容易,有时甚至没有足够的时间/开发性来试验这些方法。

配套文化

无论如何,在我看来,微服务的第二个困难是组织/项目文化。**如果产品(部门)根本不关心底层系统架构会怎么样?**我是说,他们会关心吗?


举个例子:如果你有一个拥有大量微服务的复杂架构会怎么样。产品负责人进来对团队说,让我们开发整个功能。在团队分析完功能请求后,发现它涉及 10-15 个微服务,因为它与许多其它的已有功能有关联。那你会怎么办?


你试图将它分解成更小的部分,但是这么做到底对不对,这里存在着疑问,因为每个小部分的功能没有意义,而且逐个服务发布它会增加大量开销。你当然不能对产品负责人说,仅仅因为我们用的是微服务,所以我们需要 3-4 倍时间,对吧?


这个对话会是什么样子?


  • 产品经理:嗨,伙计们,我想到了一个非常棒的功能。我们的竞争对手也准备做这个功能,所以我们要快点实现它。2 周做完,可以吗?

  • 团队:好吧,初步看来,我们可以实现这个功能。而且这个功能看起来也是一个好主意,可以带来更多的客户。我们会重新组织,好好谈谈。

  • 团队:好吧,2 周有一点儿问题。因为我们用微服务是为了更快,我们需要更多时间来实现这个功能,由于我们需要涉及 15 个服务,因此我们需要 6 周的时间来完成初步实现。

  • 产品经理:初步实现?

  • 团队:是的。这 15 个服务之间的通信非常重要,因此初步实现不会包括异常处理、弹性通信模式、调试跟踪等其它东西。如果做这些的话,我们还额外需要 4 周时间。

  • 产品经理跳窗了