zoukankan      html  css  js  c++  java
  • 7.8拯救消费者驱动的测试

     
    使用之前所提到的端到端测试,我们试图解决的关键问题是什么?是试图确保部署新的服 务到生产环境后,变更不会破坏新服务的消费者。有一种不需要使用真正的消费者也能达 到同样目的的方式,它就是CDC (Consumer-Driven Contract,消费者驱动的契约)。
     
    当使用CDC时,我们会定义服务(或生产者)的消费者的期望。这些期望最终会变成对 生产者运行的测试代码。如果使用得当,这些CDC应该成为生产者CI流水线的一部分, 这样可以确保,如果这些契约被破坏了的话,生产者就无法部署。更重要的是,从测试反 馈周期的角度来看,因为只需针对生产者运行这些CDC测试,所以它比要解决同样问题 的端到端测试更快,也更可靠。
     
    让我们再看一下客户服务的这个例子。客户服务有两个相互独立的消费者:帮助台和网络 商店。这两个消费者都有对客户服务的某些期望。在这个例子中,我们将创建两个测试集 合,每个集合分别体现帮助台和网络商店对客户服务的使用方式。一个好的实践是,生产 者和消费者团队协作来写这部分测试,所以,帮助台和网络商店的团队成员可以跟客户服 务的团队成员结对来编写这些测试。
     
    因为这些CDC是对客户服务如何工作的期望,所以如图7-9所示,客户服务本身的所有下 游依赖都可以使用打桩。从测试范围的角度来看,如图7-10所示,它们与测试金字塔中的 服务测试处在同一层,但侧重点却非常不同。这些测试侧重在消费者如何使用服务,测试 失败的解决方式与服务测试相比会有很大的不同。如果在客户服务的构建过程中一个CDC 失败了,消费者很明显将会受到影响。此时你可以选择修复这个问题,或者如我们在第4 章中所提到的,启动一个引人破坏性变化的讨论。所以通过CDC,无需使用时间可能很长 的端到端测试,我们就可以在进入生产环境之前发现破坏性变化。
     
    图7-9:客户服务的消费者驱动的测试
     
    图7-10:把消费者契约的测试集成到测试金字塔
     
    7.8.1 Pact
     
    Pact (https://github.com/realestate-com-au/pact)是一个消费者驱动的测试工具,最初是在幵 发RealEstate.com.au的过程中创建的,现在已经开源,功能大部分是由Beth Skurrie组织 开发的。该工具最初是使用Ruby语言,现在支持包括JVM和.NET的版本。
     
    Pact的工作方式非常有趣,如图7-11所示。幵始时,消费者使用Ruby DSL来定义生产 者的期望。然后启动一个本地mock服务器,并对其运行期望来生成Pact规范文件。Pact 规范文件是一个标准的JSON规范,所以事实上,你可以手写该规范,但使用语言支持的 API来生成该规范显然要容易得多。同时,它还能提供给你一个mock服务器,以后可用 来独立地测试消费者。
    图7-11:概述Pact如何实现消费者驱动的测试
     
    在生产者这边,你可以使用JSON Pact规范来驱动对生产者API的调用,然后验证响应以 测试消费者的规范是否被满足。因此生产者代码库需要访问Pact文件。正如我们在第6章 所讨论的,我们期望消费者和生产者是异构的,所以与语言无关的JSON规范是一个非常 好的选择。这意味着,你可以使用Ruby的客户端来生成消费者规范,然后在Pact的JVM 版本上用该规范来验证一个Java的生产者。
     
    Pact的JSON规范是由消费者生成的,该规范需要成为一个生产者可访问的构建物。你可 以把它存储在CI/CD工具的构建物仓库中,或者使用Pact Broker, Pact Broker允许你存 储Pact规范的多个版本。这就允许你针对消费者的多个不同版本运行消费者驱动的契约测 试。比如说,假如你想测在生产环境上的消费者,也想测开发中的消费者的最新版本,这 个功能就会很有用。
     
    容易让人混淆的是,ThoughtWorks 有一个叫作 Pacto (https://github.com/thoughtworks/ pacto)的开源项目,它也是一个用于消费者驱动测试的Ruby工具,它可以通过记录消 费者和服务之间的交互生成规范。这使得为现有服务编写消费者的契约相当容易。通过 Pacto生成的这些规范或多或少是静态的,而在使用Pact时,消费者的每次构建都能生成 新的规范。事实上,你甚至可以为未实现的生产者定义预期规范,这可以成为仍在(或尚 未)开发的生产服务工作流的一部分。
     
    7.8.2关于沟通
     
    在敏捷中,故事通常被认为是一种促进沟通的方式。CDC也起到类似的作用。它们可以 推动关于如何编写一组服务的API的讨论,当其被破坏时也可以触发API该如何演进的 讨论。
     
    重要的是,CDC需要消费者和生产服务之间具有良好的沟通和信任。如果双方都在同一 个团队(或就是同一个人!),那么这应该不难。然而,如果你消费的服务由第三方提供, 那么CDC可能不适用,因为你们可能缺乏充分的沟通及信任。在这种情况下,对有可能 出错的组件不得不使用有限的大范围的端到端测试。换一个场景,如果是为成千上万的潜 在消费者创建API (比如一个公开可用的Web服务的API),你可能不得不自己扮演消费 者(或者说一部分消费者)的角色来定义这些测试。破坏大量的外部消费者是非常糟糕的 事情,这种情况下CDC就显得尤为重要!
     
    7.9还应该使用端到端测试吗
     
    本章之前的内容详细地描述了端到端测试的大量缺点,而随着测试覆盖的服务数量的增 加,这些缺点会更加凸显。一段时间以来,通过跟实施大规模微服务的人一直保持交流, 我意识到随着时间的推移,大部分人更喜欢使用类似CDC的工具和更好的监控来代替端 到端测试。但这并不意味着端到端测试应该被全部扔掉。他们会在使用一种叫作语义监控(semantic monitoring)的技术来监控生产系统时,用到端到端场景测试,我们在第8章会 讨论更多这方面的内容。
     
    可以把运行端到端测试当作把服务部署到生产环境的辅助轮。当你正在学习使用CDC及 提高生产环境的监控和部署技术时,这些端到端测试能形成一个有用的安全网,你可以认 为这是在周期时间和低风险之间做取舍。不过在改善其他方面时,你可以慢慢减少对端到 端测试的依赖,直至完全不需要。
     
    同样,你可能处在一个对从生产环境中学习没什么兴趣的工作环境,大家更愿意在部署到 生产环境之前,尽可能努力地消除所有缺陷,即使这意味着发布软件需要更长的时间。你 要知道,再怎么测试也不可能消除所有的缺陷,所以生产环境中有效的监控和修复还是有 必要的。理解了这一点,你就能够理解从生产环境中学习是一个明智的决定。
     
    当然,对你所在组织的风险,你理解得要比我多很多,但在这里我想促使大家多思考一 下,有多少端到端测试是我们真正需要的。
     
    7.10部署后再测试
     
    大多数测试会在系统部署到生产环境之前完成。我们通过测试定义一系列的模型,希望证 明在功能需求和非功能需求方面,系统的工作方式和行为都符合预期。但如果我们的模型 并不完美,那么系统在面对愤怒的使用者时就会出现问题。缺陷会溜进生产环境,新的失 效模式会出现,用户也会以我们意想不到的方式来使用系统。
     
    我们对此通常的反应是,定义更多的测试来改进我们的模型,以便将来尽早捕获更多的问 题,从而减少发生在生产环境中缺陷的次数。然而我们必须承认,使用这种方法得到的收 益会逐渐减少。仅仅依靠部署之前进行的测试,我们不可能把缺陷率降为零。
     
    7.10.1区分部署和上线
     
    在更多问题发生之前捕获它们。要达到这个目的,一种方式是突破传统的在部署之前运行 测试的方法。如果可以部署软件到生产环境,在有真正生产负载(production load)之前运 行测试,我们可以发现特定环境中的问题。一个常见的例子是,用来验证部署后的系统是 否正常工作的、针对新部署软件的一系列的冒烟测试套件。这些测试帮助我们识别与环境 有关的任何问题。如果你能够使用一条命令来部署任何给定的微服务(应该这么做),应 该把自动运行冒烟测试也加到这条命令中。
     
    另一个例子是所谓的蓝/绿部署。使用蓝/绿部署时,我们会部署两份软件,但只有一个 接受真正的请求。
     
    让我们考虑一个简单的例子,如图7-12所示。在生产环境中,我们使用客户服务的vl23版本。我们想要部署一个新版本v456。vl23正常工作的同时,我们部署v456版本,但先 不直接接受请求。相反,我们先对新部署的版本运行一些测试。等测试没有问题后,我们 再切换生产负荷到新部署的v456版本的客户服务。通常情况下,我们会保留旧版本一小 段时间,这样如果我们发现任何错误,能够快速恢复到旧的版本。
     
    图7-12:使用蓝/绿部署区分部署和上线
    实施蓝/绿部署有几个前提条件。首先,你需要能够切换生产流量到不同的主机(或主机 集群)上。切换可以通过改变DNS条目,或更改负载均衡的配置。你还需要提供足够多 的主机,以支持并行运行两个版本的微服务。如果你正在使用一个弹性云提供商,这个要 求对你来说可能很简单。使用蓝/绿部署可以降低风险,也让你有能力在遇到问题时尽快 恢复。如果做得足够好,整个过程可以完全自动化,在无需人工干预的情况下完整地部署 或恢复。
     
    保持旧版本运行,除了给予我们在切换生产流量前可以测试服务这个好处外,还可以大幅 度地减少发布软件所需要的停机时间。使用某些生产流量重定向的机制时,我们甚至可以 做到在客户无感知的情况下进行版本切换,达到零宕机部署。
     
    还有一种方式值得我们详细讨论一下,它有时会与蓝/绿部署相混淆,因为它会使用一些 类似的实现技术。这种方式被称为金丝雀发布(canary releasing)。
     
    7.10.2金丝雀发布
     
    金丝雀发布是指通过将部分生产流量引流到新部署的系统,来验证系统是否按预期执行。 “按预期执行”可以涵盖很多内容,包括功能性的和非功能性的。例如,我们可以验证新 部署服务的请求响应时间是否在500毫秒以内,或者査看新服务和旧服务是否有相同的错 误率比例(proportional error rate)。甚至更进一步,如果我们要发布一个新版本的推荐服 务,可以同时运行两个版本,然后看看新版本的推荐服务是否能够达到预期的销售量,以 确保我们没有发布一个次优算法的服务。如果新版本没有达到预期,我们可以迅速恢复到 旧版本。如果达到了预期,我们可以引导更多的流量到新版本。金丝雀发布与蓝/绿发布 的不同之处在于,新旧版本共存的时间更长,而且经常会调整流量。
     
    Netflix广泛使用这种方法。发布前会部署新的服务版本,同时部署一个与生产环境相同版 本的作为基线。然后,Netflix会在几个小时内,引导一小部分生产流量到新版本和基线 上,同时为两个计分。如果金丝雀的分数高于基线的分数,Netflix才会全面部署新版本到 生产环境。
     
    当考虑使用金丝雀发布时,你需要选择是要引导部分生产请求到金丝雀,还是直接复制一 份生产请求。有些团队选择先复制一份生产请求,然后引导复制的请求到金丝雀。使用这 种方法,现运行的生产版本和金丝雀版本可以有相同的请求,只是生产环境的请求结果是 外部可见的。这方便大家对新旧版本做比较,同时又避免假如金丝雀失败,影响到客户的 请求。不过,复制生产请求的工作可能会很复杂,尤其是在事件/请求不是幂等的情况下。
     
    金丝雀发布是一种功能强大的技术,帮助大家用实际的请求来验证软件的新版本,同时可 能推出一个糟糕的新版本,提供工具来帮助控制风险。不过,它也确实比蓝/绿部署需要 更复杂的配置和更多的思考。你可以比蓝/绿部署共存多版本服务的时间更长,不过也会 比蓝/绿部署占用更多,时间更长的硬件资源。你还需要更复杂的请求路由,因为为了对 发布工作更有信心,你可能需要增加或减少请求。不过如果你已经实现蓝/绿部署,那实 现金丝雀发布需要的部分构建块可能已经有了。
     
    7.10.3平均修复时间胜过平均故障间隔时间
     
    通过使用蓝/绿部署和金丝雀发布技术,我们找到了方法,在类生产环境(甚至就是生产 环境)上测试,我们还构建工具来帮助管理有可能发生的失败。使用这些方法其实是,默 认我们无法在软件发布之前,发现和捕获所有的问题。
     
    有时花费相同的努力让发布变更变得更好,比添加更多的自动化功能测试更加有益。在 Web操作的世界,这通常被称为平均故障间隔时间(Mean Time Between Failures,MTBF) 和平均修复时间(Mean Time To Repair,MTTR)之间的权衡优化。
     
    减少修复时间的技术可以简单到尽快回滚加上良好的监控(在第8章我们将讨论),类似 蓝/绿部署。如果我们能早点发现生产中的问题,尽快回滚,就可以减少对客户的影响。 我们还可以使用蓝/绿部署技术,部署软件的一个新版本,在生产环境对它进行测试之后 再引导用户到新版本。
     
    对于不同的组织,MTBF和MTTR之间的权衡会有所不同,这取决于对在生产环境中失败 带来的影响的正确理解。然而,我看到大多数的组织,花费了大量的时间编写功能测试套 件,而花费很少的时间,甚至完全没有考虑如何更好地监控和如何从故障中恢賢。因此, 尽管他们可能在部署生产环境前发现并消除了很多缺陷,但是也无法保证能够消除所有的 缺陷,并且假如有些缺陷在生产环境中真的出现,他们根本没有做好任何应对准备。
     
    除了 MTBF和MTTR之外,还有别的权衡存在。例如,如果你正试图了解是否有人会真正使用你的软件,那需要尽快发布软件,这比构建健壮的软件更有意义,因为可以验证之 前的想法或业务模型是否工作。在上面例子的情况下,测试可能都是多余的,因为不知道 你的想法是否工作,其影响要远远大于生产环境上的一个缺陷。在这种情况下,在生产之 前完全不测试是非常明智的。
     
    7.11跨功能的测试
     
    本章大部分内容集中在讨论测试特定的功能,以及这种功能测试在微服务系统下有哪些不 同。不过,还有一种类型的测试也是非常重要的,值得我们在这讨论一下。非功能性需 求,是对系统展现的一些特性的一个总括的术语,这些特性不能像普通的特性那样简单实 现。它包括以下方面,比如一个网页可接受的延迟时间,系统能够支持的用户数量,用户 界面如何让残疾人也可以访问,或者如何保障客户数据的安全。
     
    非功能性这个术语一直很困扰我。这个术语所涵盖的一些内容,本质上似乎也是功能性的 啊!我的一个同事 Sarah Taraporewalla,使用跨功能需求(Cross-Functional Requirement, CFR)来替换非功能需求,我非常喜欢这个术语,因为它展现了这样一个事实,这些系统 行为仅仅是许多横切工作融合的结果。
     
    如果不是大多数,很多的CFR只能在生产环境测试。也就是说,我们可以定义一些测试策 略来帮助我们看看,是否至少是朝着满足这些目标的方向前进。这些测试归类为属性测试 象限。性能测试是其中一个很好的例子,我们马上会深入地讨论。
     
    对于一些CFR,你可能希望在一个单独的服务上跟踪。例如,你可能希望你的支付服务的 持久性明显高一些,而音乐推荐服务则允许更多的停机时间,因为你知道,即使10分钟 左右无法推荐类似干金属乐队的艺术家,你的核心业务也不会受影响。这些权衡最终会对 你如何设计和演化系统有一个比较大的影响,再强调一次,合适粒度的微服务会给你更多 的机会做这些权衡。
     
    CFR的测试也应该遵循金字塔。一些测试必须使用端到端,例如负载测试,但其他不需要。 例如,一旦你发现一个端到端的负载测试的性能瓶颈,编写一个小范围的测试,帮助你在 未来发现这个问题。其他CFR的测试很容易使用更快的测试。我记得在一个项目中,我们 坚持确保HTML标记使用适当的可访问性特性,来帮助残疾人使用我们的网站。检查生成 的标记来确保适当的特性,不需要任何网络的往返,很快就可以完成。
     
    考虑CFR时常太迟了。我强烈建议尽早去看CFR,并定期审查。
     
    性能测试
     
    性能测试作为满足跨功能需求的一个方法是值得明确说明的。将系统拆分为较小的微服务 后,跨网络边界调用的次数明显增加了。之前操作可能只涉及一次的数据库调用,现在可能涉及三四次跨网络边界来调用其他服务,还有匹配数量的数据库调用。所有这些调用都 可能减缓系统操作的速度。因此,追踪延迟的根源显得尤为重要。当有多个同步的调用链 时,链的任何部分变得缓慢,整个链都会受影响,最终会对整体速度有明显的影响。这使 得用一些方法对微服务系统进行性能测试,比对单块系统更重要。通常性能测试被推迟的 原因是,最初没有足够的系统资源用于测试。我理解这个原因,但通常性能测试会一直拖 延,如果不是直到上线都没有发生的话,也通常只在上线前才会发生!不要掉入这个拖延 的陷阱。
     
    与功能测试类似,性能测试也可以是各种范围测试的混合。你可能决定想测试单个独立服 务的性能,但开始的时候,可以用测试来检查系统中的核心场景的性能。你可以简单地使 用端到端场景的测试,然后大量并发运行。
     
    为了产生有价值的结果,我们经常需要模拟客户逐渐增多,然后在给定的客户场景一起运 行。这可以帮助我们发现,调用延迟随着负荷的增加如何变化,这意味着性能测试需要运 行一段时间。此外,我们希望性能测试的环境与系统的生产环境尽可能匹配,以确保看到 的结果能表明在生产系统也会有同样的表现。这意味着,我们需要一个类似生产的数据 量,并需要更多的机器来匹配基础设施一一项蛮有挑战的任务。即使我们仍在纠结是否 让性能测试的环境真正类似于生产环境,性能测试对追踪性能的瓶颈仍然是有价值的。只 是需要注意,结果可能是假阴性,甚至更糟,是假阳性。
     
    由于性能测试运行的时间长,因此在每次构建的时候都运行性能测试并不是可行的。一个 常见的做法是,每天运行一个子集,每周运行一个更大的集合。不管选择哪种方法,我们 都要确保尽可能频繁地运行。越长时间没有运行性能测试,就越难追踪最初引起性能问题 的原因。性能问题很难解决,因此,如果新引入的问题可以通过查看少量的提交来发现, 我们的生活将会更加轻松。
     
    然后,测试运行完后一定要确保看结果!我一直感到很惊讶,遇到的很多团队花费很大工 作量实现性能测试,但在运行它们后却从不查看结果。这个原因通常是,人们不知道一个 好的结果应该是什么样的。性能测试需要有目标。有了目标以后,可以基于运行结果让构 建变红或变绿,变红(失败)的构建是需要行动的一个清晰信号。
     
    性能测试需要与系统性能的监控同时进行(在第8章我们将做更多讨论)。理想情况下, 应该在性能测试环境下使用与生产环境中相同的可视化工具,这样我们更容易对两者进行 比较。
     
     
    优化快速反馈,并相应地使用不同类型的测试。
     
    尽可能使用消费者驱动的契约测试,来替换端到端测试。
     
    使用消费者驱动的契约测试,提供团队之间的对话要点。
     
    尝试理解投入更多的努力测试与更快地在生产环境发现问题之间的权衡(MTBF MTTR_权衡的优化)。
     
     
     
  • 相关阅读:
    list1AndCompile
    pickle序列化2
    Servlet简单例子:输出当前日期
    Servlet简单例子:前后端通信
    Java 继承的简单例子
    Java 一个简单的距离工具类
    Java 私有静态成员变量的简单使用
    mysql数据类型
    使用Bootstrap简单案例——导航条+轮播图+模态框+表单
    K-means 聚类
  • 原文地址:https://www.cnblogs.com/mongotea/p/11992108.html
Copyright © 2011-2022 走看看