停!你不需要微服务


现在已经是2020年了。如果你想要从我这边了解什么是微服务,那么这篇文章可能不适合你,你可以把宝贵的时间花在别的地方寻找相关信息。但是如果你正在经受各种微服务成功案例的洗礼并且被迫去使用微服务这款“灵丹妙药”;那就继续读下去。不过开始时我们先说点让人失望的地方。

尽管想写这篇关于微服务的文章有一段时间了,但是直到最近我和一些朋友交流过后才真正的着手去把它写下来的。最近我受邀参加了一个法定人数会议(quorum),并讨论了一个有趣的问题。“什么是微服务以及我们应该使用这种架构作为解决方案吗?”

问题的第一个部分比较好回答,第二部分就不好回答了。不过经过几分钟的讨论,有几点是比较清晰的。
  • 受益人(beneficiaries)打算把微服务架构应用到他们以后的产品上;并且他们希望得到与会人员的肯定。
  • 参加会议的人有很大一部分不是技术相关的。并且会议中对技术的讨论越多,这些人就越显得不相关。
  • 会议中长时间的沉默以及对相关问题缺乏相应的提问表明许多人对Web服务都不了解;更别提微服务了。


我不是责备他们对Web服务的不了解,或者微服务能给他们带来哪些好处和坏处。毕竟他们也有自己擅长的工作,而我不擅长的。他们只是跳上了不知道能给他们带来哪些影响的微服务这趟车上。

我第一次听到“微服务”这个词是在2013年,一个YouTube视频上,讲的是关于Netflix服务架构的。视频里面说了很多微服务相关的信息,但是我毫不犹豫的都跳过了。这对于一个当时想要进入设计领域的人来说,内容太多了。但是很快我就遇到了困扰,因为新项目计划书要求使用微服务。这个项目的设计很优秀,并且现在仍然是我手上最优秀代码库中的一员。

诚实点说,最大化模块设计让我避免了很多坑。并且当时我还仅仅是一个初出茅庐的开发人员,跳过运维开发(DevOps),还额外加了一个密度(density)层。时间快速向前推进五年,这个时候我和一帮新的同事忙于一个新的产品。并且我每天要面对许多由于不好的微服务设计引起的问题,而且还要耦合业余的运维开发策略。不过这些问题很快就解决了,但是我又面临微服务的脆弱性问题。最后我只能凭着自己的经验去分析整个架构。亡羊补牢,为时未晚。

看到微服务既扮演正派角色又扮演反派角色,我劝解自己作为反派角色的拥护者。如果你是一个架构师或者设计者并且打算把微服务作为默认架构,我强烈建议你问自己几个问题。

你的程序大到足够分解为微服务吗?

承认吧。不是所有的程序都足够大到需要分解为更小的服务。正如微服务名字所暗示的,它是一个由更小的并且用于完成某个特定功能的独立服务的集合。理想情况下,每个服务都是完全独立的应用程序。
1.png

这是微服务与单体服务每行代码成本(Cost per Line of Code)的比较图。微服务会带来更大的成本,即使这个微服务是个很轻量级的,原因是由于它在人力和计算成本方面需要一个最小的资源。每个人都要考虑成本,如果你不考虑,那么你可能就不应该做决策。

当然,你的代码库未来会不断增长,并且它本身也可能会添加一个新域(domain)。但是你要永远记住一条,当你需要的时候,一个设计良好的代码库总是能够切换到微服务。

你真的需要伸缩(scale)应用中的不同组件吗

我们假设一下。你的产品负责人向你提出了一个HRMS应用程序的想法,这个程序需要处理一个拥有10k雇员的组织。作为一个技术爱好者的你立马想出了一个解决方案:微服务架构。
2.png

当然,这是一个极端的例子,但是你能理解这个点!

使用微服务架构的另一个主要优点是伸缩单个组件很简单。我们可以发现许多应用程序都需要对组件进行单独的伸缩,但是你的应用程序真的需要这个功能吗?

你是否有跨服务的事务?

现在,这是个即艰难又要讲究策略的选择。跨多个服务的事务对整个架构来说是个不利因素。解决跨服务的事务意味着,服务之间的互斥锁,一系列很难追踪的死锁以及竞争条件都会严重影响服务的健康运行;并且有时甚至会对工程师造成很大影响。

从定义上来看REST服务是无状态的。并且它们也不应该参与到跨多个服务的事务中。在高性能世界中,两阶段提交(2PC)是不必要之恶。并且SAGA模式仅仅是加了一个你不了解的复杂层。


由于微服务使用了去中心化的数据管理,所以产生了最终一致性问题。单体应用中,你可以在一个事务中一次性更新许多东西。微服务需要更新多个资源的时候,使用分布式事务不是很好(有充分的理由)。所以现在,开发者需要意识到一致性问题,并且在代码运行出现不可挽回的错误之前能够及时的发现不一致的问题——Martin Fowler
跨多服务的事务可行吗?

是的,绝对可行的。

但是,是否有必要实现一个通过多个无状态服务的链式操作?

可能没必要!

服务之间是否需要频繁的通信?

在传统的单体服务中,每个模块就是微服务实例的表现形式。模块之间的通信都是使用内存并且延迟接近于0。微服务的引入意味着通信从使用内存转移到了使用网络。

目前市面上有许多成熟的解决方案,但是他们都有一个共同的代价——延迟。从使用内存转移到基于网络的通信会让你的延迟从以纳秒为单位变为以微妙为单位。想象一下,有三个不同的服务通过网络互相通信。假设每个服务调用需要100毫秒(在负载很高的情况下这个是很正常的事情),那么仅仅在网络上就要花费300毫秒。

另外有些应用程序天然的就与组件和服务紧密的集成在一起。比如在需要实时处理数据的应用中,额外的通信层可能会导致一个严重的后果。想象一下,在外科手术服中或者航空交通管制中出现通信延迟会出现什么后果。

其他补充

  • 复杂度的增加——当然,复杂度不好量化,并且在相对情况下才有可比性。尽管微服务最初的目的就是通过把单个应用分解为更小的多个模块来降低复杂度,但是在部署和维护方面微服务架构本身就很复杂。
  • 部署(distribution)成本——微服务都是独立的分布式系统。相同部分的部署都会有成本。比如你的单体应用之前都是部署在一个大的虚拟机上或者首选的容器上,但是微服务都是独立部署(理想情况下)在不同的虚拟机或者容器上。虽然它们相对比较小,但是做个数学计算就知道了。并且我们还没有把微服务的编排和维护算在里面。
  • 运维开发的适配——这个要根据自身的情况来看,可能有利也可能有害。运维开发是一个已经被广泛接受的并且也是一个被证实可操作的解决方案。但是如果你所在的团队很小,那么组建一个运维开发团队带来的问题要比好处大。但是有一件事是可以确定的,如果你不投入一个运维开发团队,那么你就没法对微服务进行有效的维护和监控。
  • 紧密集成——有些应用程序本身就需要紧密的耦合。为了适应微服务架构而去解耦他们可能会产生严重问题。
  • 经验不足——经验不足是个很严重的问题并且它们不仅仅局限在SOA这块。对于持有抽象定义的微服务来说,可能会产生更严重的问题。如果你的微服务是按照顺序部署的,并且你的服务依赖其他服务,那么如果你依赖的一个服务挂掉了,你自己的服务此时崩溃了,这个时候再去处理就会显得很晚了。
  • 端到端测试——典型的单体应用可以让你启动并且几乎立马运行测试。对于互相依赖的多个服务,如果没有可靠的编排,那么测试就会被延迟。
  • 混乱的数据合约(contracts)——在同一个团队中制定和保持数据合约和在不同团队之间共享有着极大的不同。并且当你使用微服务时,你的团队有可能都没在用;更不必说让他们都使用相同的编程语言。为特殊需要制定相应的数据合约也会消耗你的时间和精力。
  • 遗留代码库——对于我们中的大部分人来说,处理遗留的代码库就是每天的日常活动。大部分公司都是这种情况。快速变化的新技术不断的推动着我们向前走,同时,它也把我们与遗留代码库隔离的越来越远。
    你确信你刚刚开发的RabbitMQ框架能够和你之前部署在IBM AIX服务器上的应用程序工作很好吗?
  • 调试的痛苦——每个服务都有自己的日志文件。更多的服务=更多的日志文件。


总结

我有告诉过你“不要使用微服务”吗?

绝对没有!!

我们经常会听到微服务一个比较好的优点是,它们解决了之前我们都认为解决不了的问题。Netflix使用微服务构建系统已经成为了一个好的典范。当然成功的案例不仅仅只有Netflix。 UberSoundCloud,以及伟大的亚马逊都是成功的一员。另外也不要认为成功的案例仅仅局限于消费应用程序。我曾在美国一家医疗保健巨头工作过,每一次打开源代码看到他们在设计上的创新(possibilities),都会让我很着迷。

如果五年前你就入了微服务的坑,那么我不会谴责你的盲从。毕竟时间不一样了,我们现在能做的就是诚实的看待它。但是现在是2020年了,我们已经踩过太多的坑并且我们身边也有很多这样的案例。不必要的引入微服务架构,将会导致不良的代码变成不良的基础架构。

我喜欢一个充满热情的程序员。我曾经是并且现成依然是其中的一员。他们坚持自己所做的事并且能够超预期的解决别人的问题。但是在做决策方面就很难保持这样的热情,毕竟那是需要一点运气成分在里面的。很抱歉让你失望了。微服务不应该作为你的默认选择。它们不是你寻找的银弹。坚持使用KISSYAGNI原则。

作为一个技术拥护者和爱好者,你有权利有自己的喜好。但是当面对“正确的选择”和“喜欢的选择”的时候,让你变得更卓越的是实用的选择能力。

祝好运。

原文链接:STOP!! You don’t need Microservices(翻译:王欢)

0 个评论

要回复文章请先登录注册