微信营销过程中存在的问题(简历驱动开发?微服务中的几种失败路径)



本文要点:


  • 微服务是手段而不是目的。
  • 分布并不能保证解耦。
  • 契约测试是所有微服务架构的重要组成部分。
  • 前端、后端和集成层,以及业务逻辑都需要分解。
  • 如果企业无法快速独立的发布微服务,那么微服务的很多好处都会丧失。

  • 在去年11月的QCon Plus上,我介绍了一些微服务可能误入歧途的路径。我是IBM的顾问。我的部分工作是帮助企业向云原生迁移。本文提到的问题都是我从经验中总结出来的——可惜在实践中非常普遍。


    我看到的第一个问题是,有时候我们甚至不知道问题出在哪里。我们觉得应该做微服务,但是并没有真正花足够的时间去发现为什么要做。


    我们要解决什么问题?我们现在遇到了哪些痛点?我们做了微服后,会有哪些改进?人们总是倾向于尝试新事物,尤其是对我们这些技术人员来说。我们想一步一步跳到解决方案,我们想玩闪亮的新东西。可悲的是,尽管找出我们想要解决的问题非常重要,但这远不如尝试新的解决方案有趣。


    容器加速了我们尝试新解决方案的趋势,因为它是一项神奇的技术和伟大的解决方案。它们是如此的轻和便携,以至于很多东西都可以变得更好。所以最后我们下定决心:“因为我已经有这么多容器了,如果我只在一个容器中运行我的应用程序,那将是对容器容量的严重浪费。我应该在尽可能多的容器中运行它!”不幸的是,“没有足够的容器”不是问题的有意义的表达。

    简历驱动的开发


    我看到的另一个问题是简历驱动的开发。我们看了一下简历,里面应该写“微服”的地方空是空白的。这看起来还不够好,所以我们会说,“我可以通过重构公司的技术栈来解决这个问题”。你可能会想,“不,这太夸张了,霍莉。肯定没有人会真的为简历做架构决策吧?”事实证明...他们会的。



    Red Hat公司最近进行了一项调查,研究基于容器的发展的主要驱动因素,发现职业发展是头号驱动力。所谓的职业发展,不过是简历驱动发展的高情商说法。


    简历上缺乏开发微服务的经验是一件大事,因为微服务简直就是目前的新标准。即使疫情以来我们没有加入离职潮,我们此刻也没有在找新的工作,我们不想与众不同。当我们环顾四周,发现别人好像都在做微服务,那么我们当然会想:如果他们都在做微服务,我不做会不会有什么问题?我把这种心理称为“微服羡慕”。

    微服务不是目标


    “微服羡慕”是个问题,因为微服不是我们应该羡慕的那种东西。我们的一个顾问有一个诀窍。如果一个客户一直在谈论网飞,并希望转向微服务,他知道这种合作有问题。几乎可以肯定的是,他们没有正确的理由转向微服务。如果对话深入,涵盖耦合和内聚,他就知道合作方向没有问题。


    微服务转型的起点不应该是微服务本身。微服务是实现业务敏捷性/弹性等更高层次目标的手段。其实微服务甚至不是唯一的手段,只是一种选择。

    分布式单体


    有一个问题一定要问:“你做的是微服务,还是分布在几百个Git仓库的单个实体?”可惜这是我们经常看到的。这是一个分布式的实体,也是一个可怕的存在。很难推理。它比普通单体更容易出错。在传统的单体中,一切都包含在单一的开发环境中,从中可以获得一些好处,比如编译时检查和IDE重构支持。因为你总是在一个进程中执行操作,所以函数的执行会得到保证。你不用费心去记那些分布式计算错误,不用担心服务发现,也不用去处理你试图调用的东西已经不复存在的情况。正常单体中的一切都是相对安全的。相比之下,如果我们去掉单体的安全性,但保留其耦合性,最终会得到原来的带云的意大利面。

    分布不等同于解耦


    几年前,我被叫到一个有问题的项目去执行救援任务。我到岗位后,团队对我说的第一句话就是“每当我们换一个微服务,另一个就会坏”。如果你一直关注微服务的承诺,你就会知道这种情况和应该发生的完全相反。微服务应该是独立的,解耦的。但是,如果您只是分发您的系统,解耦将不会是一个免费的奖励。“分布式”和“解耦”的共同点是两个词都是以D开头,但不是一回事。



    我们很容易陷入这样的困境:拥有一个高度分布式的系统,拥有分布式带来的所有痛苦,仍然完全纠缠耦合在一起。这就是上面案例中发生的情况。当我开始探索他们的代码库时,我一直在每个仓库中看到相同的代码。这个应用程序的对象模型相当复杂,大约有20个类,其中一些有70个字段。这是一个复杂的模式。


    微服务开发的一个原则就是放下干巴巴,避免使用常用库,因为它们是耦合的源头。在这种情况下,为了避免中央对象库的耦合,每个微服务在其代码中都有一个剪切粘贴对象模型的副本。然而,如果领域模型仍然是共享的,耦合仍然是不可避免的。复制目标代码并没有消除耦合,只是消除了编译时检查的可能性。字段名的改变仍然会破坏一切,但是这种破坏直到运行时才会发生。



    这个悲伤的故事说明了域驱动设计原则在微服务中的重要性。我们想要实现的理想是,每个微服务都可以整齐地映射到一个域。这样做的一个副作用也是你做得对的标志,就是你的微服务会有一个小界面。如果沿着技术边界而不是领域边界分解,最终会出现我看到的那种情况。每个微服务都有一个巨大而脆弱的接口。结果会是一团意大利面。

    火星气候轨道航天器


    虽然从技术上来说它是一个航天器,而不是一个微服务平台,但火星气候轨道航天器显示了分布和解耦的区别。美国宇航局于1998年发射了火星气候轨道航天器。它的任务是研究火星的气候。不幸的是,飞船未能在轨道上绕过火星;相反,探测器坠入了火星大气层。美国宇航局的事后调查发现,问题源于两个不同控制系统之间的关系,这两个系统是由不同的团队建造的。大多数时候,转向操作是由探测器本身的系统完成的。每隔几天,当轨道飞行器进入地球视野时,佛罗里达州的监控系统就会发出一个路线修正指令。这可能是一个系统所能做的最分散的设计;一部分是在太空飞行空。但这两个系统之间的领域实际上是相似的:它们都在处理发动机推力的计算。两个团队在交流中没有搞清楚接口应该是什么样子,最后用了不同的计量单位——泰空部分用公制单位,地球部分用英制单位,于是灾难发生了。我们可以有把握地说,在这种情况下,系统非常分散,但这种分布没有任何好处。

    消费者驱动的契约测试


    上面提到的微妙的沟通问题经常发生在多个团队参与开发的场景中。幸运的是,有一个非常有用的缓解措施:消费者驱动的契约测试。在IDE不会帮助我们检查类型的系统中,我们需要测试各种集成,但是我们希望尽量减少全面的集成测试。集成重,运行成本高,脆弱,耦合。如果我们已经投入了微服务的开发,我们当然不希望在测试的时候回到一个巨大的集成单体。那么,我们如何确定我们所建立的是真正有效的呢?


    Mock是一种常见的解决方案,但是它有自己的问题。为了设置mock,制作团队和消费团队会在开发之初讨论界面应该是什么样子。他们达成协议,然后消费者试图写一个mock,这就是他们认为制作团队在谈论的内容。理想情况下,他们会做得很好。问题是,他们也将自己的假设写入了mock,因为他们编写了mock,他们可能不知道其他代码应该是什么样子或行为,因为这不是他们编写的代码。



    运气好的话,他们会得到正确的结果。单元测试全部通过,集成阶段绿灯继续,一切正常。可惜这种运气不是每次都有。有时候,实际执行和消费团队理解的不一样,要么是制作团队改变了主意,要么是有人在某个位置做了不正确的假设。在这种情况下,测试仍然会通过。然而,当我们真正开始整合真正的服务时,它就会失败。问题是,mock的行为并没有得到真实服务的验证。制作团队可能从来没有看到过创造的模拟。


    更好的选择是做一个消费者驱动的契约测试。契约测试的优势在于它不同于mock,因为双方都会与契约测试进行交互。对于消费者来说,合同测试就像一个方便的模拟。




    另一方面,契约测试对于提供者来说是一种方便的功能测试。它是一种更深层次的验证措施,而不仅仅是像OpenAPI那样的语法检查。契约测试实际上是对语义和行为的检查,节省了提供者团队编写功能测试的时间。


    如果一切都兼容并正常工作,那么所有的合同测试都将通过。它可以迅速增强团队的信心,因为它们运行起来既便宜又轻便。如果提供者团队破坏了某些东西,他们的测试将会失败,并在破坏性的更改泄漏到集成环境之前给出早期警告。如果API发生变化,双方(或连接双方的经纪人)之间将启动新版本的合同。


    现在市场上有几种合同测试系统。如果你在Spring生态系统,Spring Contract非常有用。如果你涉及的范围很广,那么我推荐Pact,它绑定了你可能用到的几乎所有语言。

    企业毛球


    当然,即使我们解决了所有的测试问题,即使我们在业务逻辑层有一套漂亮的解耦微服务,也不能保证我们一定会成功。系统中还有许多其他元素,在设计真正干净的微服务架构时可能没有考虑到。我们对业务逻辑如此兴奋,以至于忘记了前端和后端的事情以及所有的胶水。胶水在企业架构中尤其常见,而且非常粘。我们的一位建筑师称这种情况为企业毛球。


    如果把所有的功能分解工作都集中在业务层,最终会得到一堆整齐的解耦微服务,夹在一个单体的前端和一个单体的数据库层之间。在这些类型的系统中,很难做出改变。但是,从行业角度来看,我们正在进一步分解数据库,将其映射到各种微服务,我们正在开发各种微前端。


    但是我们还没有完成分解。如果系统不是很复杂,我们会有一个集成层。这一层可能负责消息传递,或者一些其他的集成解决方案,它们负责将复杂的系统集合在一起。即使在架构的其他部分现代化之后,集成层通常也是单一且不灵活的。团队本身可能压力很大——我同事称之为“恐慌三明治”。因为集成层是单片的,他们必须仔细安排所有的更改,这妨碍了其他人的工作。



    这种现象可能会带来很多挫折,尤其是对于整合团队来说。从外表上看,他们似乎很慢很慢,尽管他们很努力。为了解决耦合问题,我们需要采用模块化集成模式。


    如果不把集成层、数据库、前端层分开会怎么样?几乎可以肯定,我们的微服务是达不到预期效果的。球的各部分之间的依赖性会让任何一部分都无法快速移动。业务层的微服务不会独立部署,部署的节奏会有明显的间歇性。

    阻碍发布的累赘


    你们中有多少人见过这样的情况?你非常努力,创造了一些惊人的东西。你知道用户会喜欢它,但它还没有交付给他们。价值在架子上,你的惊人成就无法发表。即使你有微服务架构,有发布板,你也无能为力。其他所有微服务都需要同时发布,因为需要一起测试;除了批量测试,成本太高。光是填出版单也很贵。企业害怕出版,因为它过去从劣质版本中吸取了教训。发布清单、发布委员会、单线程测试和其他发布咒语都是为了减少感知风险。因为整个机构的发布截止日期是统一的,所以只能争分夺秒赶在截止日期前插上各种功能。当然,这也使得出版风险更大。有人在跟踪一个所有微服务之间存在依赖关系的电子表格,这些微服务的耦合度超出了预期的水平。当然,发布日期也必须是一个吉祥的日子。我们选择微服务架构的时候,没想到会陷入这样的困境!所有这些用心良苦的流程都很繁琐,拖累了价值到达用户的过程,而且往往增加了实际风险。

    测试自动化


    为什么会这样?通常,我们之所以如此害怕发布,是因为在发布过程中涉及到大量的手工工作。更何况真正给我们信心的测试并不是自动的,所以我们需要做大量的工作来发现应用程序是否能正常工作。当我拜访一个客户,听到他抱怨“我们的测试没有自动化”时,我听到“我们不知道我们的代码目前是否正常工作。可能是正常的。上次我们做人工QA,效果不错;我们希望它仍能正常运行”。这是一个可悲的局面。


    如果你关心它,就自动化它——你应该关心质量。尤其是当架构已经变成一团乱七八糟的意大利面,耦合已经悄然出现的时候,很有可能出现断点。去除意大利面是非常困难的,所以要在一个可以快速反馈的地方尽快找到断点。如果你想做意大利面,至少要做经过测试的意大利面。

    发布周期


    手动测试只是发布过程的一部分。在受监管的行业或注重合规的行业,几乎总会有大量的人工合规工作。法规遵从性是我们非常关心的问题,因此我们应该实现自动化。


    由于所有这些手动流程和所有这些阻碍,这意味着即使我们部署到云,我们也无法获得云最初承诺的好处。我们正在使用的云似乎不是美丽的云。具有讽刺意味的是,在云中,那些让我们更安全的好东西、好想法和东西实际上正在伤害我们。旧的治理机制在云中不起作用。无法达到预期的业务效果,也让我们失去了很多云应该有的业务优势。


    通过观察企业发布周期,很容易发现企业是否实现了云计算承诺的好处。几年前,我的一个同事和一家大型传统银行开了一个销售会议。他们的市场份额正在被金融科技公司和新兴的挑战者银行竞争对手蚕食。这家公司也知道他们为什么会输——因为他们不能快速跟上市场。他们找到我们,解释说他们有很多COBOL资产,这是拖累他们的原因。(这大概是真的。然后他们又补充说,他们显然需要摆脱所有的COBOL,转向微服务,因为别人都在做微服务。然后他们说他们的出版委员会一年才开两次会。这时候,同事的心凉了半截。如果你的发布委员会六个月才开一次会,你就知道你的发布节奏会是六个月一次。您有多少个可以独立部署的微服务并不重要。如此缓慢的步伐意味着你无法获得敏捷性。



    这家银行需要的帮助实际上不是技术性的;他们需要改变对风险的思考方式和经营方式。他们的发布计划需要彻底的改革,他们需要大量的自动化。缺乏持续交付的纪律是阻碍他们的原因。COBOL只是一个失败者。


    “我想解耦”是一个常见的客户需求,但是解耦意味着不止一件事。当我们想要一个解耦的应用程序时,这并不能保证模块化。有时候只是说明乱七八糟的东西分散的更广。如果有一些外部约束,比如发布板,过时的流程,让我们止步不前,那么在解决这些问题之前,解耦与否并不重要。

    微信营销的问题

    您可以还会对下面的文章感兴趣

    使用微信扫描二维码后

    点击右上角发送给好友