Python 企业级应用开发实用指南(三)
原文:
zh.annas-archive.org/md5/B119EED158BCF8E2AB5D3F487D794BB2译者:飞龙
第十二章:微服务中的测试和跟踪
到目前为止,我们已经看到了微服务如何帮助我们改变构建和交付应用程序到生产环境的方式。无论是更快地推出新功能,还是保持团队的小规模,微服务都能为我们实现这一点。但是在这种架构中,每个组件都是自己的小服务,我们有一些挑战需要解决。这些挑战涉及我们如何在遵循微服务方法的同时将稳定且无错误的应用程序部署到生产环境中。
在单体架构中,我们只有少量需要测试的移动组件。我们可以编写单元测试来测试单体应用程序的各个方法,然后进行集成测试以验证这些组件是否与彼此正确运行。但是现在,随着微服务的出现,图片中有越来越多的移动组件。在微服务内,我们有不同的功能,其中每个功能都被描述为自己的微服务。
在本章的过程中,我们将看看微服务的测试与单体应用程序的测试有何不同,我们现在不仅需要考虑单个微服务的正确功能,还需要确保这些服务以明确定义的方式相互通信,以满足业务需求的正确结果。
此外,由于基于微服务的应用程序中的信息从一个服务流向另一个服务,我们需要了解当客户端发出请求时到生成响应时这些信息的流动。通过这样做,我们可以准确地找到并修复可能导致不正确响应或导致应用程序性能瓶颈的任何问题。
作为读者,在本章结束时,您可以期望了解以下内容:
-
单体应用程序和基于微服务的应用程序的测试之间的差异
-
微服务测试的方法
-
在微服务内实施分布式跟踪
技术要求
本书中的代码清单可以在github.com/PacktPublishing/Hands-On-Enterprise-Application-Development-with-Python的chapter12目录下找到。
可以通过运行以下命令克隆代码示例:
git clone https://github.com/PacktPublishing/Hands-On-Enterprise-Application-Development-with-Python
可以通过在终端上执行以下命令来安装基于 Python 的应用程序的要求:
pip install -r requirements.txt
除了通常的基于 Python 的要求之外,本章中的代码示例需要具有以下附加依赖项才能正常工作:
- Docker:需要 docker 客户端来运行我们将在其中使用的一些工具...
微服务世界中的测试
随着我们远离单体架构,我们需要了解在单体应用程序开发中为我们工作的过程也需要跟随。在开发单体应用程序期间,我们通常使用单元测试等测试策略,旨在覆盖应用程序内部各个方法的功能,然后进行集成测试,用于覆盖这些方法是否与彼此正确运行的事实。
在微服务架构中,情况变得有点复杂。现在我们有了小型服务,每个服务都应该执行特定的功能。这些服务确实需要通过网络相互交互,以产生任何可能存在的业务用例的有意义输出。但事情并不会在这里结束。每个微服务都由多个需要正确工作的个体方法和接口组成。这使得测试微服务的情况变得有趣,因为现在我们不仅需要对微服务的各个组件和它们之间的交互进行单元测试,还需要测试微服务是否能够正确地相互操作。
这需要对应用程序进行更详尽的测试,使用多种不同的技术。让我们看看这些技术,以便更好地理解它们。
微服务中的单元测试
微服务内的单元测试遵循与单体应用程序测试相同的原则。我们致力于为微服务内的各个方法编写单元测试,并手动或通过自动化运行这些测试,以验证这些组件是否产生了预期的结果。
微服务中的功能测试
一旦我们确定微服务内部的各个方法正常工作,我们需要确保微服务在完全独立的情况下能够在没有任何问题的情况下运行。这是因为大多数微服务本身就是一个完整的包。它们具有自己的一套依赖项,以及可以管理其数据的数据源。
作为开发人员,重要的是确保微服务能够与其依赖项正确交互。为此,我们致力于实现微服务的功能测试。
此外,在功能测试期间,我们需要注意一些事情。由于微服务内的每个 API 端点可能需要与其他微服务交互以产生正确的结果,我们可能需要模拟某些微服务的存在,以便功能测试能够成功完成。
微服务中的集成测试
一旦我们确定我们的微服务与其依赖项正确工作,就是时候确保它们在相互交互时也具有相同的复选框。这是重要的,因为这些服务无论如何都需要相互交互,以产生任何有意义的业务结果。
在集成测试期间,我们通常旨在通过向 API 端点引入正确和不正确的参数来测试请求-响应周期,以验证微服务能够处理两种用例并且不会失败。这确保了两个外部服务之间的通信接口足够健壮,能够处理各种输入...
微服务中的端到端测试
一旦我们确保不同的微服务能够无缝地相互操作以产生有意义的结果,就是时候验证整个系统,包括不同的微服务及其依赖项,是否能够在没有任何问题的情况下工作。这种测试旨在覆盖整个系统的请求-响应周期,验证中间阶段产生的输出以及最终阶段。这被称为端到端测试。
这种测试确保整个系统以明确定义的方式运行,并且在向系统提供超出系统域的输入时不会产生任何意外。
可扩展性测试
基础设施中的每个微服务都是为处理一定的请求而设计的。随着对应用程序的请求数量增加,一些微服务可能会比其他服务承受更大的负载。
想象一下,有一个基于微服务架构的电子商务网站。正在进行限时抢购,很多顾客同时尝试结账和付款。如果处理顾客结账和付款的服务在负载增加时没有扩展,顾客可能会面临增加的响应时间或超时,给电子商务公司带来混乱,其客户服务现在可能正忙于处理...
微服务测试中的挑战
当涉及测试时,微服务架构提出了一些挑战。这些挑战有时是架构的副作用,测试策略不佳,或者对微服务架构的经验不足。以下是使微服务测试成为复杂过程的一些挑战:
-
对微服务的知识不完整:对于基于微服务架构构建的应用程序的集成测试和调试问题,负责编写应用程序测试的测试人员需要完全了解基础设施和各个微服务。没有这些知识,测试人员无法编写能够覆盖应用程序中所有可能的请求流程的测试,这可能导致一些错误在测试阶段逃脱。
-
协调不足:在开发微服务时,有多个团队拥有自己的一组微服务,并且通常以自己的节奏工作。这可能会导致协调问题,并且可能会延迟应用程序的测试,如果某个微服务,其上有某些依赖,仍然没有完成开发阶段。
-
增加的复杂性:对于只有少量微服务的应用程序,测试通常很容易。但随着支持应用程序的微服务数量的增加,这种测试变得越来越繁琐。这是因为现在测试人员需要为增加的请求流程编写测试,并确保不同的 API 端点按预期运行。
-
高灵活性:微服务允许增加灵活性。作为开发人员,我们可以自由选择技术栈来支持特定的微服务。同样,这也增加了应用程序测试的问题,因为现在测试需要考虑用于支持特定微服务的不同类型的组件。
上述观点是使测试微服务工作成为一项具有挑战性的任务的一些挑战。然而,每个问题都有解决方案,这些挑战也不例外。让我们看看我们有哪些可能的解决方法来克服这些挑战,如下所述:
-
实施发布计划:负责构建应用程序的团队可以承诺按里程碑发布应用程序的计划。在每个里程碑阶段,根据要部署的服务的优先级,一些服务将可用于测试。这有助于改善团队的协调。
-
标准化 API 端点:每个服务都需要公开一组用于接收请求和生成响应的 API。标准化 API 并定义特定 API 端点可能需要的参数在测试阶段非常有帮助,测试人员现在可以轻松地模拟一个服务,即使该服务尚未可用于测试。
-
标准化开发实践:尽管负责开发特定微服务的每个团队都可以自由选择用于开发微服务的任何一组工具,但通常最好将团队可能使用的一组工具和技术标准化,以避免基础设施内部的不必要复杂性。
-
集成 DevOps 实践:随着微服务架构的转变,应该采用 DevOps 实践,旨在使团队对其正在开发的微服务的完整生命周期负责。这不仅有助于加快开发过程,还允许在部署到生产环境之前对微服务进行彻底测试。
现在,我们知道了测试微服务架构所需的变化。这使我们能够提前规划我们的策略,并确保服务在部署到生产环境之前经过了充分测试。
在测试知识的基础上,现在是时候了解微服务领域中一个非常重要的概念,它让我们能够了解个别服务在生产环境中的行为。这也让我们能够找出基于微服务的应用程序中特定请求失败的确切位置。因此,让我们深入了解这个概念。
微服务内部的请求追踪
在任何应用程序内,一个请求可能在生成请求的最终响应之前流经多个组件。所有这些组件可能会在将请求交给另一个组件之前进行一些处理,这些处理可能在进程之前是必需的。
请求追踪使我们能够可视化关于特定请求流的丰富细节。有了对请求流的完整了解,我们现在可以着手查找可能导致请求-响应周期性能瓶颈的地方,或者找出可能导致生成不正确结果的组件。
今天,在应用程序开发世界中,任何严肃的应用程序...
OpenTracing 标准
目前,市面上有许多解决方案提供了实现应用程序追踪功能。其中一些解决方案是专有的,而另一些是开源的。
作为开发人员,您可以根据应用程序的要求和所选择解决方案提供的功能自由选择任何解决方案。但问题是,如果您想要切换到不同的追踪解决方案,因为该解决方案提供了更好的功能和更多对环境的控制,会发生什么?现在您陷入困境,因为您可能需要更改基础设施和应用程序代码中的许多内容才能使新的追踪解决方案正常工作。这是很麻烦的。
OpenTracing 标准提供了一组通用的供应商中立 API 和仪器,用于在应用程序内实现分布式追踪。任何实现了这组标准 API 的追踪解决方案都与 OpenTracing 标准兼容,并且可以与遵循相同标准的其他工具进行互操作。
我们选择 Jaeger 作为演示应用程序的追踪工具,它也是符合 OpenTracing 标准的工具。现在,让我们不再浪费时间,在我们在上一章中构建的应用程序内实现追踪。
在待办事项管理器内实现追踪
在上一章中,我们致力于构建一个简单的应用程序,允许我们管理“待办事项”列表。现在是时候在这个应用程序内实现请求追踪了。作为第一个示例,我们将致力于在用户服务内实现请求追踪。
为了使追踪工作,我们需要满足一些要求。如果您遵循了本书的技术要求部分,您就可以开始使用本教程了。但在我们深入实施追踪之前,让我们看看我们将需要的以下组件:
- Jaeger 全一体图像: Jaeger 全一体图像为我们提供了 Jaeger 服务器、代理和 UI...
分布式追踪
在微服务世界中,请求可能在生成最终响应之前从一个服务传输到另一个服务。即使是我们简单的todo列表管理应用程序的示例也展示了这种行为,todo管理器服务经常向用户服务发出请求以实现用户身份验证,并收集有关用户的详细信息,从而创建一个新的todo列表。
分布式追踪系统旨在在请求从一个微服务到另一个微服务的传输过程中实现追踪。
为了实现这一点,追踪系统利用了许多机制,其中最简单的机制是将唯一的追踪密钥嵌入到每个请求的 HTTP 标头中。然后,追踪系统能够通过读取 HTTP 标头中存在的请求标识符来区分和聚合特定请求,从而使请求从一个服务流向另一个服务。
现在,是时候让我们看看分布式追踪是如何运作的了。为此,我们将进行一些更改,以在我们的todo管理器服务内启用追踪。
以下代码片段展示了在todo管理器服务内启用分布式追踪所需的更改:
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_client和flask_opentracing一起导入,我们就继续配置和启用应用程序内的追踪,这是在init_tracer方法中完成的。
现在,追踪已配置好,让我们重新启动应用程序,然后通过向 API 端点传递适当的参数,向http://localhost:5001/list/new发出请求,以创建一个新的todo列表。
一旦这个操作成功,我们可以转到运行在http://localhost:16686上的 Jaeger UI,查看 Jaeger UI 显示我们刚刚进行的 API 调用的追踪。以下截图显示了屏幕可能的样子:
从上述截图中可以看出,Jaeger UI 不仅显示了 todo 管理器 API 服务端点的请求跟踪,还进一步显示了在用户服务内调用的端点,并提供了在响应返回给客户端应用程序之前在每个 API 端点上花费了多少时间的详细信息。
有了这个,我们现在知道了微服务内部的分布式跟踪是什么样子的。但是有哪些可能的用例可以从这种追踪中受益呢?让我们找出来。
分布式追踪的好处
在基于微服务架构的应用程序中实施分布式追踪后,我们已经能够处理许多用例,例如以下用例:
-
理解应用程序的流程: 通过分布式追踪,我们现在可以可视化客户端发来的请求在我们的应用程序内从一个服务到另一个服务的流动。这种信息对于弄清楚应用程序的工作原理并实现更好的应用程序测试非常有用。
-
缩小错误范围:通过了解请求是如何从一个服务传递到另一个服务的,我们可以通过分析每个步骤来快速确定可能导致请求产生错误响应的服务...
总结
在本章的过程中,我们了解了向微服务架构的转变如何影响应用程序开发生命周期内的流程。我们了解了微服务应用程序内的测试与单体应用程序的测试有何不同,以及在处理微服务架构时通常需要哪些测试阶段。然后,我们了解了在测试阶段出现的挑战,由于向微服务架构的转变,以及我们如何克服这些挑战。
本章的第二部分带领我们深入了解应用程序内的分布式跟踪,我们进行了实际操作,使我们能够跟踪我们在上一章中开发的 ToDo 管理器应用程序中的请求流程。在此过程中,我们了解了跟踪的工作原理,以及分布式跟踪与常规跟踪方法的区别。我们还了解了 OpenTracing 标准如何帮助提供一个供应商中立的 API,以实现微服务应用程序内的分布式跟踪。
现在,有了所有这些知识,让我们继续来看一下另一种开发企业应用程序的方法,其中我们不是构建服务或组件,而是构建在发生某个事件时执行的函数。下一章将带领我们了解这种无服务器的应用程序开发方法。
问题
-
我们如何为微服务编写集成测试?
-
跟踪单体应用程序与跟踪基于微服务的应用程序有何不同?
-
除了 Jaeger 之外,还有哪些工具可用于实现分布式跟踪?
-
我们如何使用 Jaeger 对代码的特定部分进行仪器化?
第十三章:无服务器
正如我们迄今所探讨的,微服务提供了一种很好的替代架构,我们可以通过它来处理应用程序开发场景。具有更快的发布周期、易于启动新功能和高可伸缩性的优势,微服务对开发人员来说是一个引人注目的选择。但所有这些微服务仍然在基于服务器的环境中运行。
在基于服务器的环境中运行对于应用程序的响应时间是有用的,因为总是有一个准备接受传入请求的服务。但有一个缺点:如果没有用户,应用程序将继续消耗系统资源。
最近,应用程序开发人员开始转向一种新的应用程序开发方法。这种开发方法侧重于应用程序是事件驱动的,并且根据某些事件的发生启动操作。这种类型的应用程序被称为无服务器应用程序,因为当没有用户时它们不会继续运行,它们的实例只有在发生了某些事件时才会启动。
随着我们在本章中的深入,我们将看看这种无服务器应用程序开发方法以及它如何改变开发场景。
作为本章的读者,您将学习以下内容:
-
应用程序开发的无服务器方法
-
驱动无服务器架构的流程
-
构建无服务器应用程序
-
无服务器方法的好处
技术要求
本书中的代码清单可以在github.com/PacktPublishing/Hands-On-Enterprise-Application-Development-with-Python的chapter13目录下找到.
可以通过运行以下命令克隆代码示例:
git clone https://github.com/PacktPublishing/Hands-On-Enterprise-Application-Development-with-Python
此外,为了成功执行代码,还需要一些额外的软件:
-
Docker:Docker 是运行 OpenWhisk 软件平台以部署无服务器应用程序所需的依赖项。要在您的平台上安装
docker,请查看docs.docker.com/install/。 -
Apache OpenWhisk:Apache OpenWhisk 提供了一个开源平台,用于...
无服务器应用程序开发方法
近年来,作为开发人员,我们已经习惯了以传统方式构建应用程序并在生产基础设施上处理它们的部署。在这种传统架构中,我们开发了应用程序,其中应用程序接收来自客户端的请求,检查客户端是否被授权执行该操作,然后继续执行该操作。
一旦应用程序开发完成,我们将其部署到与我们的应用程序兼容的平台上。这涉及选择操作系统、平台运行的基础设施类型,例如裸金属服务器、虚拟机或容器,然后通过处理其可伸缩性和解决可能出现的任何问题来维护基础设施。例如,一个管理组织内员工工资的简单系统将如下所示:
在这种情况下,应用程序在服务器上持续运行,等待请求到来,并在请求到达时执行操作。
这种方法虽然非常有用,但通常会使开发人员从编写实现系统特定结果的逻辑的主要任务中分心,并使他们专注于涉及基础设施管理和可伸缩性的许多领域。
现在想象一种架构,它允许开发人员专注于只编写特定业务流程背后的逻辑,而不必担心这些逻辑将在何处执行以及如何扩展。
构建应用程序的无服务器方法提供了这些功能。在无服务器中,这是通过引入两种新的应用程序开发技术来实现的:
-
后端即服务(BaaS):BaaS 是一种新的云计算服务,为应用程序开发人员提供了通过 API 将其应用程序与后端服务链接的功能,以提供一些常见的功能集,如用户认证和数据存储。它与应用程序开发的一般架构不同,因为后端提供的这些服务可能不需要由应用程序开发人员自己开发,而是通过这些服务提供的 API 来访问这些服务。
-
函数即服务(FaaS):FaaS 是云计算的另一类别,允许开发人员专注于编写应用程序逻辑,而不必担心这些逻辑将在何处执行。在 FaaS 中,应用程序以无状态和短暂的方式运行,它们可能在执行的基础设施仅适用于少数调用,甚至可能只有一个调用。
在应用程序开发的无服务器架构中,应用程序通常被开发为作为对某个事件的响应而执行的函数。这些函数在它们自己的无状态容器中执行,这些容器可能仅在基础设施中存在几次调用。我们将在本章的后面部分看一下无服务器应用程序是如何工作的。作为一个快速参考,如果我们必须将工资系统实现为无服务器应用程序;以下图表显示了系统架构的样子:
正如我们所看到的,我们的无服务器工资单应用程序包含了 BaaS 提供的功能,其中客户直接通过Auth DB暴露的 API 与Auth DB进行交互,并且工资单生成和员工搜索在 FaaS 提供的功能中运行,它们被存储为函数,并且只在特定事件发生时执行。
这两个功能都不维护任何状态,因此它们可以在短暂的容器中运行,这些容器可能只存在很短的时间。
现在,让我们看看驱动无服务器架构的组件以及无服务器架构如何工作,以更好地理解我们如何开发最好利用无服务器架构的应用程序。
无服务器架构的组件
正如我们所见,无服务器架构为我们提供了一种开发应用程序的方式,我们只需要负责编写应用程序背后的逻辑,而不用担心如何管理运行应用程序的基础设施,以及应用程序如何根据请求的数量进行扩展或缩减。
但是是什么驱动了这种架构?让我们花点时间来看看架构内部的不同组件是如何工作的,以提供一种无服务器开发方法来进行应用开发。
正如前面讨论的,应用程序开发的无服务器方法是通过使用两种技术来实现的...
后端即服务
我们开发的大多数应用程序共享一组常见的功能。这些功能可能包括实现用户认证数据库,提供存储和检索文件的方式,或者通过电子邮件或推送通知发送通知。
大多数情况下,这些功能是通过在应用程序中引入新组件来构建的,其他组件可以与这些组件进行交互。对于基于微服务的应用程序也是如此,这些功能被实现为不同的微服务,其他微服务与这些服务进行交互以实现特定的结果。
在 BaaS 方法中,我们通过使用第三方云提供商来解耦这些功能,通过使用第三方提供商提供的 API,我们的应用程序通常集成这些功能。
为了更好地理解这一点,让我们来看一下我们之前介绍的无服务器工资管理系统。在这个系统中,我们通过利用第三方提供的 BaaS 服务,将用户认证作为我们应用程序的一个不相关的部分。
在这种方法中,我们的用户认证系统及其相关的任何数据都由第三方提供商管理。该提供商公开了一些服务的 API,我们可以使用这些 API 将服务与我们的应用程序集成。
在我们的例子中,我们通过使用服务提供的 API 来向客户端公开部分用户认证服务。这允许客户端直接与服务进行用户认证,而无需通过整个应用程序的后端。我们使用 BaaS 服务的第二个地方是当我们将员工搜索功能与用户认证服务链接起来,根据某些标准检索特定的员工。
BaaS 的这一概念为我们提供了几个优势,例如:
-
**减少开发时间:**通过 BaaS,应用程序的开发人员无需担心开发可以直接从第三方服务提供商那里获取的常见功能集,而是使用服务提供商提供的 API。
-
**操作的便利性:**由于服务和与服务相关的基础设施仅由云计算提供商管理,这减少了管理服务和其提供的操作的复杂性,从而减少了操作上的麻烦。
-
**易于扩展性:**云计算提供商提供的服务直接由他们管理,可以轻松扩展,现在只由提供商完成。
-
**集成的灵活性:**提供商提供的服务通常通过 API 进行集成。如果提供的平台有所需的服务集成 API,平台可以轻松地与服务集成,而不必担心集成背后的复杂性,从而支持不同类型的应用程序。
函数即服务
FaaS 是一个有趣的概念,也是支持无服务器架构的主要技术之一。在这种方法中,我们开发后端代码,而不用担心代码将如何部署以及在哪里执行。
针对 FaaS 的应用程序就像任何其他不需要任何特殊框架进行开发和执行的应用程序一样。FaaS 应用程序与部署在服务器上的常规应用程序之间唯一的区别在于,FaaS 应用程序在维护其状态和执行的时间方面有严格的限制。因此,让我们深入探讨这两个主要方面...
国家管理的限制
在 FaaS 模型中,应用程序的不同部分被构建为单独的函数,每个函数在发生某个事件时被执行。当应用程序应该被部署时,云提供商会自动管理应用程序将在哪里运行以及应用程序如何扩展。
与传统应用程序相比,一旦部署,就会启动服务器进程并准备接受传入连接,基于 FaaS 的应用程序是动态启动作为对某个输入的响应。一旦事件发生,函数开始执行,等待一段时间,然后包含函数的实例被终止。现在,这使得这个过程变得有趣,因为函数在基础设施中存在的时间是有限的,而且不能保证同一个函数实例也会处理下一个调用。
这使得状态管理,也就是当前执行操作的本地数据管理,在基于 FaaS 的服务中成为一个具有挑战性的任务,严重限制了我们可以在函数实例中存储的本地数据。
为了处理这种情况,我们依赖于一个可以为我们存储状态数据的外部服务。这可能包括使用外部数据库或缓存服务器,其中数据可以被持久化以供将来参考。
执行时间限制
一旦函数在 FaaS 服务中开始执行,它只有有限的时间来完成执行。大多数知名的云服务提供商都对 FaaS 服务中的函数执行时间设置了限制。例如,如果我们选择 AWS 最著名的 FaaS 服务 AWS Lambda,函数的最大执行时间限制为五分钟。其他提供商的限制可能略有不同,但不会太高。
现在,这对我们作为应用程序开发人员来说是一个有趣的案例。如果我们试图将一个应用程序组件实现为一个函数,可能需要花费相当长的时间...
在 FaaS 中执行函数
一旦我们将应用程序开发为函数形式,我们需要一个地方来托管和运行它。这些函数的托管地点由云服务提供商提供。一旦我们成功托管了这些函数并实施了特定函数执行的规则,云服务提供商就有责任处理这些函数的正确执行。
现在,当这些函数需要执行时,云服务提供商确定执行特定函数所需的正确环境。一旦确定了这个环境,云服务提供商就会启动一个临时容器,函数代码就驻留在这个容器内。这个容器为函数提供了完全隔离,使其与可能在环境中执行的其他函数隔离开来。一旦容器成功启动,函数就会执行,并返回响应。
有趣的部分发生在函数完成执行后。一旦函数完成执行,云服务提供商可以终止包含函数的容器实例,也可以保持其活动以处理新的请求。大多数情况下,决定是基于到达的请求频率和用户设置的策略来做出的。
如果一个函数实例仍在运行并等待,新的请求可能会被重定向到该实例,而如果没有正在运行的函数实例,云服务提供商将启动一个新实例并将请求重定向到该实例。
通过这样,我们对 FaaS 在无服务器架构中的工作原理以及它如何使我们能够开发无服务器应用程序有了一个很好的了解。但是这些函数实际上是如何触发的呢?这就引出了构成无服务器服务的另一个重要组件。让我们看看它是什么。
无服务器架构中的 API 网关
在第十一章 采用微服务方法中,当我们了解了微服务的概念时,我们介绍了 API 网关以及它们如何帮助开发微服务。这些 API 网关在基于无服务器架构的应用程序开发中也起着重要作用。
API 网关只是嵌入有关应用程序的某些 API 端点的信息并将这些端点与某些处理程序相关联的 HTTP 服务器。一旦向某个 API 端点发出请求,就会调用与 API 端点相关联的处理程序来处理请求。
在无服务器架构中,与特定 API 端点相关联的处理程序...
理解无服务器应用程序的执行
到目前为止,我们已经了解到无服务器应用程序是以函数的形式构建的,这些函数基于某些事件的发生而执行。此外,这些函数并不永远保持活动状态。相反,这些函数在需要时被执行。那么,当请求到来时,提供者如何处理这些函数的执行呢?让我们来看一下。
冷启动函数
当应用程序刚刚部署时,很容易想象当前不会有任何正在执行的函数实例。当新请求到来并要求由我们刚刚部署在基础设施上的函数提供的功能时。现在,云提供者系统被通知说没有正在运行的函数实例可以处理传入的请求。
一旦提供者系统意识到情况,它就会生成一个包含函数代码的新实例。这个实例现在开始根据请求中提供的参数执行函数,并且函数生成响应并发送回请求的客户端。
热启动函数
与冷启动完全相反,热启动函数利用已经在提供者基础设施中运行的函数的现有实例。当这种情况发生时,传入的请求不必等待新实例生成才能处理请求。这允许请求快速处理。
这里需要注意一点:即使在函数的热启动情况下,也不会存储函数先前执行的状态。
现在我们知道了函数性能可能取决于的一个主要因素。现在让我们继续构建我们的第一个无服务器应用程序。
构建我们的第一个无服务器应用程序
有了我们对无服务器架构及其工作原理的基本了解,现在是时候开发我们的第一个无服务器应用程序了。在本教程中,我们将使用 Apache OpenWhisk 项目,在本地开发系统上运行我们的演示应用程序。因此,让我们看看 Apache OpenWhisk 为我们提供了什么,以及我们如何利用该平台来获益。
Apache OpenWhisk 的快速介绍
Apache OpenWhisk 平台为我们提供了功能和功能,使我们能够设置自己的平台来运行无服务器应用程序。该项目提供了根据环境中某些事件的触发执行函数的功能。
这些函数的执行发生在 docker 容器内,OpenWhisk 平台管理其中的函数的部署和扩展。
以下是平台提供的一些功能:
-
易于使用的工具: 该平台提供了许多工具,使我们能够轻松打包和移植应用程序以在 OpenWhisk 平台上运行,除了应用程序遵循平台定义的一组约定。
-
使用容器进行隔离: 该平台通过使用 Docker 容器来隔离不同的功能,使得每个功能都在自己独立的环境中运行,以避免任何环境依赖冲突。
-
支持多种语言: OpenWhisk 平台为我们提供了许多支持的语言平台,我们可以使用这些平台来构建我们的无服务器应用程序。这还包括使用 Go、C++和 Rust 构建的二进制可执行文件。
-
内置 API 网关: OpenWhisk 软件包配备了自己的内置 API 网关,使我们能够通过 RESTful API 端点轻松集成应用程序。
所有这些功能使 OpenWhisk 成为在云端或本地开发环境中运行无服务器应用程序的绝佳平台。
但在我们开始构建应用程序之前,我们需要在系统上部署 OpenWhisk。要部署项目,请按照本章开头的技术要求部分中的步骤进行操作。
对于演示,我们将构建一个应用程序,该应用程序会查询 GitHub API,并检索与我们的用户帐户关联的存储库。
设置开发环境
在我们开始编写应用程序的代码之前,我们需要先安装一些依赖项。因此,让我们先构建环境,然后开始编写将驱动我们应用程序的代码。
作为第一步,让我们创建一个目录,其中包含与我们项目相关的所有文件。让我们将这个文件夹命名为github_demo。以下命令可以帮助我们创建这个文件夹:
mkdir github_demo
一旦我们设置好目录,让我们进入目录并设置一些东西:
cd github_demo
完成这些步骤后,我们现在可以设置我们的项目了。在我们开始编写代码之前,让我们完成虚拟环境的设置,这将帮助我们保持项目依赖项的隔离。...
构建我们的配置文件
为了这个应用程序,我们将使用一个配置文件来存储与我们用户帐户相关的数据,这将允许我们对Github API 进行身份验证。为此,在我们的项目目录中,创建一个名为config.ini的新文件,其中包含以下内容:
[github_auth]
username = ‘<your github username>’
password = ‘<your github password>’
一旦我们完成了配置文件的设置,让我们继续编写我们的应用程序代码,这将与Github交互以获取我们的repos。
与 GitHub API 集成
现在我们即将开始我们应用程序的实际部分,让我们开始编写代码。以下代码片段描述了我们用来查询Github API 的代码:
from github import Githubimport configparser# Provide the location of where the config file existsCONFIG_FILE = 'config.ini'def parse_config(): """Parse the configuration file and setup the required configuration.""" config = configparser.ConfigParser() config.read(CONFIG_FILE) if 'github_auth' not in config.sections(): return False username = config['github_auth']['username'] password = config['github_auth']['password'] return (username, password)def get_repos(): """Retrieve the github repos associated with the user. Returns: Dict ...
准备好与 OpenWhisk 一起运行的代码
代码就绪后,现在是时候将其转换为 OpenWhisk 可以执行的格式了。
要在 OpenWhisk 内执行任何功能,代码应该从__main__.py文件中调用。因此,让我们创建该文件并添加以下内容:
from github_demo import get_repos
def main(dict):
repos = get_repos()
return repos
代码就位后,让我们试着理解我们在这里做了什么。首先,我们导入了在github_demo.py文件中创建的get_repos函数,该函数有助于从Github API 中检索内容:
from github_demo import get_repos
然后,我们定义main()函数,OpenWhisk 将调用该函数来执行代码。任何存在于主函数中的代码都将由 OpenWhisk 直接执行。因此,我们使用这种方法来调用我们的get_repos()函数:
def main(dict):
一旦完成这一步,我们就快要准备好部署我们的应用程序了。
朝着部署的最后步骤迈进
在我们部署应用程序之前,我们还有一些步骤要完成。为了成功安装应用程序,让我们创建一个文件,用于存储运行我们项目所需的依赖项。以下命令可帮助我们安装所需的依赖项:
pip freeze > requirements.txt
有了这些要求打包好后,现在让我们打包我们的项目,以便可以部署到 OpenWhisk。为此,运行以下命令可以帮助我们创建不同项目组件的包:
tar -zcvf github_demo.tar.gz github_demo
有了这个,我们现在已经准备好将我们的应用程序部署到 OpenWhisk 了。
部署到 OpenWhisk
一旦我们准备好部署包,我们需要运行 OpenWhisk 提供的一些命令,以便在平台上启动并运行包。
作为第一步,我们必须执行以下命令,将包上传到 OpenWhisk:
wsk action create github_demo –kind python:3 github_demo.tar.gz
一旦执行了这个命令,包将被上传到 OpenWhisk 平台,并准备好运行。
现在,要调用应用程序,我们可以运行以下命令,以异步方式执行应用程序:
wsk action invoke github_demo
完成后,我们的应用程序开始以异步方式执行。通过异步运行,我们的意思是命令的执行不会等到函数执行结束,而是会提供一个可以用来跟踪调用结果的操作激活 ID。
现在,让我们看看应用程序部署后 OpenWhisk 如何处理这个应用程序的执行。
了解 Openwhisk 内应用程序的执行
有了演示应用程序,现在是时候了解这个应用程序在幕后是如何执行的了。
应用程序成功执行的背后,有几个步骤涉及,从我们运行wsk action invoke命令开始执行我们的应用程序。因此,让我们看看幕后发生的步骤:
- 发出 API 调用: 我们构建的每个要部署到 OpenWhisk 的操作都被映射为将调用该操作的 API 端点。当我们运行
wsk action invoke时,该命令会调用为所提供的函数映射的 API 端点。然后,这个调用被 OpenWhisk 内的 Nginx 拦截,起到...
无服务器的优势
了解了无服务器应用程序的工作原理后,现在是时候看看这种开发方法提供的优势了:
-
减少开发工作量: 通过使用第三方云提供商提供的服务,我们可以减少一些在应用程序中找到的常见功能的开发工作量,例如用户身份验证、通知和文件存储。所有这些功能都可以通过云提供商提供的 API 来实现。
-
操作复杂性较低: 无服务器应用程序的执行和扩展由云服务提供商管理,这消除了管理我们自己的基础设施以处理应用程序执行的操作复杂性。
-
高可用性: 以无服务器方式构建的应用程序由于基础设施由云提供商管理,因此可以在世界各地的不同数据中心运行应用程序,从而降低了应用程序的可用性受影响的机会。
-
优化资源分配: 由于只有在发生某个事件时才执行函数,因此只有在执行特定函数时才分配资源,这优化了跨基础设施的资源使用。
-
编程语言的选择: 大多数无服务器解决方案都支持各种类型的编程语言,这使我们能够使用最佳的技术栈来实现我们的解决方案。
有了这个,我们现在有足够的理由选择无服务器开发方法,以便我们的需求与构建无服务器应用程序所需遵循的开发方法论相一致。
总结
在我们阅读本章的过程中,我们看到了无服务器架构如何成为应用程序开发的新趋势,以及这种架构的工作原理。我们涵盖了无服务器架构的不同组件,并介绍了后端即服务和函数即服务的概念,它们支持无服务器架构。然后,我们看了一下 API 网关在架构中的作用,以及无服务器应用程序中的 API 网关与微服务中使用的 API 网关有何不同。
之后,我们开始构建我们的第一个无服务器应用程序,并通过 Apache OpenWhisk 运行它,该平台提供了一个运行无服务器应用程序的开源平台。在这里,我们也深入探讨了...
问题
-
无服务器架构提供了哪些优势?
-
BaaS 如何帮助应用程序开发?
-
API 网关如何帮助执行无服务器应用程序?
-
有哪些因素使将应用程序转换为无服务器格式变得困难?
进一步阅读
你觉得无服务器架构的理念有趣吗?看看Jalem Raj Rohit的Packt Publishing出版的使用 Python 构建无服务器应用程序,深入了解无服务器架构。
第十四章:部署到云端
到目前为止,我们的重点大部分都花在了应用程序的开发上,不管是以大型单体应用程序的形式还是以基于微服务的应用程序形式,其中存在许多服务。为了让这些应用程序对用户可用,应用程序需要部署到一定的地方,以便一般用户可以与应用程序进行交互。
在 DevOps 的现代世界中,部署策略以及应用程序的部署地点在定义应用程序如何工作和向用户提供访问权限方面起着重要作用。关于应用程序部署的决策可以影响基础设施中的许多事情,例如运行特定应用程序所需的基础设施的复杂性,以及应用程序内的新功能将如何推出。
在本章的过程中,我们将看看如何为单体应用程序和基于微服务的应用程序创建部署,以及如何实施部署策略,优先考虑应用程序在基础设施上部署后的稳定性。我们还将研究使用容器部署应用程序的现代方法,并在私有、公共和混合云部署之间做出选择。
作为本章的读者,您将学习以下内容:
-
部署策略的需求
-
将应用程序容器化以进行部署
-
将测试集成为部署策略的一部分
-
在私有云上部署
-
在公共云上部署
-
向混合云的转变
技术要求
为了理解本章,对使用 Docker 进行容器化和至少一个云提供商的 CLI 的知识将会有所帮助。
部署企业应用程序
在本书的过程中,我们已经看到了如何使用不同的原则开发企业应用程序,无论是通过单体应用程序开发的方式还是通过使用小型微服务来开发应用程序。但这些事情都汇聚在一个共同点。为了让我们的应用程序对一般用户可用,它们需要部署到开发环境之外的某个地方,以便一般用户可以访问。
为了成功部署,特定应用程序的基础设施和所选择的部署类型需要提供一定的功能:
-
高可用性: 应用程序部署的任何基础设施都需要提供高可用性,以便为用户提供几乎无中断的应用程序服务。如果基础设施容易频繁宕机,那么可能会导致应用程序的可用性严重中断,并且可能会导致依赖于应用程序的流程停滞,直到应用程序运行的基础设施恢复在线。
-
低延迟: 为应用程序提供服务的基础设施的延迟应该低,以便为用户提供足够的响应时间。如果基础设施的延迟很高,用户可能需要等待与应用程序进行交互,或者应用程序生成的响应可能严重影响他们的生产力。
-
容错性: 部署基础设施应具有容错性,并且应能够从偶尔的几个节点故障中恢复。如果缺乏容错性,即使基础设施内出现单个问题,也足以使整个应用程序崩溃,给应用程序的用户造成严重的可靠性问题。
这只是需要满足的基本基础设施要求,才能考虑将应用程序部署到该基础设施上。可能会有其他要求,这些要求可能是由于选择特定的基础设施部署策略而施加的,但讨论这些要求超出了本书的范围。
到目前为止,我们经常听到部署策略这个词,但当我们说我们需要为应用程序选择适当的部署策略时,我们到底是什么意思呢?让我们花点时间来探讨一下。
选择部署策略
一旦我们确定我们现在准备将应用程序投入生产,我们现在的任务是找出我们将要使用的应用程序部署策略。
应用程序的部署策略通常会规定应用程序的推出方式,取决于我们所拥有的应用程序的类型。这些部署策略涵盖了有关在生产中使应用程序可用所需的步骤的信息,并且可能还涵盖有关如何在应用程序中推出新功能的其他重要领域。
因此,让我们花点时间讨论可用的不同部署策略...
不同的部署策略
在软件开发世界中,没有一种解决方案适用于所有情况,即使在选择我们将要遵循的部署策略类型时也是如此。
我们选择的每种部署策略都会有与之相关的优缺点。一些部署策略提供的灵活性不大,但实施起来简单,而其他部署策略非常灵活,但在实施过程中可能会变得麻烦。作为开发人员,选择取决于我们如何处理应用程序的部署。主要地,我们将在本章的过程中涵盖六种部署策略,即:
-
重新创建部署
-
滚动部署
-
蓝绿部署
-
金丝雀部署
-
A/B 部署
-
影子部署
因此,让我们花点时间来熟悉每种部署策略。
重新创建部署
这是应用程序部署的最传统方法。在这种部署策略中,我们简单地销毁旧版本的应用程序,并引入新版本的应用程序,并将所有用户请求路由到新版本的应用程序。以下图表显示了重新创建部署策略的表示:
这种策略对于遵循单体开发方法的应用程序的部署非常有用,因为对于每个新功能或升级,整个应用程序都需要重新部署。
使用重新创建部署的优势...
滚动部署
在部署应用程序的滚动部署模型中,我们不会突然关闭所有旧版本应用程序的实例,以替换它们为新版本。相反,我们采取逐步推出新应用程序版本的方法,覆盖整个基础设施。
在这个过程中,我们首先启动升级后的应用程序的新实例,放在负载均衡器后面,一旦它准备好接受流量,我们就移除旧版本应用程序的等效实例。这个过程会持续进行,直到所有旧版本应用程序的实例都被新版本实例替换。以下图表显示了滚动部署策略的表示:
这种部署策略对于单片应用程序也是一个不错的选择,如果我们希望通过应用程序的逐步推出来实现低停机时间,因为应用程序在基础设施内逐渐推出。
滚动部署提供了几个好处,例如:
-
易于恢复故障升级:如果应用程序的升级版本引入了一些错误或故障,我们可以在中间阶段轻松回滚升级。这是因为新版本在基础设施内逐渐推出。
-
易于设置:具有应用程序运行基础设施知识的情况下,这种部署策略易于设置和自动化,基础设施的不同部分逐一更新。
蓝/绿部署
蓝/绿部署策略是一种有趣的策略。该策略实现了一系列用于测试应用程序并在生产环境中启动的技术的混合。
在蓝/绿部署方法中,更新的应用程序被引入基础设施,其实例数量与旧版本的应用程序相同。完成后,在基础设施内测试新版本的应用程序。一旦发现版本稳定,流量就会从旧版本切换到新版本的应用程序,然后旧版本的应用程序被废弃。以下图表显示了蓝/绿...
金丝雀部署
在这种部署方法中,我们遵循与蓝/绿部署相同的策略,但有一个小改变。在蓝/绿部署中,测试是在内部进行的,一旦应用程序的新版本被标记为稳定,所有请求都将立即切换到新版本。
在金丝雀部署方法中,测试是基于实际用户请求进行的。负载均衡器被配置为将一定百分比的请求重定向到已部署在基础设施中的金丝雀版本,以查看新版本在实际请求存在的情况下的性能。
以下图表显示了金丝雀部署策略的表示:
当应用程序的内部测试被认为不足以满足要求,并且对应用程序运行的基础设施的稳定性存在疑虑时,通常会使用这种部署方法。
这种测试方法提供了在生产用例中测试应用程序的优势,同时允许在应用程序未达到预期结果时轻松回滚。
使用这种部署方法的缺点是基础设施内部的复杂性增加,现在需要智能地将部分传入请求路由到应用程序的金丝雀版本。
A/B 部署
A/B 部署方法与金丝雀部署方法有很多相似之处,其中新版本的应用程序被引入生产基础设施,并且一定数量的传入请求被重定向到金丝雀版本。
在 A/B 部署中,应用程序的升级版本(版本 B)被引入生产基础设施,然后负载均衡器被配置为根据一些预定义的标准将一定数量的请求重定向到升级版本。
当我们不确定升级版本将如何影响某个用户子集时,就需要这种部署方法。例如,使用智能手机的用户将如何受到升级版本的影响...
阴影部署
在阴影部署方法中,我们引入了一种新方法。与金丝雀部署或 A/B 部署相比,在这些方法中,一定数量的请求由旧版本处理,一定数量的请求由新版本处理,我们在生产基础设施中有两个应用程序版本。这些是旧版本和包含最新更新的新版本。
在阴影部署中,应用程序的更新版本看到与旧稳定版本应用程序发送的完全相同的请求,但新版本应用程序实例的任何处理都不会影响仅由生产中的稳定实例处理的请求的响应。以下图表显示了阴影部署策略的表示:
这种部署方式通常适用于基于微服务的应用程序,并且在开发人员希望测试应用程序在负载变化时的行为时使用。
这种部署方式也被用来检查应用程序在真实使用情况下是否表现正常,这是在内部环境无法测试的。
采用这种方式的唯一缺点是这种部署方式会增加基础设施成本,因为我们需要同时以全面规模运行旧版本和新版本。
现在,通过这个,我们已经习惯了不同类型的部署策略,这些策略可以帮助我们决定如何在生产环境中部署应用程序。虽然其中一些部署策略侧重于流程的简单性,但其他部署策略侧重于确保部署的新版本足够稳定并提供最佳结果。
选择应用程序部署的部署策略在很大程度上取决于几个因素,包括您可以承担的基础设施成本、可以花费在基础设施维护上的时间以及您计划部署的应用程序类型。另一个限制可以使用的部署策略的重要因素是应用程序之间的 API 是否发生了变化。通常,这些变化受 SLA 的约束,如果发生了变化,可能需要更新部署策略以适应所做的更改。
在撰写本书时,许多组织正在将云作为他们首选的基础设施选择,用于在生产环境中部署他们的应用程序。因此,让我们花一些时间了解目前存在的各种云基础设施,以及我们如何决定使用哪种基础设施进行部署。
选择基础设施
应用程序需要一个可以运行的基础设施。根据存在的应用程序类型,所需的基础设施可能会发生变化。选择哪种基础设施用于部署应用程序的选择受到正在部署的应用程序类型、应用程序的复杂性以及应用程序将支持的用例类型的极大影响。
在选择应用程序部署的基础设施时,另一个重要因素是对应用程序可扩展性的关注,包括我们可以如何扩展应用程序以及可以采用的扩展类型的复杂性。
首先让我们来看一下...
传统基础设施
过去,当应用程序使用大型单块来执行多个业务流程时,开发人员和组织通常会采用由大型主机或虚拟机组成的基础设施,以提供运行应用程序所需的充足资源。
这些裸机或虚拟机都配备了运行应用程序所需的所有要求,然后将应用程序部署在这些机器上,并提供给用户供一般使用。
这种基础设施选择运行良好,甚至允许多个应用程序存在于同一台强大的裸机服务器上,通过使用虚拟机进行隔离,将服务器的硬件抽象化。
然而,这种方法存在许多问题,例如:
-
基础设施成本高:对于使用裸机系统或虚拟机的部署,基础设施成本很高。组织要么需要购买能够运行这些应用程序的强大服务器,要么必须求助于专门的托管提供商,这通常成本很高。
-
开销增加:对于任何运行在虚拟机内的应用程序,运行支持应用程序的完整虚拟化操作系统所产生的开销非常高,大大减少了可以共存在同一硬件上的应用程序数量。
-
启动时间长:随着负载的增加,需要生成应用程序的新实例来处理增加的请求数量。然而,由于需要为虚拟机启动完成整个过程,启动完整的虚拟机及其中运行的应用程序实例是一个缓慢的过程。
-
扩展困难:在传统基础设施中,可以进行的水平扩展量非常有限,通常唯一的选择是通过使用垂直扩展来扩展应用程序,即根据需求增加应用程序所需的资源。
这些缺点使开发人员开始考虑替代传统的应用程序部署方式。
推动远离传统基础设施的另一个主要原因是转向容器化的应用程序打包方式。让我们来看看这是什么。
容器化的应用程序打包方式
随着现代硬件的出现和软件工程的进步,一些操作系统推出了一种轻量级的替代方案,用以取代笨重的虚拟机。这种替代方案以容器的形式出现,承诺不仅可以更轻量地进行应用程序隔离,而且由于它们根本不会对底层硬件进行抽象,因此也可以快速启动。
随着应用程序开发向微服务架构的转变,容器化的应用程序打包方式变得越来越主流。在这种方法中,每个微服务都被打包为一个单独的容器,可以部署...
向云端转移
在过去的十年里,许多云服务提供商已经出现,以帮助支持应用程序部署。每个云服务提供商都提供一套独特的功能,以使他们的服务在吸引组织和开发人员使用其平台进行应用程序部署时脱颖而出。
云部署模型的转变为负责应用程序开发的开发人员/组织提供了各种优势,包括以下内容:
-
降低基础设施维护成本:随着应用程序部署转移到云端,维护基础设施的成本正在降低。这是因为云服务提供商现在负责维护应用程序运行的硬件,个人开发人员和组织不需要购买这些硬件或处理可能发生的任何问题。
-
高运行时间:大多数云提供商保证其基础设施的高运行时间,这是由于他们在端上进行的大量基础设施复制。受益者是在云中维护特定应用程序的开发人员,因为现在他们可以为应用程序用户提供高运行时间,而不必担心基础设施崩溃可能导致的生产损失。
-
低延迟:在云部署方法中,开发人员可以为用户提供低延迟的应用程序。这是通过在云服务提供商的不同地理数据中心之间复制应用程序实例来实现的。一旦应用程序被复制,云服务提供商就会将请求重新路由到靠近客户端的应用程序服务器,以便实现低延迟响应。
-
易扩展:随着应用程序的负载增加,可能需要生成新的应用程序实例来处理增加的负载。云服务提供商通常提供动态扩展应用程序的功能,随着负载的增加而扩展应用程序,并随着负载的减少而缩减实例。这提供了一种高吞吐量、低成本的解决方案,可以处理高峰负载,而不必担心传统基础设施通常需要的手动干预。此外,与传统基础设施相比,这种扩展的响应时间通常较低。
所有前述观点都为应用程序转向基于云的部署提供了有力的论据。但根据组织的需求,他们可能希望或不希望将其应用程序部署在组织几乎无法控制的第三方服务器上。为了处理这种情况,组织可能决定转向在其基础设施上运行并处理组织内所有应用程序部署的私有云。因此,让我们花一些时间了解当前存在的各种云部署模型。
不同类型的云部署
对于企业来说,他们非常关注他们的应用程序在哪里运行。这是因为企业处理各种可能包含大量敏感信息的数据,任何一种违规行为都可能威胁到他们的业务。作为构建企业应用程序的开发人员,我们有责任建议和决定应该使用哪种云部署来部署应用程序。目前存在的云类型主要分为两大类:
-
公共云
-
私有云
最近,还出现了第三种类别,称为混合云。因此,让我们来看看...
私有云
私有云是由企业严格管理的一组计算资源。这些云运行在企业的企业内网中,通常位于组织拥有的数据中心或由第三方维护。
这些云实施了非常严格的安全策略,定义了在其上运行的应用程序如何被访问以及谁可以访问它们。
通常,企业选择私有云是因为以下几点:
-
企业已经拥有自己的数据中心,不想再投资于第三方云
-
企业运行的应用程序非常重视安全性,并且公共云提供商实施的安全策略不能被信任或不足以满足所需的用例。
私有云提供了一定的优势:
-
**更灵活:**由于组织控制决定私有云中将存在哪些计算资源,组织保持了灵活性,可以做出符合其最佳利益的决定
-
**提高安全性:**组织可以自由地在企业防火墙或内部网络后运行其云基础设施,并实施严格的安全策略,这在使用公共云时可能是不可能的
对于处理安全敏感数据并且没有成本障碍的企业来说,私有云是部署和运行应用程序的不错选择。
公共云
在公共云中,计算资源由第三方云服务提供商拥有和管理。作为企业,您部署的应用与其他应用共享相同的硬件资源,这些应用可能是您开发的,也可能是其他组织开发的。
当组织的应用程序不涉及可能需要严格的安全策略来防止任何事件发生的安全敏感数据,或者运行常用的应用程序,例如他们的电子邮件服务器时,通常会使用公共云。
公共云提供的优势是巨大的。其中一些如下:
- **降低成本:**由于公共云提供商提供的基础设施...
混合云
混合云部署模型提供了私有云和公共云方法的最佳结合。在这里,来自私有云和公共云的计算资源被汇集,应用程序可以根据需要从私有云转移到公共云。
企业通常采用这种部署模型,在公共云上运行一些不太安全敏感的应用程序,同时在私有云中运行安全敏感的应用程序。
通常采取的另一种方法是首先在私有云中部署应用程序,然后当请求数量增加时,从公共云中汇集资源,通过在公共云中启动更多进程来扩展应用程序。
混合云方法的好处如下:
-
**控制:**组织可以控制在私有云中运行安全敏感的应用程序,同时在公共云上运行不太安全敏感的应用程序
-
**灵活性:**在需要时,组织可以从公共云中汇集资源来处理更高的负载
-
**成本效益:**由于只有在应用程序需求高时才从公共云中汇集资源,组织可以通过仅在需要时使用公共云资源来节省公共云的成本
对于可以轻松从一个地方过渡到另一个地方的应用,或者可能需要动态扩展同时保持安全性的基础设施的应用,混合云部署方法为一个不错的选择。
总结
在我们对这一章的探索中,我们看了如何做出与企业应用程序部署相关的决策。我们探讨了不同的部署策略以及它们如何影响我们的应用程序在生产环境中的运行方式。接下来,我们了解了可用于部署单片和基于微服务的应用程序的六种不同部署策略,以及它们提供的优缺点。
一旦熟悉了部署策略,我们深入研究了应用程序部署的基础设施选择,并了解了从传统...过渡到基于微服务的开发方法如何推动了转变。
问题
-
采用蓝/绿部署方法有哪些好处?
-
在应用程序投入生产之前,金丝雀部署如何帮助测试应用程序?
-
如果我们使用虚拟机的方法来运行基于微服务的应用程序,可能会面临哪些问题?
-
我们如何在混合云模型中处理部署?
第十五章:企业应用集成及其模式
在本书的过程中,我们已经介绍了如何实现企业应用程序。这些应用程序要么是实现了大量组件以提供一定功能集的大型单体,要么是基于微服务的应用程序,其中应用程序由多个小型服务组成,所有这些服务都通过网络相互交互,以根据业务需求提供某种功能并提供输出。
但是,在任何企业中,我们很少会开发的应用程序是唯一存在的应用程序。相反,大多数情况下,企业基础设施将包括许多应用程序,这些应用程序具有...
技术要求
本章的后续不需要任何特殊工具或在开发系统上存在特定软件。但对中间件和企业服务总线解决方案功能的了解将有助于理解本章的上下文。
EAI 的需求
在任何大型企业中,可能存在一些应用程序来解决特定的问题领域。这些系统中的每一个都致力于解决一组特定的问题。通常,这种方法对于在企业内部构建应用程序是可取的,因为现在应用程序可以使用最佳的可用技术栈来解决其领域的问题。
但要使这些应用程序产生任何有用的业务影响,通常情况下这些应用程序需要以某种方式相互通信,以促进可能存在于一个应用程序中并且另一个应用程序需要的数据的交换。
但由于不同应用程序的集成是一项具有挑战性的任务,因为...
点对点集成
解决问题的方法之一是实现应用程序之间的点对点集成。这意味着每个应用程序都有一组连接器,允许它与另一个应用程序通信。对于需要彼此通信的每一对应用程序,都需要存在一个单独的连接器来促进应用程序之间的通信。
当需要相互通信的应用程序数量较少时,这种方法是完全可以接受的。但随着企业的发展,其需求也会增长,这意味着需要将新应用程序纳入基础设施中。现在,随着应用程序数量的增加,需要用于促进不同应用程序之间通信的连接器数量也将开始增长,达到一个基础设施变得过于复杂以至无法管理和维护的水平。
为了在企业内部集成大量应用程序,可能会随着时间的推移而增长,我们可能需要一些更灵活的东西,可以帮助我们标准化这些应用程序之间的通信方式。转向 EAI 正是我们正在考虑的架构类型,因为它旨在帮助我们实现这些目标。因此,现在让我们来看看 EAI 为我们提供了什么。
走向 EAI
当我们的目标是标准化基础设施中运行的不同应用程序之间的通信方式以及它们将如何存储数据时,EAI 方法确实为我们提供了一种选择,不仅具有灵活性,而且可扩展,而不会给基础设施引入不必要的复杂性。
EAI 模式为我们提供了一个框架,其中包含帮助我们标准化不同应用程序之间通信方式的工具和技术。EAI 框架通常配备了促进应用程序之间数据交换和数据从一种格式转换为另一种格式的组件,并充当不同应用程序之间的粘合层的组件。
传统的 EAI 方法
在 EAI 的早期,应用程序需要以各种格式相互交互,这可能包括通信一些信息或交换数据。为了促进这种交换,组织采用了 EAI 的中心枢纽模型。
在这种中心枢纽模型中,有一个基于路由器的中间件组件和事件的概念。每当一个应用程序的状态发生变化时,该应用程序会生成一个事件。其他应用程序订阅它们感兴趣的事件流。
现在,每当生成新事件时,路由器负责将事件传递给感兴趣的应用程序,并处理数据从一种格式转换为另一种格式,以便应用程序之间可以相互通信。在这种方法中,路由器成为促进不同应用程序之间集成的中心点。
路由器提供了许多功能,例如以下功能:
-
适配器和 SDK: 应用程序需要与路由器通信以触发事件,它们需要一种促进应用程序和路由器之间连接的粘合剂。路由中间件提供的适配器用于提供必要的粘合层。如果某个应用程序没有受支持的适配器,路由中间件将提供 SDK 以促进适配器的开发。
-
消息转换: 当生成新事件时,路由器根据一组预定义的规则,将与事件相关的消息转换为另一个应用程序可以消费的格式。这种功能对于促进两个不同应用程序之间的通信非常重要,每个应用程序都有自己的数据存储格式和通信风格。
-
智能路由: 应用程序需要无缝地相互配合,必须保证正确的事件能够到达正确的目标受众。路由中间件用于实现基于应用程序生成的事件和对该事件感兴趣的应用程序的智能路由,以便将作为事件一部分生成的消息传递给正确的接收者。
这种方法提供了一种很好的机制,可以消除企业基础设施中不必要的复杂性,如果每个应用程序都必须直接与其他应用程序通信,管理自己的连接器并处理数据一致性,那么这种复杂性将会增加。而采用这种方法,路由器促进了不同应用程序之间的通信。
但是,尽管这种方法带来了许多好处,但它也存在一些严重的缺点:
-
单点故障: EAI 的经纪模型被证明是一个单点故障。如果路由中间件出现故障,不同应用程序之间的所有通信将停止。
-
集中逻辑: 数据转换的逻辑以及数据的路由都集中在单个路由器中。这使得经纪成为一个复杂的组件,使得经纪的运营和维护成为一项艰巨的任务。
-
扩展性差: 当路由器的负载增加时,路由器处理消息的能力会受到影响。这会导致不同应用程序之间的数据状态不一致。此外,如果试图连接到彼此的应用程序位于世界各地的不同地理位置,那么单一的、集中位置的路由器将成为路由器地理扩展的障碍。
-
**专有解决方案:**在早期,当存在基于路由器的集线器和分支集成企业应用程序的方法时,大多数解决方案通常是专有的,只支持供应商的子集。对于从不受支持的供应商集成的应用程序,这对开发人员来说是一个巨大的问题,然后他们需要基于提供的 SDK 编写和维护自己的适配器。
所有这些问题都需要实施更好的方法,不会遭受基于路由器的方法所遇到的问题。最终,企业开始转向面向服务的架构(SOA)模型,并引入了企业服务总线(ESB)来集成 SOA 内部的不同服务。因此,让我们看看 ESB 如何改变了 EAI 的发生方式。
ESB 的引入
随着时代的推移,企业转向了一种新的应用程序开发模式。这种模式用于将应用程序建模为服务,其中每个服务提供一定的业务能力。因此,例如,在一个企业中将有一个工资服务,该服务将提供与员工工资管理相关的所有必要功能,例如处理新员工的数据,记录他们获得的薪水金额并生成每月的工资单。
现在,这些服务需要相互集成,以便在这些服务之间促进数据交换。在这一点上,企业需要一些...
EAI 中的模式
EAI 是一种方法,与之相关的有几种模式,它们规定了应用程序如何集成。通常使用哪种模式取决于企业基础架构中存在的应用程序类型,以及集成中存在哪些挑战。
因此,让我们看看这些模式通常是如何实现的。
集成模式
在 EAI 期间,集成模式定义了应用程序如何相互集成。这可能会定义不同应用程序如何相互通信以及这些应用程序如何转换数据。因此,让我们看看应用程序如何相互集成的两种广泛方式。
调解模式
在 EAI 的调解模式中,有一个负责事件传播的中央组件。例如,在基于代理的中间件模型中,每当一个应用程序生成一个事件时,该事件由中间件代理处理,然后负责将事件传播到对该事件感兴趣的其他应用程序。
在这种集成模式中,通常应用程序直接相互交互,由中间件代理进行便利,中间件代理传递发生的事件,之后另一个应用程序的事件处理程序负责。
另一种通常实现调解模式的集成方法是消息总线方法,其中消息总线充当不同应用程序之间传递消息以促进它们之间通信的调解者。
联邦模式
联邦模式在功能上与调解模式完全相反。虽然调解模式侧重于应用程序之间的直接通信,而不提供任何障碍,联邦模式通常限制应用程序之间的自由通信。
在联合模式中,中间件公开一组标准的端点,通过这些端点,其他应用程序可以与其通信。一旦应用程序向联合中间件的 API 发出请求,联合中间件就负责翻译并将该请求传递给后端应用程序。一旦后端应用程序处理了请求,联合中间件...
访问模式
访问模式定义了企业基础设施内应用程序对数据的访问方式。
通常有两种访问模式:异步和同步访问模式。让我们看看这些模式的目标是什么。
异步访问模式
异步访问模式遵循“发出请求并忘记”的数据访问方式。在这种情况下,一旦中间件转发了请求,它就不会等待该请求的响应返回,而是继续处理它正在接收的新请求。
异步访问模式通常在调解方法中使用,其中路由器中间件一旦被通知发生某个事件,就会传播该事件并忘记该事件的回复而不等待。
消息总线模型也是如此;一旦消息总线传递了消息,它就不再关心消息生成的响应,因此使得该过程...
同步访问模式
同步访问模式与异步模式相反。同步访问模式不是转发请求然后忘记响应,而是发出请求,然后等待其他应用程序生成响应。
这种模式通常在联合集成的情况下使用,其中中间件充当中间人,处理对其管理的后端应用程序的访问。
例如,在基于网关的模式中,中间件通常接受请求,将请求转发给后端应用程序,然后等待响应到达,然后再处理下一个请求。
这些只是控制 EAI 过程的一些基本模式。目前仍在使用近 65 种 EAI 模式,以促进 EAI 的概念。
现在,让我们来看一下一些常见的问题,这些问题阻碍了不同企业应用之间成功集成。
EAI 中的问题
企业应用程序的成功集成通常受到许多因素的影响;让我们来看一下:
-
**专有数据格式:**一些应用程序使用自己的专有数据格式,并且几乎没有关于如何与它们集成的文档,阻止了应用程序之间的集成,或导致应用程序集成质量不佳,从而导致一系列问题。
-
**数据一致性问题:**维护数据一致性可能成为 EAI 的问题。当每个应用程序都维护自己的数据源时,跨不同数据源的数据一致性可能会成为问题,特别是如果中间件遇到重负载,导致...
总结
在本章中,我们看到了为什么 EAI 对企业业务流程的正常运行是必要的。一旦我们了解了 EAI 的必要性,我们就开始了解 EAI 的方法,我们探讨了应用程序点对点集成以及为什么点对点集成的过程存在问题。然后我们探讨了通过使用经纪人中间件模型来实现 EAI 的传统方式,然后继续讨论模型是如何随着 SOA 的出现而转变的,以及 ESB 如何取代基于经纪人的模型。
然后我们继续理解 EAI 中的不同模式,并了解连接不同应用程序的调解和联合集成模式,然后理解不同访问模式(如异步和同步访问)在信息从一个应用程序传输到另一个应用程序时是如何工作的。我们通过探讨一些困扰企业成功集成应用程序的问题来结束本章。
随着我们进入下一章,我们将了解微服务的引入如何改变了 EAI 的格局,并取代了 ESB 的使用,现在被分布式消息代理和 API 网关所取代。
问题
-
在点对点集成过程中通常会面临哪些问题?
-
ESB 如何连接不同类型的应用程序?
-
存在哪些不同类型的 EAI 模式,促进了应用程序集成的方法?
第十六章:微服务和企业应用集成
微服务架构的引入彻底改变了企业应用程序的视角。这些应用程序不再是大型的单体或提供特定领域问题解决功能的大型服务。相反,现在我们有小型的微服务,每个提供特定的功能集。
这些小型微服务通过网络相互通信,以提供与组织业务需求相对应的特定输出。
随着我们在本章中的深入,我们将看到传统的企业应用集成(EAI)的传统方法正在被微服务的使用所淘汰,微服务引入了新的集成模式,由小型、无状态的消息代理组成,而不是一个庞大而复杂的企业服务总线。
客户端之间的通信现在已经被 API 网关取代,API 网关提供了客户端和后端微服务之间的联合。
作为本章的读者,您将学习以下内容:
-
微服务和 EAI 景观的变化
-
企业服务总线的转变
-
以微服务架构思考 EAI
技术要求
这一章建立在第十一章 采用微服务方法 和 第十五章 企业应用集成及其模式 的内容之上。因此,阅读本章的内容并不需要特殊的硬件或软件,但对分布式消息代理和异步消息系统的一些了解将有助于在阅读本章时提供更广泛的背景。
微服务和不断变化的 EAI 景观
最近,组织开始转向一种新的应用程序开发方法。这种方法侧重于开发由多个提供单一功能并且提供良好的小型服务组成的应用程序。这些小型服务被称为微服务。
这些微服务模拟了企业领域的一个子集的功能。例如,基础设施中可能有一个负责处理用户凭据和身份验证的服务,另一个可能负责处理电子邮件的功能,还有另一个处理员工工资的服务。
所有这些服务通过消息传递的机制或通过使用服务暴露的 API 从一个服务向另一个服务发出 API 调用来进行网络通信,以实现特定的用例。
与传统应用程序相比,传统应用程序通常庞大,并且需要中间件来处理数据从一个应用程序支持的格式到另一个应用程序支持的格式的转换,然后安全地传输这些数据,微服务要求通过 API 直接与其他服务通信,或者通过小型消息代理将数据以消息的形式从一个微服务传输到另一个微服务。
这改变了企业应用集成的方式,因为现在基础设施中没有复杂的中间件解决方案,提供粘合层来连接基础设施内的不同应用程序。
因此,让我们看看为什么传统方法在微服务架构中不起作用,并尝试理解出现的新替代方案,以促进企业应用程序的集成。
微服务中传统 EAI 的挑战
在通过现代的小型微服务开发实践开发、将它们托管在企业基础设施上,然后将它们集成在一起进行通信的应用程序中,我们不能再使用我们在运行和维护大型单片应用程序或服务时熟悉的传统方法。让我们先花点时间了解为什么点对点集成在微服务的情况下可能行不通。
微服务的点对点集成
在微服务的点对点集成方法中,我们使微服务直接通过它们暴露的 API 相互交互。为了实现这一点,每个微服务都需要了解其他服务暴露的端点。这是完全可以的,但是如果微服务需要执行依赖于与其他五个微服务交互的操作会发生什么呢?
在这一点上,我们必须将五个不同微服务的端点嵌入到我们的微服务中。作为一项一次性任务,这是一个完全可以接受的解决方案。但是,由于微服务的性质,它们会随着时间不断发展。这现在导致我们不断更新我们的微服务,以反映更新的 API。
这只是其中一个挑战。通常,基于微服务架构的应用程序会随着时间的推移而增长,拥有超过 100 个在基础设施中运行的服务,这使得在不同的微服务之间实现点对点集成变得非常困难。
那么,现在我们知道了微服务不能通过点对点集成来集成,我们能否使用老式的企业服务总线?让我们来看看。
使用 ESB 集成微服务
企业服务总线通常提供一个中间总线,通过该总线,两个应用程序可以通过消息传递机制进行通信。这个 ESB 还有一个标准格式,可以在发送之前对消息进行编码。
现在,我们可以将我们的微服务连接到 ESB,然后这些服务可以通过传递消息进行通信。这种方法是完全可以的,也是有效的。但是,当微服务的数量开始在基础设施中增长时,真正的问题开始出现。一旦发生这种情况,ESB 就会因为传输的大量消息而开始承受沉重的负载。
ESB 无法扩展的另一个原因...
利用 API 网关集成微服务
在微服务架构中使用 API 网关提供了一种非常有趣的方法来解决微服务集成问题,同时也遵循了应用程序集成的模式之一,即通过使用联合网关。因此,让我们看看 API 网关如何帮助我们进行微服务集成的过程。
微服务架构中的 API 网关充当中心点,通过它,微服务可以与基础设施中的其他微服务进行交互。这个 API 网关提供以下特点:
-
API 的受限暴露: API 网关提供了仅从后端微服务中暴露一组受限 API 的功能,因此限制了暴露的功能。除此之外,API 网关还可以在基础设施中引入新的 API 端点,其中每个 API 端点可以映射到后端微服务的多个 API 端点。
-
联合访问: API 网关实现了微服务的联合访问。这是因为,如果任何两个服务想要相互交互,需要向 API 网关发出调用,API 网关将确实向其他微服务发出请求,并从微服务中提供结果。
-
**请求的转换:**API 网关还负责在微服务之间转换请求,如果它们都使用不同的数据表示机制。为了进行这种转换,API 网关通常实现了一个通用的数据格式,每个服务都可以使用它来处理与 API 网关的通信,这个概念通常由 ESB 实现。
通过使用 API 网关进行微服务集成,不同微服务之间的通信过程如下:
-
想象一下有两个微服务,A和B
-
微服务A希望通知微服务B某个事件已经发生,这是某个调用或其他外部事件的结果
-
微服务A调用 API 网关暴露的微服务B的端点
-
API 网关接收请求,对请求进行任何类型的转换,并将调用转发给微服务B
-
API 网关现在等待来自微服务B的响应返回
-
一旦响应返回,API 网关将响应转换为微服务A支持的格式,并将响应返回,完成循环
其他服务通常也会遵循这种过程。
使用 API 网关集成微服务相比传统方法提供了许多优势,如以下示例所示:
-
**提高安全性:**由于 API 网关限制了后端 API 的暴露,API 网关在不同微服务之间提供更好的安全性。通过在不同微服务和 API 网关之间的通信中实现简单的端到端加密,也可以增加安全性。
-
**更好的可扩展性:**API 网关通过使用负载均衡器允许动态扩展,提供比传统基于中间件的方法更好的可扩展性。多个 API 网关进程可以在负载均衡器后运行,最终分发到它们的请求。
-
**更容易维护:**与使用 API 网关集成的应用通常更容易维护,因为它们需要单独管理的 API 端点数量减少了。
这些都是一些很大的好处,看起来是集成基于微服务的应用的一个很好的方法。那么,我们不再需要 ESB 了吗?ESB 已经消失了吗?
答案是否定的。相反,随着微服务的出现,它已经发生了变化。让我们看看这种转变是什么样子的。
ESB 的转变
随着微服务革命的出现,企业服务总线也发生了变化,现在已经被一些类似的解决方案所取代,但具有更好的可扩展性和去除单点故障的优势。
应用集成中的 ESB 曾经扮演着中央总线的角色,作为希望相互通信的应用之间的中介。ESB 通过引入通用数据格式并提供适配器来促进这种通信,应用可以通过这些适配器与 ESB 进行通信。
但 ESB 仍然存在两个主要缺点:
- 可扩展性: ESB 是一种沉重的中间件,需要专门化才能使用……
在微服务中重新思考 EAI
有了微服务的参与,他们拥有自己的工具和不同的要求,我们现在必须重新思考企业基础设施中的 EAI 方法。因此,让我们看看在考虑基于微服务的基础设施中的应用集成时需要注意的一些要点:
-
**扩展规划:**微服务基础架构内的应用不断发展,它们的集成需要以相同的方式进行规划。在考虑集成策略时,我们需要确保它能够支持我们应用的未来规模和应用可能需要的通信类型。
-
**定义 API:**微服务暴露的 API 在不同应用的集成中起着重要作用。在开始开发微服务之前,应该充分规划和记录其 API,以便与其他服务更顺畅地集成。
-
**保持数据格式标准:**不同微服务管理数据的数据格式应该标准化,只有少量格式,以便实现简单的集成和减少基础架构的复杂性。
总结
在本章的过程中,我们看了一下微服务作为企业应用开发方法的引入是如何彻底改变企业内部应用集成方式的。
我们看了一下当传统的企业应用集成方法应用到微服务架构时会失败,然后我们看了一下在引入 API 网关和分布式消息路由器后,EAI 的转变是如何发生的。
在本章结束时,我们看了一下随着我们转向基于微服务的方法,企业应用集成的规划发生了什么变化。
从这里,我们现在对不同方面有了一个概念...
问题
-
微服务应用中点对点集成的瓶颈是什么?
-
企业服务总线在微服务出现后发生了什么变化?
-
微服务架构内的消息代理如何提供高可用性?
第十七章:评估
第一章
答案 1
在 Python 3 标准中,不允许将不可变类型的byte类型和str类型进行连接;任何尝试连接这两种类型的操作都会引发TypeError错误。
答案 2
Python 3 中引入的类型提示支持只旨在提供对方法和参数进行更清晰的文档记录,并不强制执行任何操作标准。
答案 3
除了功能性和非功能性需求外,软件需求规格说明文档还指定了其他要求,如 UI、性能、业务和市场需求。
答案 4
各种类型的需求被分类如下:
-
**必须要求:**这些是必须存在于系统中的要求。如果缺少任何一个,其缺失将影响系统中的关键功能。
-
**应该要求:**这些要求如果存在,将增强应用程序的功能。
-
**可选要求:**这些要求在性质上是非关键的。如果缺少它们,不会对应用程序的功能产生任何影响。
-
**需求愿望清单:**这些是利益相关者可能希望在应用程序的未来更新中看到的要求。
答案 5
一旦生成了软件需求规格说明文档,流程的下一步包括软件的设计阶段。在设计阶段,决定软件应用程序的结构,并就可能使用的技术栈做出决策。
第二章
答案 1
Python 中的责任链模式允许我们构建一个考虑松散耦合的应用程序。这是通过将接收到的请求通过软件内的一系列对象链传递实现的。
以下代码片段显示了在 Python 中实现责任链模式的方法:
import abcclass Handler(metaclass=abc.ABCMeta): """Handler provides an interface to build handlers.""" def __init__(self, handler=None): """Initialize the handler. Keyword arguments: handler -- The next handler object to be called """ self._next_handler = handler @abc.abstractmethod def handler(self, data): """The handler abstract method. Keyword arguments: data -- The data to be processed by the handler """ passclass StringHandler(Handler): ...
答案 2
__new__方法是在需要创建对象的新实例时调用的第一个方法,而__init__方法仅在需要初始化对象的新创建实例时运行。在类实例创建的正常流程中,__new__方法将始终首先执行,并且只有在开发人员希望控制新实例的创建时才应该被重写。然后应调用__init__方法,该方法将在实例创建后调用,并且需要进行初始化。
答案 3
使用 ABC 元类很容易定义一个新的抽象类。以下代码片段展示了实现这种行为的示例:
import abcclass Handler(metaclass=abc.ABCMeta): """Handler provides an interface to build handlers.""" def __init__(self, handler=None): """Initialize the handler. Keyword arguments: handler -- The next handler object to be called """ self._next_handler = handler @abc.abstractmethod def handler(self, data): """The handler abstract method. Keyword arguments: data -- The data to be processed by the handler """ pass
第三章
答案 1
DBMS 中模式的规范化提供了许多好处,例如:
-
改进关系的整体组织
-
减少冗余数据的存储
-
改进数据库中数据的一致性
-
更好地索引数据,提高对数据的访问
答案 2
SQLAlchemy 中的延迟加载为开发人员提供了使用select或joined模式进行延迟加载的选项。当开发人员选择select模式加载数据时,数据集的加载通过发出 SQL SELECT语句进行,这样可以根据需求加载数据。
在使用joined时,相关数据集通过发出 SQL JOIN语句一次性加载。这种技术也被称为连接式急加载。
答案 3
在进行数据更新时,我们可以通过多种方式来保持数据的完整性。其中一种最简单的方法是通过使用事务来实现,这允许我们在原子事务中进行多个更新,其中要么应用所有更新,要么不应用任何更新。
在事务中的更新失败时,之前应用的事务中的更新也会被回滚,从而保持数据库中关系的一致状态。
答案 4
可以在数据库中实现的不同级别的缓存如下:
-
数据库级缓存: 当我们在数据库级缓存时,通常利用数据库的内置功能,通过维护查询缓存来缓存频繁使用的数据集。
-
块级缓存: 块级缓存发生在应用程序级别,我们将 ORM 层获取的数据缓存到基于内存的数据存储中,以避免每次请求特定结果时运行数据库查询。
-
用户级缓存: 在用户级缓存时,非安全关键数据通过会话 cookie 或本地存储在客户端缓存。
第四章
答案 1
Python 有两种不同的方式允许我们构建能够并发处理请求的应用程序。具体如下:
-
多进程: Python 的多进程模块允许开发人员启动多个进程以并行处理工作负载
-
多线程: Python 多线程模块允许开发人员执行多个线程,可用于处理并发工作负载
答案 2
当获得锁的线程突然终止时,根据获得锁的方式,可能会出现多种情况。
如果锁是通过 Python 中的with语句获得的,那么一旦线程终止,锁就会被释放。
如果锁是在try-except-final方法中获取的,那么当异常传播到最终语句时,锁将被释放。
如果锁是在没有任何安全程序的情况下获得的,线程的突然终止将导致死锁,因为锁没有被释放。
答案 3
通常,当主程序接收到终止信号时,信号也会传播到其线程;否则,线程可以被标记为守护线程,以便其执行随主程序终止而终止。
另一种实现方式是通过标志,线程可以定期检查。如果标志被设置,线程就会开始终止。
答案 4
不同进程之间的状态共享可以通过使用管道来实现,这可以帮助进程彼此通信。
答案 5
在 Python 中,我们有多种方法来创建进程池以分发任务。我们可以手动创建这些池——就像本章进程同步部分中的示例所示——或者我们可以利用concurrent.futures库中提供的ProcessPoolExecutor。
第五章
答案 1
为了通过多个应用程序实例处理请求,我们使用水平扩展的概念,其中我们在负载均衡器后启动多个相同应用程序的实例。负载均衡器负责在这些应用程序实例池中分发传入的请求。
答案 2
可以通过 Python 中的concurrent.futures库中的ProcessPoolExecutor来实现进程池。如何使用ProcessPoolExecutor在本章的使用线程池处理传入连接部分中有示例。
答案 3
完全可以编写一个同时使用多进程和多线程的程序。以下代码片段显示了这种实现方式:
import threading
import multiprocessing
def say_hello():
print("Hello")
def start_threads():
thread_pool = []
for _ in range(5):
thread = threading.Thread(target=say_hello)
thread_pool.append(thread)
for thread in thread_pool:
thread.start()
for thread in thread_pool:
thread.join()
def start_process():
process_pool = []
for _ in range(3):
process = multiprocessing.Process(target=start_threads)
process_pool.append(process)
for process in process_pool:
process.start()
for process in process_pool:
process.join()
if __name__ == '__main__':
start_process()
上述实现方式是有效的,并且可以轻松实现而不会出现任何问题,尽管您可能会发现它的使用案例有限,并且其使用将受到 GIL 实现的限制。
答案 4
本章的使用 AsyncIO 实现简单的套接字服务器部分展示了实现套接字服务器的简单示例。另一种通过使用aiohttp框架实现完全功能的 Web 服务器的方法是使用基于 AIO 的 HTTP 服务器。
第六章
答案 1
除了我们在本章中看到的通用View类之外,Flask 还提供了另一个预构建的可插拔视图类,称为MethodView。
答案 2
是的,我们可以删除用户表中对角色表的外键约束,并保持关系。但是每当我们需要存储数据时,我们将需要在用户表中手动插入角色对象所需的对象。
答案 3
有许多替代方案可用于为基于 Flask 的 Python 应用程序提供服务,例如以下内容:
-
uWSGI
-
扭曲的网络
-
mod_wsgi -
Gevent
答案 4
增加 Gunicorn 工作进程的数量非常简单。我们只需要在命令中添加-w <worker count>参数来设置 Gunicorn 工作进程的数量,如下例所示:
gunicorn -w 8 --bind 0.0.0.0:8000 wsgi:app
第七章
答案 1
使用 CDN 确实可以提高网页的加载性能。这是因为浏览器缓存来自给定 URL 的内容的方式。有时,当我们使用现有的 CDN 来提供一些内容时,我们可以获得以下好处:
-
对于一些常见的前端库,有可能用户的浏览器已经缓存了这些库,因为他们访问了其他网站,其中包括来自 CDN 的内容。这有助于我们避免重新下载这些库,减少带宽使用,并提高页面的加载速度。
-
CDN 还可以根据用户地理位置将请求路由到服务器,以便以最小的延迟下载内容,从而提高页面的加载速度。
答案 2
为了让浏览器使用现有的连接,我们可以利用一个叫做KeepAlive的概念。当请求中设置了KeepAlive头时,服务器会保持用于发出请求的连接打开一段固定的时间,希望可以在另一个请求中继续使用相同的连接,避免为每个请求进行初始连接设置的成本。
答案 3
JavaScript API 提供了一个非常方便的方法,称为removeKey(key),可以用来从浏览器的本地/会话存储中删除特定的键。
第八章
答案 1
单元测试和功能测试之间的主要区别是测试的范围,如下所述:
-
**单元测试:**单元测试通常侧重于测试软件中的单个组件,这些组件可以被分解为单个函数或类的方法。
-
**功能测试:**功能测试也称为集成测试,通常测试系统的特定功能,可能涉及多个组件之间的交互,以及它们与外部环境(如数据库系统)的交互。
答案 2
测试套件是需要在特定程序上运行的一系列测试用例。使用 Python 的unittest库编写测试套件非常容易实现。例如,如果您编写了一些测试用例,比如TestTextInput、TestTextUppercase和TestTextEncode,我们可以使用以下代码片段将它们组合成一个测试套件:
import texttest # Module containing our text related test casesimport unittest# Create a test loaderloader = unittest.TestLoader()# Create a test suitesuite = unittest.TestSuite()# Add tests to a suitesuite.addTests(loader.loadTestsFromModule(texttests)
答案 3
Pytest 中 fixture 的目的是提供一个固定和稳定的环境,以便测试用例可以执行。这些 fixture 负责通过设置所需的变量或接口来初始化环境,以便测试执行。
使用 fixture 的另一个优点是它的可重用性,允许同一个 fixture 在多个测试中使用而没有任何问题。
答案 4
Pytest 中的 fixture 作用域描述了 fixture 将被调用的频率。fixture 有许多不同的作用域可以应用于它们,如下所示:
-
**函数作用域:**Fixture 在每个测试中运行一次
-
**类作用域:**Fixture 在每个类中运行一次
-
**模块作用域:**Fixture 在每个模块中运行一次
-
**会话作用域:**Fixture 在每个测试会话中运行一次
第九章
答案 1
应用程序内部可能导致性能瓶颈的多个因素,包括以下内容:
-
不足的硬件资源规划,以运行应用程序
-
在应用程序中实施功能的算法选择不当
-
数据库关系实施不当,存在大量冗余
-
未对频繁访问的数据实施适当的缓存
答案 2
Python 中方法的时间分析有助于我们了解方法执行所需的时间。根据要求,我们可以通过几种不同的方式对方法进行时间分析,如下所示:
-
使用
timeit模块:timeit模块提供了一个功能,可以用来找出脚本或方法执行所需的时间。 -
使用
time**模块:**我们还可以使用time模块来帮助我们测量 Python 中方法的运行时间。我们可以通过创建装饰器来实现这一点,这可以帮助我们对方法的运行时间进行分析。 -
使用
cProfile模块:cProfile模块允许我们对 Python 程序内部的不同步骤进行性能分析。
答案 3
尽管 Python 是一种具有垃圾回收功能且没有直接访问内存指针的语言,但通过非法指针操作可能导致的典型内存泄漏几乎不会发生。但还有另一种方式,即 Python 程序可以继续消耗更多内存而不释放它。当程序忘记在对象不再使用时取消引用这些对象时,可能会导致分配新对象而不进行不再使用对象的垃圾回收。
答案 4
应用程序的 API 响应可以通过测量 API 返回响应所需的平均时间来进行性能分析,这可以通过多种方式进行测量,可能涉及使用 Python 标准库中的timeit或time模块。
答案 5
设计模式在应用程序性能中起着重要作用,不正确的设计模式可能会对应用程序性能造成影响。例如,考虑分配一个对象来实现应用程序中的日志记录。如果这个日志记录对象分配需要在每个单独的模块或类中进行,那么我们可能会浪费大量资源来分配一个对象,而这个对象本可以在不同模块之间共享。
第十章
答案 1
目前有许多问题使得应用程序的安全性变得困难。这些问题包括以下内容:
-
难以应对的复杂攻击
-
未被修补的 0-day 漏洞的增加
-
越来越多的国家支持的攻击针对系统的多个漏洞,通常很难追踪
-
越来越多的设备上线而没有适当的安全措施,使它们容易受到 DDoS 攻击的利用
答案 2
XSS(或跨站脚本)攻击是指攻击者在受信任的网站中注入恶意脚本。当加载带有恶意脚本的页面时,它会导致客户端系统受到攻击者的威胁。
答案 3
DoS(或拒绝服务)攻击是攻击者用来通过向系统发送多余的请求来使服务或资源对其用户不可用的一种方式,这会导致系统排队这些请求并造成服务中断。
可以通过在不同级别实施不同的技术来减轻攻击,例如:
-
添加防火墙规则以拒绝来自给定不受信任来源的流量
-
使用云安全提供商的服务,可以分析传入流量并在到达应用程序基础设施之前阻止它,有助于减轻 DoS 攻击
-
配置基础设施以将流量引导到没有运行应用程序的节点,或者重新路由...
答案 4
有很多可能的错误可能会危及应用程序的安全性,例如:
-
在应用程序内部使用不安全的第三方库,可能包含安全漏洞
-
不过滤用户提供的应用程序输入
-
在应用程序内部以未加密的方式存储安全敏感数据
-
不实施适当的限制以控制对内部基础设施的访问
第十一章
答案 1
面向服务的架构和微服务架构之间的主要区别在于,在面向服务的架构中,应用程序由不同的服务组成,每个服务提供组织业务领域之一的功能。这些服务通过企业服务总线进行通信,该总线将消息从一个服务路由到另一个服务,同时提供消息交换的通用格式。
在微服务的情况下,应用程序将由许多小型微服务组成,每个微服务负责提供仅限于单一功能的功能,可能无法映射到组织的完整领域,可能只是更大问题领域的子集。这些微服务通过各自微服务公开的 API 或通过无状态消息路由器进行通信,允许消息从一个服务传递到另一个服务。
答案 2
为了确保基于微服务的应用程序具有高的正常运行时间,我们可以使用以下技术:
-
不使用单一存储来存储所有微服务
-
在负载均衡器后运行同一微服务的多个实例
-
使用 API 网关在关键服务失败时提供优雅降级的服务,使客户端仍然在服务失败时收到响应
答案 3
使用服务级别协议(SLA)提供了许多保证,例如:
-
对服务的 API 稳定性的保证
-
对服务正常运行时间的保证
-
对服务的预期响应时间的保证
-
对服务实施的请求速率限制的保证
答案 4
API 网关可以通过服务注册表提供的 SDK 或通过服务注册表公开的 API 与服务注册表直接通信。这允许 API 网关自动从服务注册表中获取给定服务的正确位置。
答案 5
微服务内部的异步通信可以通过使用无状态消息代理来实现。要实现异步通信,一些微服务充当生产者,并将消息发送到消息代理队列。然后,其他微服务可能会消费该消息,处理它,并将响应发送回发送消息的微服务。然后,由请求微服务设置的回调函数来处理响应。这就是微服务之间的异步通信建立的方式。
第十二章
答案 1
微服务的集成测试基本上与单片应用程序的测试方式相同,只有以下几点不同:
-
如果微服务需要与另一个外部微服务通信,则集成测试可能需要设置外部服务,以便正确执行测试用例
-
组成单个服务的各个组件应该为基础设施中的所有微服务设置好,例如,伴随特定微服务的数据库需要用于测试目的
答案 2
单体应用程序的跟踪与基于微服务的应用程序的跟踪不同,单体应用程序的跟踪涉及理解请求从应用程序内的一个组件到另一个组件的流程。相反,跟踪基于微服务的应用程序涉及理解请求不仅在特定微服务内的流动,还涉及请求从一个微服务到另一个微服务的流动。
答案 3
有多个可用于微服务架构内跟踪的工具,如下列表所示:
-
Jaeger
-
Zipkin
-
Appdash
答案 4
为了跟踪微服务内的各个组件,我们可以利用 Jaeger 提供的功能之一,称为 spans。如何使用 spans 的示例可以在github.com/jaegertracing/jaeger-client-python中看到。
第十三章
答案 1
迁移到无服务器架构提供了许多优势,例如:
-
通过集成第三方服务减少开发工作量
-
操作复杂性减少,因为现在组织不需要关心基础设施
-
改进的安全性,因为各个功能在它们自己独立的容器中执行,这有助于我们保持不同功能之间不会相互干扰
-
应用程序的可扩展性得到改善
答案 2
使用后端即服务(BaaS)有助于通过集成 API 提供的常见功能来创建应用程序。这些服务由第三方提供商托管,从而减少了应用程序开发人员在从头开始重建它们的应用程序中所需的工作量。
答案 3
无服务器架构中的 API 网关将 API 端点映射到后端的一个功能。当特定事件发生时,客户端可以调用这些 API 端点,从而调用后端功能。
答案 4
有一些原因导致应用无法成功迁移到无服务器架构。这些原因如下:
-
使用无服务器基础设施提供商不支持的技术堆栈
-
需要存储请求处理状态以生成正确结果的应用程序
-
代码基础紧密耦合,很难定义个别方法
-
应用程序的某些组件执行时间非常长
第十四章
答案 1
使用蓝绿部署为我们提供了以下一系列好处:
-
能够立即将应用程序从一个版本切换到另一个版本
-
在新版本遇到一些关键功能错误时,能够轻松将应用程序从新版本回滚到旧版本
-
与应用程序升级相关的停机时间减少
答案 2
使用 Canary 部署可以帮助测试应用程序的以下方式:
-
应用程序经过一小部分真实请求的测试,这可能有助于暴露应用程序中的任何未识别的错误
-
Canary 部署使我们能够同时运行应用程序的新版本和旧版本,以便比较 API 提供的响应
答案 3
使用虚拟机运行基于微服务的应用程序可能会导致微服务实例的开销增加,因为虚拟机的要求更高。此外,使用虚拟机会限制可以共存在同一基础设施上的服务数量,因为虚拟机比容器更重,容器利用操作系统功能来保持程序隔离。
答案 4
混合云模型中的部署可以以与公共或私有云中处理的方式进行处理。区别在于应用程序需要进行扩展时。在这种情况下,使用混合云方法时,组织可以根据扩展的需求从公共云中汇集资源,然后在公共云中运行其应用程序的某些部分,而在私有云中运行其他部分。
第十五章
答案 1
企业应用程序的点对点集成需要为需要集成的每对应用程序构建连接器。这将创建一个复杂的基础设施,如果引入新应用程序,可能难以管理和扩展。
答案 2
企业服务总线负责通过消息传递机制帮助基础设施内的不同服务相互连接。ESB 为应用程序提供连接器,应用程序可以通过这些连接器连接到 ESB 并向 ESB 发送消息。
然后,ESB 负责将这些消息路由到它们预期的正确服务,从而促进基础设施内两个服务之间的通信。
答案 3
EAI 的不同类型的模式如下:
-
调解模式
-
联邦模式
第十六章
答案 1
由于基础设施中可能使用特定微服务的不同技术堆栈,因此很难实现不同微服务的点对点集成。这可能导致为每对微服务构建单独的连接器,以将一个微服务的数据格式转换为另一个微服务的数据格式。
另一个瓶颈是由于这些服务的可伸缩性,现在连接器必须连接部署的每个单个微服务实例。
答案 2
随着微服务架构的出现,企业服务总线已被无状态消息路由器所取代,这些路由器可以单独扩展,并为可能在基础设施内运行的大量微服务实现消息路由。
答案 3
微服务架构中的消息代理通过在多个消息代理实例之间复制消息队列来提供高可用性。这允许路由器取代失败的路由器,并保持基础设施内的通信完整。