Skip to content

Latest commit

 

History

History
207 lines (124 loc) · 17.4 KB

File metadata and controls

207 lines (124 loc) · 17.4 KB

十二、微服务中的测试和跟踪

到目前为止,我们必须了解微服务如何帮助我们改变构建应用并将其交付生产的方式。无论是更快地推出新功能,还是保持团队规模较小,微服务都能为我们实现这一点。但在这种体系结构中,每个组件本身都是一个小服务,我们需要解决一些挑战。这些挑战涉及我们如何在遵循微服务方法的同时,将一个稳定且无 bug 的应用交付到生产环境中。

在单片架构内部,我们只有少量需要测试的移动组件。我们可以编写单元测试来测试单片应用的各个方法,然后继续进行集成测试,以验证这些组件是否能够正确地相互操作。但是现在,随着微服务的出现,我们有了越来越多的移动组件。在微服务内部,我们有不同的功能,每个功能都被描述为自己的微服务。

在本章中,我们将了解微服务的测试与单片应用的测试有何不同,在单片应用中,我们不仅需要考虑单个微服务的正确功能,但还必须确保这些服务以定义良好的方式相互通信,以产生满足业务需求的正确结果。

此外,由于基于微服务的应用中的信息从一个服务流向另一个服务,因此当客户机发出请求时,了解这些信息的流动对于我们来说变得非常重要。通过这样做,我们可以准确地发现并修复任何可能产生错误响应或导致应用性能瓶颈的问题。

作为一名读者,在本章结束时,您可以期望了解以下内容:

  • 单片应用和基于微服务的应用测试之间的差异
  • 微服务测试的探讨
  • 在微服务中实现分布式跟踪

技术要求

本书中的代码清单可在chapter12目录下找到 https://github.com/PacktPublishing/Hands-On-Enterprise-Application-Development-with-Python

可以通过运行以下命令克隆代码示例:

git clone https://github.com/PacktPublishing/Hands-On-Enterprise-Application-Development-with-Python

通过在终端上执行以下命令,可以安装基于 Python 的应用的要求:

pip install -r requirements.txt

除了通常基于 Python 的需求外,本章中的代码示例还需要具有以下附加依赖项才能正常工作:

  • Docker:Docker 客户端需要运行我们将在系统内部使用的一些工具。。。

微服务世界中的测试

当我们离开单片体系结构时,我们需要了解,在单片应用开发中曾经为我们工作的过程也需要继续前进。在单片应用的开发过程中,我们使用测试策略,如单元测试,其目的是覆盖应用中各个方法的功能,然后是集成测试,用于覆盖这些方法彼此正确操作的事实。

在微服务架构内部,事情变得有点复杂。我们现在有一些小服务,其中每个服务都应该执行特定的功能。这些服务确实需要通过网络相互交互,为可能存在的业务用例生成任何有意义的输出。但事情并没有就此结束。这些微服务中的每一个都由几个独立的方法和接口组成,这些方法和接口都需要正常工作。这使得测试微服务成为一个有趣的例子,因为现在,我们不仅需要对微服务的各个组件以及它们之间的交互进行单元测试,还需要测试微服务是否能够彼此正确地操作。

这就要求使用几种不同的技术对应用进行更详细的测试。让我们看看这些技术来更好地理解它们。

微服务中的单元测试

微服务内部的单元测试遵循与单片应用测试相同的原则。我们致力于为微服务中的各个方法编写单元测试,并手动或通过自动化运行这些测试,以验证这些组件是否产生了预期的结果。

微服务中的功能测试

一旦我们确定微服务中的各个方法能够正常工作,我们就需要确保一个完全独立的微服务能够在没有任何问题的情况下运行。这是因为大多数微服务本身就是一个完整的包。它们具有自己的一组依赖项,以及可以用来管理数据的数据源。

作为一名开发人员,我们必须确保微服务能够与其依赖项正确交互。为此,我们致力于实现微服务的功能测试。

此外,在功能测试期间,我们需要注意某些事情。由于微服务内的每个 API 端点可能需要与其他微服务交互以产生正确的结果,因此我们可能需要模拟某些微服务的存在,以便成功完成功能测试。

微服务中的集成测试

一旦我们确定我们的微服务能够正确地处理它们的依赖关系,我们就应该确保它们在相互交互时勾选了相同的复选框。这一点很重要,因为无论发生什么情况,这些服务都需要相互交互以产生任何有意义的业务结果。

在集成测试期间,我们通常通过向 API 端点引入正确和不正确的参数来测试请求-响应周期,以验证微服务能够处理这两个用例并且不会失败。这确保了两个外部服务之间的通信接口对于不同的输入足够健壮。。。

微服务中的端到端测试

一旦我们确保不同的微服务能够无缝地相互操作以产生有意义的结果,就应该验证由不同的微服务及其依赖关系组成的整个系统是否能够正常工作。这种测试旨在覆盖整个系统的请求-响应周期,验证中间阶段以及最后阶段产生的输出。这就是所谓的端到端测试。

这种测试确保系统作为一个整体以一种定义良好的方式运行,并且在向系统提供超出系统域的输入时不会产生任何意外。

可扩展性测试

基础设施中的每个微服务都是用来处理特定的一组请求的。随着对应用的请求数量的增加,与其他服务相比,一些微服务的负载可能会增加。

设想有一个基于微服务体系结构的电子商务网站。有一个快速销售正在进行,许多客户试图结帐,并支付他们的购买在同一时间。如果为客户处理结帐和付款的服务在增加的负载期间没有扩大规模,客户可能会面临更多的响应时间或超时,给电子商务公司造成混乱,其客户服务部门现在可能正忙于处理。。。

微服务测试面临的挑战

当涉及到测试时,微服务架构提出了相当多的挑战。这些挑战有时作为体系结构的副作用、糟糕的测试策略或对微服务体系结构缺乏经验而发生。以下是使微服务测试成为一个复杂过程的一些挑战:

  • 微服务知识不完整:对于基于微服务架构构建的应用内部问题的集成测试和调试,负责编写应用测试的测试人员需要对基础架构和单个微服务有完整的了解。如果没有这些知识,测试人员就无法编写能够覆盖应用内部所有可能的请求流的测试,而这些请求流可能会导致一些 bug 在测试阶段逃逸。
  • 协调性差:在微服务的开发过程中,有多个团队拥有自己的微服务集,并且通常都在按照自己的进度工作。这可能会在协调过程中产生问题,并可能在存在某些依赖关系的微服务尚未退出开发阶段的情况下延迟应用的测试。
  • 复杂性增加:对于只有少量微服务的应用,测试通常很容易。但随着为应用供电的微服务数量的增加,这种测试变得越来越麻烦。这是因为现在,测试人员应该为越来越多的请求流编写测试,并确保不同的 API 端点按预期运行。
  • 高灵活性:微服务允许增加灵活性。作为一名开发人员,我们可以自由选择一种技术堆栈,为特定的微服务提供动力。同样的事情也增加了应用测试的问题,因为现在,测试需要考虑用于为特定微服务供电的不同类型的组件。

前面几点是一些挑战,它们使得测试微服务的工作成为一项具有挑战性的任务。然而,每个问题都有解决方案,这些挑战也是如此。让我们来看看我们克服这些挑战的可能的解决方案,概述如下:

  • 实施发布时间表:负责构建应用的团队可以根据里程碑承诺发布应用的时间表。在里程碑的每个阶段,都会根据要部署的服务的优先级提供一些服务以供测试。这有助于改善团队的协调。
  • **标准化 API 端点:**每个服务都需要公开一组 API,用于接收请求和生成响应。标准化 API 和定义特定 API 端点可能需要的参数在测试阶段会有很大帮助,测试人员现在可以轻松地模拟服务,即使该服务尚不可用于测试。
  • 标准化开发实践:尽管每个负责开发特定微服务的团队都可以自由使用任何一套工具来开发微服务,标准化团队可能使用的一组工具和技术,以避免基础架构内部不必要的复杂性,这通常是一种良好的做法。
  • 集成 DevOps 实践:随着向微服务架构的转变,也应该采用 DevOps 实践,其目的是让团队负责他们正在开发的微服务的整个生命周期。这不仅有助于加快开发过程,还允许在将微服务部署到生产环境之前对其进行全面测试。

现在,我们知道了测试微服务体系结构所需的流程更改。这使我们能够提前规划我们的战略,并确保服务在部署到生产环境之前经过良好的测试。

有了测试方面的知识,现在是时候了解微服务领域中的一个非常重要的概念了,它使我们能够了解各个服务在生产中的行为。这也使我们能够找出基于微服务的应用中的特定请求到底在哪里失败。所以,让我们深入研究这个概念。

在微服务中跟踪请求

在任何应用中,在生成请求的最终响应之前,请求可能会流经多个组件。所有这些组件都可能在将请求转交给另一个组件之前,对流程进行一些可能需要的处理。

对请求的跟踪使我们能够可视化关于特定请求流的大量细节。有了请求流的完整图片,我们现在可以找出可能导致请求-响应周期性能瓶颈的地方,或者找出可能导致生成错误结果的组件。

今天,在应用开发领域,任何严肃的应用。。。

OpenTracing 标准

目前,有许多解决方案提供了实现应用跟踪的功能。其中一些解决方案是专有的,而其他解决方案本质上是开源的。

作为开发人员,您可以根据应用的要求以及所选解决方案提供的功能自由选择其中的任何一种。但问题是,如果您希望转移到另一个跟踪解决方案,因为该解决方案提供了更好的功能和对环境的更多控制,那么会发生什么?现在您陷入了困境,因为您可能需要更改基础结构和应用代码中的许多内容,以使新的跟踪解决方案能够工作。那可真麻烦。

OpenTracing 标准提供了一组通用的供应商中立 API 和工具,用于在应用内部实现分布式跟踪。实现这组标准 API 的任何跟踪解决方案都与 OpenTracing 标准兼容,并且可以与遵循相同标准的其他工具进行互操作。

我们选择 Jaeger 作为演示应用的跟踪工具,也是一个符合 OpenTracing 的工具。现在,在不花费任何时间的情况下,让我们通过在上一章中构建的应用内部实现跟踪来解决问题。

在 ToDo 管理器中实现跟踪

在上一章中,我们致力于构建一个简单的应用,它允许我们管理ToDo列表。现在是我们在这个应用中实现请求跟踪的时候了。对于第一个示例,我们将致力于在用户服务中实现对请求的跟踪。

为了使跟踪工作正常进行,我们需要制定一些要求。如果您遵循了本书的技术要求部分,那么您就可以开始学习本教程了。但是,在我们开始执行跟踪之前,让我们来看一下我们将需要的以下组件:

  • **Jaeger 多功能一体机映像:**Jaeger 多功能一体机映像为我们提供了 Jaeger 服务器、代理和一个用户界面,该用户界面。。。

分布式跟踪

在微服务世界中,在生成最终响应之前,请求可能会从一个服务传递到另一个服务。甚至我们的todo列表管理应用的简单示例也显示了这种行为,todo管理器服务经常向用户服务发出请求,以实现用户身份验证,并收集有关用户的详细信息,从而创建一个新的todo列表。

分布式跟踪系统的目标是在请求从一个微服务传输到另一个微服务的过程中对其进行跟踪。

为了实现这一点,跟踪系统利用了许多机制,其中最简单的是将唯一的跟踪密钥嵌入每个请求的 HTTP 头中。然后,跟踪系统能够通过读取 HTTP 头中存在的请求标识符来区分和聚合从一个服务流向另一个服务的特定请求。

现在,是我们看到分布式跟踪的时候了。为此,我们将进行一些更改,以在todomanager 服务中启用跟踪。

以下代码片段展示了在todomanager 服务中启用分布式跟踪所需的更改:

from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from jaeger_client import Config
from flask_opentracing import FlaskTracer
from opentracing_instrumentation.client_hooks import install_all_patches
import datetime
import requests

app = Flask(__name__, instance_relative_config=True)
app.config.from_object('config')

def init_tracer():
    """Initialize the tracing system."""

    config = Config(
        config={ # usually read from some yaml config
            'enabled': True,
            'sampler': {
                'type': 'const',
                'param': 1,
            },
            'logging': True,
        },
        service_name='todo-service',
        validate=True,
    )
    return config.initialize_tracer()

install_all_patches()

flask_tracer = FlaskTracer(init_tracer, True, app)

todo_service.py文件中有了前面的代码,我们就启用了分布式跟踪。但在我们看到这一点之前,有几件事我们需要看一看。在前面的代码片段中,我们从opentracing_instrumentation库中导入了一个名为install_all_patches()的额外方法,如下所示:

from opentracing_instrumentation.client_hooks import install_all_patches

此方法负责跟踪 SQL 库内部或通过python_requests库发生的操作。

一旦这个库与jaeger_clientflask_opentracing一起导入,我们就开始配置并启用init_tracer方法中的应用跟踪。

现在,在配置了跟踪之后,让我们重新启动应用,然后通过向 API 端点传递适当的参数来请求http://localhost:5001/list/new,以创建新的todo列表。

一旦此操作成功,我们可以导航回在http://localhost:16686运行的 Jaeger UI,查看 Jaeger UI 是否显示了我们刚才进行的 API 调用的跟踪。以下屏幕截图显示了屏幕外观的示例:

从前面的屏幕截图可以看出,Jaeger UI 不仅显示 todo manager API 服务端点的请求跟踪,还进一步显示在用户服务内调用的端点,同时提供在将响应生成回客户机应用之前,每个 API 端点花费了多少时间的详细信息。

有了这些,我们现在对微服务内部的分布式跟踪有了一个概念。但是,有哪些可能的用例可以看到这种跟踪的好处呢?让我们看看。

分布式跟踪的好处

通过在基于微服务体系结构的应用中实施分布式跟踪,我们已经能够处理相当多的用例,例如:

  • 了解应用的流程:通过分布式跟踪,我们现在可以可视化来自客户端的传入请求如何在应用内部从一个服务流向另一个服务。这类信息对于了解应用的工作方式以及更好地测试应用非常有用。
  • 缩小 bug:通过了解请求如何从一个服务传递到另一个服务,我们可以通过分析哪些步骤快速隔离可能导致请求产生错误响应的服务。。。

总结

在本章的过程中,我们了解了迁移到微服务体系结构如何影响应用开发生命周期内的流程。我们了解了基于微服务的应用内部的测试与单片应用的测试有何不同,以及在处理微服务体系结构时通常需要什么样的测试阶段。然后,我们了解了由于转向基于微服务的方法而在测试阶段出现的挑战,以及我们如何克服这些挑战。

本章的第二部分介绍了应用内部的分布式跟踪过程,在这一过程中,我们进行了实际操作,以便能够跟踪在上一章中开发的 ToDo manager 应用中的请求流。在此期间,我们了解了跟踪的工作原理,以及分布式跟踪与常规跟踪方法的区别。我们还了解了 OpenTracing 标准如何帮助提供与供应商无关的 API,以便在基于微服务的应用中实现分布式跟踪。

现在,有了所有这些知识,让我们继续看一看开发企业应用的另一种方法,在这种方法中,我们不构建服务或组件,而是构建在某个事件发生时执行的功能。下一章将介绍这种无服务器的应用开发方法

问题

  • 我们如何为微服务编写集成测试?
  • 跟踪单片应用与基于微服务的应用有何不同?
  • 除了 Jaeger 之外,还有哪些其他工具可用于启用分布式跟踪?
  • 我们如何使用 Jaeger 插入代码的特定部分?