• 微服务频繁被吐槽 这到底是微服务自身的问题还是用的人的问题?
  • 发布于 2个月前
  • 149 热度
    0 评论
  • 凝晨
  • 0 粉丝 26 篇博客
  •   
《Death by a thousand microservices》这篇文章确实很棒,我赞同这篇文章的观点,但是,我想强调微服务本身并没有问题。我也不认为,对于规模较小的产品,单体架构就总是适合的。事实上,就我而言,我更喜欢服务(我特意避免使用 “微” 这个前缀)而不是一个庞大的单体系统。我认为,真正的问题,是缺乏工程能力和公司的不负责任。这才是造成复杂系统无人能高效工作的原因。

一.无能为力
软件工程界是非常容易受到炒作驱动的。微服务是目前的热门趋势(尽管我认为人们现在已经准备好迎接下一个潮流了)。实际上,我非常高兴我们能如此迅速地采纳新技术。这使得我们的职业生涯保持有趣。如果我不喜欢探索,我可能就不会有机会得到如此多的完全不同技术的经验,我可能就会成为一个更糟糕的程序员(可能还在用我职业生涯开始时的那个糟糕的俄罗斯 PHP 框架)。

然而,采纳新的(颠覆性的)技术意味着我们从资深专家变成了新手。是的,我们应用了我们迄今为止学到的知识和技术,但有时这些不容易移植。还记得 Scala 开始流行的时候,人们注意到写出的代码要么是过于 “聪明” 的数学怪话,要么就只是换了语法的 Java 代码吗?

公司经常以为,如果他们雇佣了 “聪明的人”,这些人就会自己解决所有问题。但事实并非如此(另见下文的 “自付的复杂性”)。聪明并不能保证你可以解决所有问题。经验或许是最宝贵的资产(高质量的经验本身也是非常珍贵的),但当采纳新技术时,我们放弃了经验。我们自愿变得无能为力。

当然,有些人确实对这些技术非常了解。但他们是否真正设法教育其他人并引导他们正确利用这些技术呢?并没有。

示例:Apache Kafka 和事件驱动系统
现在,Google 上关于 “Apache Kafka” 的搜索量急剧上升。很多公司,尤其是初创公司都在使用它(你可以在 LinkedIn 或某些招聘网站上搜索一下)。我遇到的很多人当我把它称作事件总线时,他们会显得愤怒(并带着那种居高临下的怜悯的眼神),他们会说:“不,它不是事件总线,它是一个事件流和存储平台”,我会反问:“所以,它是一个不删除消息的事件总线?”,他们会坚持:“不,你不懂,让我再解释一遍。” 

实际上,许多公司甚至没有使用它进行事件流处理,而是将其作为可以在出错时重放消息的消息队列。在我目前的公司,它甚至与 Avro 模式一起使用,作为使用 protobuff 的 gRPC 的替代方案。这有什么意义呢?不如直接使用 HTTP。

引入它的人通常(但不总是)有一个相当有说服力的推介,并且听起来他们确实理解它的有用之处以及如何有效地使用它。问题是,即使那是真的,他们雇佣或管理的人并不这么认为。10 年前 Kafka 还不流行,我怎么可能有相关的经验呢?

如果有人一生都在用锤子和钉子,你给他们螺丝和螺丝刀,他们可能会说:“哦,这个锤子不太好用,但工作还是那个工作”,然后继续用手柄把螺丝敲进木头。如果你想让他们正确使用这些工具,你需要投资来培训他们,或者至少在他们熟练之前指导他们。人们真的这么做了吗?除非是一个非常小的工程团队,每个人都紧密合作,否则并不多见。别忘了还有那些连技术领导也不知道自己在做什么的团队。

我其实很喜欢基于 Kafka 的架构。对我来说,它就像是一个更好的 AWS SNS(如果我不是那个搭建和维护它的人)。但是,如果我在职位描述中看到它,那么很可能,这个公司的使用方式是错误的,它将成为你日常工作中的麻烦,而不是帮助你的工具。

二.不在乎
回到锤子和螺丝刀的例子,你可能会问,为什么不自己抽时间来学习呢?毕竟,你可以每天花一两个小时阅读一些材料,而且这样做还能拿到薪水(对公司来说也是有帮助的)。

问题在于,大部分人不想学习,不愿意自我提升。是的,程序员应该持续学习,所有这些都是人们在 Reddit 上对新手说的话。但事实是,大多数人并不这么做。

还有另外一类人。他们是优秀的(有时候甚至是杰出的)工程师,他们确切知道自己在做什么,但他们就是不够关心去做正确的事,不够关心去指导别人做正确的事。对他们来说,他们有任务,他们解决问题,他们得到报酬,没有额外的事。对于他们而言,公司哪怕毁灭他们也无所谓。我遇见过一些这样的人,在日常的聊天中他们见识非常深刻,但在会议中却很安静,显得无聊。

“不在乎” 文化是病态的。它会污染周围的一切。终有一天,每个人都会停止尝试去改进事情。他们只是随波逐流,就像下水道里的污物一样(正如我的老师常说的)。这也是为什么我总是建议初级员工至少每年换一次工作的原因之一。在不同的地方学习一点一滴总比卡在一个地方要好。

这种态度也体现在代码审查中,决策中。在招聘中,无能的人招聘其他无能的人,然后这些人又参与招聘。这是指数级的增长。有时候我觉得我们需要实施一种封锁,并为这种态度开发一种疫苗。

三.自负的复杂性
面对现实吧,工程师们,我们大多数人并不聪明。我们只是被引导去相信我们是聪明的。知道如何编程,如何打开终端使用 git grep 并不意味着你聪明或受过教育。充其量,这只能让你接近技术娴熟。在很多国家,STEM 领域的人甚至被认为比学习社会科学的人更聪明(明白了吗?更聪明,哈哈,我有时候会自嘲得让自己发笑)。但事实是,不管人们的兴趣、爱好和工作是什么,人都是愚蠢的。成为一名工程师(即使是有经验的)并不会使你或你的决定变得聪明。

我提起这一点,是因为我认为拥有所谓的 “自负的复杂性”,往往会让我们:

将问题复杂化。比如构建一个带有 10 个不同 Haskell 容器 Lambda 的 Slack 机器人,在 AWS 上通过 SNS 相互通信,通过 Circle CI 连续部署,使用数据下沉到 Athena。现在我们不必自己选择主持站会的人了,它会在每天早上 8 点自动完成!


给同样的东西起不同的名字。适配器?翻译器?门面?代理?装饰器?桥接器?啊,你是说我们可以简单地称之为包装器的那个东西?是的,你可能会争辩说重要的是上下文,但你可以先简单解释一下你试图解决的问题,而不是用你那花哨的术语听起来像个自负的书呆子?同时,当你下次称你基于 Kafka 的系统为 “事件驱动” 时(真的是吗?)也要思考这一点。


看到不存在的问题。是的,我们需要进行大规模扩展,让我们效仿 Twitter 的做法,使用 Java 和 Scala,因为我们将来肯定会在我们这个只有几千专业人士使用的小众应用中遇到同样的扩展问题。当那个未来到来时,我们会说:“开玩笑!我们已经准备好了”。实际上,Java 太慢太老了,让我们用 Elixir 吧。我们有一堆 Ruby 开发者,他们肯定知道如何在其中构建系统,它几乎就是 Ruby,只是更快而且更加符合函数式编程。

等等。我在这里有点跑题了。

不聪明是完全可以的(我肯定不是)。我们只需要避免愚蠢。我们需要保持谦逊,不让自负蒙蔽双眼。如果我们意识到自己不聪明,我们就可以回归基础,保持简单。如果我们不够聪明以预见未来的问题,我们甚至不应该去尝试。相反,让我们解决手头的问题,尽我们最大的努力保持事物的简洁与灵活(参见双向门决策)。这将是一个聪明的决定。

附言:巧合的是,当我在写这部分内容的时候,我在 Reddit 上看到了一个关于软件工程文化中毒性的问题(不幸的是,内容被版主删除了)。其中一个提到的点是试图成为房间里最聪明的人的文化。这种情况经常发生(我自己也有过这种错误)。

四.权限
在这个被炒作驱动的软件工程世界中,我们该如何选择方向呢?我希望我知道答案。我会尝试提供一些想法。

SOLID 系统
让我们谈谈 SOLID 原则。SOLID 原则为人所熟知,但我发现人们几乎总是仅将其与面向对象编程(OOP)联系起来,而在讨论其他编程语言或系统设计时则完全忽视它。事实上,在提到 SOLID 原则适用于非面向对象的环境时,我在会议上被纠正过无数次。但事实是,深入思考后你会发现,每个原则对于整体系统设计都同样适用,不仅仅是对单一代码库。

如我们常说,编程的艺术在于将大问题拆解为小问题,逐个击破。这种分解方法不仅适用于问题,也适用于系统和组织结构(参见康威定律)。无论是组织层面还是系统层面,我们都需要将其合理划分为更小的单元。在每个层面,良好划分的系统单元都应当体现 SOLID 原则所定义的特性。

单一职责原则意味着一个单元应当有明确的职责并始终如一。在代码层面,这意味着你的库、模块、类或函数应仅仅承担一个功能。在系统层面,这同样适用于微服务。实际上,只要服务通过独立的 API 提供功能,它们承担多少职责并不重要。从本质上讲,服务仅是 API 的基础设施单元或连接点,而这些 API 在抽象层次上位于服务之上。在组织层面,团队应有清晰的角色,并理想地负责特定领域内的特定项目。

开闭原则要求单元对扩展开放,对修改封闭。这一原则不仅适用于数据结构的向后兼容性,还适用于服务/API 的设计 —— 它们应允许在现有基础上构建新功能,但禁止修改。这与单一职责原则相辅相成。比如,支付服务应仅提供资金流动功能,与之相关的特定产品业务逻辑应作为独立的 API 存在,这些 API 应使用但不修改原支付服务。在组织层面,团队的角色应该保持稳定,如果角色被淘汰,应解散团队并重新组建。

里氏替换原则表明所有子类都应能代替其父类。这个原则听起来非常面向对象,但其实不是。在代码层面,如果有两个单元实现了相同的接口,它们都应在任何需要该接口的场合下可用。仅仅提供相同的结构或方法是不够的。在系统层面,API 的含义实际上与此相同,即应用程序编程接口。在组织层面,团队应该依靠工作成果而非其他团队。

接口隔离原则强调单元不应依赖它们不使用的其他单元。在代码层面,这意味着使用 B 的 A 不应直接使用 C,即使 B 依赖于 C。在系统层面和组织层面,这同样适用于通信 —— 我们不应该为了从另一个团队获取资源而必须与不相关的人打交道。

依赖倒置原则主张单元不应相互依赖,而应依赖于抽象(如接口和契约)。在代码层面,你通常不希望直接使用其他单元,尤其是在某些语言中,这可能导致测试不佳。这个原则可以被重新解释为 “接口第一,实现第二”。在系统层面,服务间的通信应依赖于契约,因为服务内部往往会变动,有时服务甚至会被替换。在组织层面,契约也比个人更为重要。

这一部分内容很长,但是我觉得还是没讲清楚。

单体应用 vs 服务 vs 代码
至于单体应用与服务的区别,关键在于通信。想象两个相关联的单元:
    module Authorisation
      def allowed?(user, action)
        check_database
      end
    end

    module Users
      def freeze(user)
        if !can_freeze_users?(current_user)
          return auth_error
        end

        do_it_mf
      end

      def can_freeze_users?(user)
        check_permission(user, :freeze_users)
      end
    end
看起来像是典型的单体代码,不是吗?但如果我告诉你这些单元不是来自同一个代码库会怎样?实际上,第一个模块是一个独立的服务。这改变了什么?
单体代码库与面向服务的代码库之间的根本区别在于调用 API 的方式。
    # Monolithic
    Authorisation.allowed?(user, :freeze_users)

    # Service-oriented
    # makes HTTP/GRPC/etc request
    AuthorsationApiClient.allowed?(user.id, :freeze_users)
面向服务的异步系统中,通信将变得不那么直接,具体实现将依赖于具体场景。你可能需要通过订阅变更日志来本地维护一份用户权限的副本。说到技术能力不足,我偏爱服务导向架构的一个原因是,我更愿意拥有几个小服务,这些服务只有我的团队能够接触,这样我们就可以完全控制它们。我不信任其他人,所以我宁可不让他们碰我的东西。当每个人都对所有东西负责(这在单体代码库中很常见)时,往往没什么事情能做成。

另外,支持单体应用的人常说,一个写得好的单体应用总是可以轻松地拆分出来。这话没错。但是,你的组织有多大概率能够写出这样的单体应用呢?

思考开发者体验
我们总是在谈论 UI/UX,用户至上,逆向工作 —— 许多公司都在模仿亚马逊的这些原则。但人们并不总是想到,面对的客户并不总是商业客户。当你编写代码或创建项目时,你的同事(包括你自己)也成了你的用户,因为他们将要使用你刚写的代码。他们会阅读它,他们会修改它。为你的用户考虑人体工程学。

测试你的系统/服务容易吗?本地运行容易吗?我们可以轻松地发现并解决生产问题吗?我们需要文档吗?是否有清晰的方法来解决特定问题(现有的代码应该鼓励编写良好的代码)。

保持简单
保持简单。仅仅是你所在的组织把事情搞复杂了,不意味着你也必须这么做。当问题影响到系统层面时,你应该跟你的技术领导讨论,让他们来处理任何反对意见。他们是你处理外界问题时的最佳伙伴(或者至少应该是)。时刻运用常识,并且请学会识别一次性和双向门决策,并据此采取适当行动。

全力以赴并不可取
在软件开发领域,“全力以赴” 或 “孤注一掷” 并非总是明智的选择。服务或单体架构似乎是好主意,但这并不意味着必须走到极端,可以采取混合策略。人们往往持 “非此即彼” 的态度,但这种做法不实际且往往是危险的。

我们应始终运用常识。此外,你可能已经注意到,许多初创公司突然决定转向服务。当他们如此激进地做出改变时,事情就会变得失控。最好是逐步进行,并由有能力的人负责监督。为 “微服务” 添加额外功能并不丢人。但要确保这些功能不会与现有功能纠缠在一起。如果需要,你可以稍后再将它们提取出来(剧透:YAGNI)。

所有权
单一责任和开闭原则拥有几项服务的所有权,不要让任何人碰它们。理想情况下,甚至自己都不要碰它们。所有变更/功能请求都必须通过特定的沟通渠道提交到你的积压工作中。如果有多个紧急请求,那就很难办了,让他们滚蛋吧。不,我们不在乎你的 KPI。我们有自己的 KPI。如果你总是让他们把 “紧急” 的事情推给你,他们就会一直这样做。时不时表明立场。一味妥协只会拖慢工作进度,公司也无法扩大规模(这样的例子比比皆是)。

在我目前的工作场所,所有权是我最大的不满。有 3 个不同的团队在开发我的软件仓库。这到底是怎么回事?最近我看了一眼代码,发现有些地方我都不认识了。有些改动甚至把我们团队搞得一团糟(幸好不太严重)。

这在不同层面上造成了很多不同的问题:

动机。只有你的团队在做的事情,你才更有可能去管它。你想保持整洁干净,这是你自己的事。当共享所有权时,这种想法就会消失。正如我在上文所说的(虽然我很确定是亚里士多德先说的),如果每个人都拥有一切,实际上没有人会去负责。


缺乏沟通。如果没有适当的所有权,决策过程中往往缺少必要人员的参与。他们的注意力也会因为被拉扯到太多(有时不相关的)事务上而分散。

沟通过度。一些相关人员参与不够,但一些完全不相关的人却频繁参与。这就降低了会议的质量,并要求增加更多的会议。因此,缺乏沟通也会导致沟通过多。  


知识折旧速度。如果人们所从事的工作经常被不同的人改变,他们对系统的整体理解就会受到影响。你积累的所有知识都会加速退化。你今天所了解的知识明天就会失效。当你只需要了解系统中你自己的部分,而所有的变化都要经过你和你的团队时,情况就不是这样了。  

无法估算,期限紧迫。由于每个人都可以随时随地进行更改,管理层就会设定期限,并期望单个团队给出估计。如果估计的完成时间太长,他们就会投入更多的人手来处理(按照 “两个女人可以在四个半月内生出一个婴儿” 的逻辑)。


问题在于,因为人们在同一个问题上相互干扰,这会造成额外的障碍和不必要的对话。同时,由于知识迅速过时,团队甚至无法准确估计完成特定服务的工作所需的时间。如果有了明确的所有权,你就可以联系相关团队,他们或多或少都会对自己的工作和潜在的障碍做出精确的估计。  


糟糕的代码就像滚雪球一样越滚越大。你是否曾经需要在一个充满糟糕代码的文件中添加更多测试或一个额外的功能,但你又无法合理地编写你的代码,因为你必须遵循已有的模式?是的。不拥有东西就会制造更大的问题。人们不在乎,有时人们甚至无法确认糟糕的代码是不是有其特殊的理由。这显然会减慢未来产品功能的交付速度。它也使得向你心仪的 “合适” 的微服务过渡变得更加不可能,因此你最终会陷入一种尝试全力以赴但却失败的境地。糟糕的代码会导致更多糟糕的代码,这是一个指数级的恶化过程。

人力资源成本。上面所有的事情都导致必须雇佣更多的人,以及更多的人离职(并且需要替换这些人)。组织得当的团队会工作得更有效。不管你是否雇佣了出色的开发者,如果你不能合理地管理他们,这都无关紧要。

我现在意识到,因为它对整个组织和产品产生蝴蝶效应/多米诺效应,我可以写这个列表很长时间。所以我就此打住。

治理
我之前说过,即使你有出色的架构师,如果没有人去实践他们的规划,这些架构师也毫无价值。仅仅告诉人们 “我们现在要追求事件驱动” 是不够的。你需要确保他们真正理解这意味着什么,并且能够实际执行。我们如何实现这一点?我也不确定。我认为,或许一个更严格的层级结构可能有所帮助:架构师指导团队领导,团队领导管理他们的团队,并向架构师汇报。工程经理应该推动这种层级结构的执行。是的,这听起来有些限制性。但我们是一个组织,不是一群孤立的个体,我们需要高效地协同工作,而不是单打独斗。

如果你不关心公司,那么至少要关心你自己,并且要敢于发声

老实说,我理解那种 “无所谓” 的态度。毕竟,处在风险中的不是你的生意。但你肯定不想只是混日子。你肯定不愿意在糟糕的代码和任务中煎熬,只是为了某个只想达成 KPI 指标的无能经理做些毫无价值的任务。不要担心公司的成功。想想你自己的舒适度。我们可以做些什么改变,让你感觉更好一些?找出这些问题并提出来。


是的,提出问题并不容易。有时你可能想要避免冲突。在这种情况下,找一个愿意倾听的人。找一个不怕冲突的人。找一个能尝试实现变革的人。这可能是你的经理,或是团队领导,甚至是与你或你的团队不相关的人。但你需要与某人讨论这些问题。如果你不这么做,一切都将保持不变。

正如人们在伦敦地铁上所说:“看到问题,说出问题,解决问题。”

五.结论
我承认这个标题和文章内容有些尖锐。但这就是生活。所有观点都仅代表我个人。

我还意识到,我可能让人误解,以为所有问题都是由不称职的工程师造成的。实际上并非如此。工程师的水平一般都处于平均水平。所以那些在 LinkedIn 上辞职,然后称赞与他们工作过的人是聪明绝顶的,让我感到不适。

在这里,当我谈到无能时,我是指整个组织的无能。当无能的人担任领导角色时,就会造成最大的破坏。效率低下的管理会导致员工效率低下。软件工程很简单(对所有精英主义的守门人说:去你的)。它是一门简单的手艺,与锁匠工作没有太大区别。它并没有什么浪漫可言。你不需要是个天才才能精通它。该死的,你甚至不需要受过教育。但是,如果你的组织混乱无序,你的工作也会一团糟。俗话说得好,上梁不正下梁歪。所以我们需要从高层解决问题,而不是从底层。

用户评论