使用 Amazon CloudFront 使用同域策略提高单页应用程序 (SPA) 性能

在这篇文章中,我们将演示如何在 Amazon CloudFront 中使用相同的域名策略。使用此方法,您无需启用跨域资源共享 (CORS),从而提高了单页应用程序 (SPA) 的性能。

多年来,诸如ReactJS和AngularJS之类的SPA框架已成为开发和托管网站的流行方法,同时限制网络服务器上的资源使用。SPA 是一个 Web 应用程序,它通过 API 使用来自 Web 服务器的新数据动态重写当前网页,从而与用户/客户端进行交互。这极大地提高了网站的性能,并提供了更具动态性的用户体验。

SPA 部署的一种常用方法是利用 亚马逊简单存储服务 (Amazon S3) 来托管网络应用程序的静态资产,例如 htm l、javascript、样式表和图像。SPA 由 CloudFront 进行前端,用作可扩展的分发服务。此外,前端将与 亚马逊API Gateway公开的一组API 进行交互 。有关此架构,请参见下图。

Figure 1. Architecture of a typical SPA hosted in AWS.

图 1。在 亚马逊云科技 中托管的典型 SPA 的架构。

在我们深入探讨此设置的性能挑战之前,让我们回顾一下 CORS 和预检请求的一些基本原则。

CORS 和预检请求

“CORS 是一种基于 HTTP 标头的机制,它允许服务器指出除自身以外的任何来源(域、架构或端口),浏览器应允许从中加载资源”

CORS 定义了一种方法,使加载在一个域中的客户端 Web 应用程序与另一个域中的资源进行交互。CORS 让浏览器获得对另一个域资源利用的批准的机制是通过 “预检请求”。CORS 预检请求是一个 CORS 请求,它使用特定的方法和标头检查 CORS 协议是否被理解以及服务器是否知道。

在 SPA 的上下文中,API 调用通常是对托管后端应用程序的所有 API 端点的不同域进行的。这种模式需要在 API 上启用 CORS。之前在 亚马逊云科技 中部署的示例 SPA 需要在 API Gateway 上启用 CORS,因为对后端应用程序的 API 请求来自与 SPA 不同的来源。

启用 CORS 的后果之一是,如果 HTTP 请求不是 简单 请求,则会发出预检请求。

简单 HTTP 请求的一个标准是,如果存在内容类型标头,则其值只能 来自文本/纯文本、 多部分 /表单数据或应用程序/x-www-form-urlen coded。 由于 REST API 的使用很常见,因此大多数应用程序 HTTP 请求都包含标头 内容类型:application/json ,因此将请求呈现为非简单请求。

在此设置中,大多数 HTTP 请求将在后端应用程序提供内容之前产生预检请求。如果访问网站的用户所在的区域与 API 的区域不同,则预检请求的延迟可能会显著增加,从而由于性能不佳而降低用户体验。

下图描述了非简单 HTTP 请求的请求和响应顺序。

Figure 2. Sequence of requests and responses for CORS and non-simple http request.

图 2。CORS 和非简单 http 请求的请求和响应顺序。

解决此问题的一种方法是确保可以从同一个域访问前端和后端组件资源。这消除了预检请求,从而提高了应用程序性能。也可以通过引入可以将请求转发到 API Gateway 的反向代理来实现。我们可以利用 CloudFront 的多源功能来解决这个问题,而不是设置代理服务器。

使用 CloudFront 提供来自 API 的动态内容

CloudFront 通常用作内容分发网络 (CDN),也可以从应用程序后端提供动态内容。出于此目的使用 CloudFront 可以省去 CORS 预检请求,因为整个 SPA 都在同一个源上。请注意,SPA 还利用 CDN 组件,使用 亚马逊云科技 骨干网络将 API 请求路由到最近的接入点。这可以进一步提高性能(减少延迟),从而提高用户体验。

Figure 3. Architecture depicting use of CloudFront to serve data from API Gateway.

图 3。描述使用 CloudFront 为来自 API 网关的数据提供服务的架构。

使用 亚马逊云科技 云开发套件自动部署

上图中描述的架构可以使用 亚马逊云科技 云开发套件 (亚马逊云科技 CDK) 自动部署 ,该套件 定义了云基础设施。亚马逊云科技 CDK 是一个软件开发框架,它使用编程语言通过 亚马逊云科技 CloudFormation 进行部署。当应用程序运行时,它会编译由 CloudFormation 服务提供的完整格式的 CloudFormation JSON 和 YAML 模板。

我们 为你准备了一个 GitHub 存储库 ,其中包含一个用 TypeScript 编写的 亚马逊云科技 CDK 应用程序。要部署它,请完成以下步骤:

先决条件

要进行部署,您必须具备以下条件:

  • 一个 亚马逊云科技 账户。如果您没有 亚马逊云科技 账户,请 在此处 注册 。
  • 亚马逊云科技 CDK 已安装在您的本地环境中。
  • 用于托管 SPA 资产的 S3 存储桶。
  • 云端分发。
  • 一个 亚马逊云科技 Lambda 函 数。
  • 一个 API Gateway REST API, 集成了 Lambda 函数的 代理集成

部署软件包的步骤

1。使用命令行外壳克隆 GitHub 存储库。

git clone https://github.com/aws-samples/cloudfront-spa-with-samedomain-multiorigin

2。导航到存储库的根目录。

cd cloudfront-spa-with-samedomain-multiorigin

3。运行 npm install 来安装节点包依赖关系。

4。如果这是您第一次在您的 亚马逊云科技 账户中使用 亚马逊云科技 CDK 部署堆栈,请运行以下命令来引导您的 亚马逊云科技 环境。

cdk bootstrap

请注意,引导会将 亚马逊云科技 CDK 所需的资源启动到您的 亚马逊云科技 环境中。其中包括用于存储文件的 S3 存储桶以及授予运行部署所需权限的 亚马逊云科技 身份和访问管理 (IAM) Istentity Management 角色。

5。部署 亚马逊云科技 CDK 应用程序。

cdk 部署

让我们来看看已部署资源的配置。使用 亚马逊云科技 CDK 部署了四个主要组件以及所需的角色和权限。

以下屏幕截图显示了如何在 CloudFront 发行版上配置两个源。第一个源配置为从部署的 REST API 提供服务,第二个源提供来自托管 SPA 的 S3 存储桶中的文件。

Figure 4. Figure showing multiple origins configured on CloudFront distribution.

图 4。该图显示了在 CloudFront 发行版上配置的多个来源。

此外,发行版上还配置了两种行为。此配置决定哪些客户端请求应从 Amazon S3 提供,而不是 API Gateway。

Figure 5. Figure showing additional behavior configured on CloudFront distribution.

图 5。该图显示了在 CloudFront 分发上配置的其他行为。

在此部署中,我们禁用了缓存 `/api/*` 路径的行为,以确保 API 请求始终由 API 网关提供。但是,我们还启用了对提供 SPA 资产的默认行为的缓存。请注意,这不是先决条件。如果您的后端需要在 CloudFront 上缓存,则可以选择在该行为上启用缓存。此外,我们还将 Origin 请求策略设置为 “AllViewerexcepthostHeader”。本政策旨在与亚马逊 API Gateway 源一起使用。该源希望主机标头包含原始域名,而不是 CloudFront 分配的域名。将查看器请求中的主机标头转发到这些来源可能会阻止它们起作用。

Figure 6. Figure showing configured properties on additional behavior.

图 6。该图显示了其他行为的已配置属性。

对于 API 网关端点,由于我们依赖 CloudFront 作为所有请求的来源,因此未启用 CORS。请注意,此设置不限制您启用 CORS。如果您的 API 的其他使用者具有不同的源域,则您仍然可以选择启用 CORS。

该存储库包含一个示例 SPA,您可以选择使用以下步骤将其部署到预配置的 S3 存储桶。

  1. 运行以下命令来构建 Angular 应用程序。
cd sample-spa
npm install && ng build
  1. 将在 dist/sample-spa 下生成的所有构建文件复制到预置的 S3 存储桶的根目录。
  2. 现在,您可以从 CloudFront 分发域访问该网站。
  3. 通过从项目根目录运行以下命令来清理 亚马逊云科技 资源
cdk destroy

此设置允许将所有以 /api/ * 结尾的客户端请求发送 到 API Gateway 源,而其他请求则由托管 SPA 的 S3 存储桶提供服务,这是默认行为。此部署允许您在 S3 存储桶中托管 SPA 资产,实际上是使用 CloudFront 的域从客户端浏览器访问前端应用程序和后端 API。除了通过消除预检请求提高性能外,您的工作负载还受益于 CloudFront 接入点 (POP) 的延迟得到改善。

但是,这种模式需要权衡取舍。例如,一种设置,其中来自前端的所有请求都由单个 CloudFront 发行版作为前端,但 API 网关有其他不来自 CloudFront 的消费者,您希望使用不同的 Web 应用程序防火墙规则。在这种情况下,您需要为 CloudFront 和 API 网关启用单独的 Web 应用程序防火墙,这两个防火墙的防火墙规则可能相互重叠。这将增加成本,具体取决于针对相同请求多次评估了多少防火墙规则。

结论

在这篇文章中,我们学习了如何使用 Amazon CloudFront 提供 来自 API Gateway 的动态内容,以及如何通过消除预检请求来提高性能。本文中介绍的架构非常适合通过CloudFront分发提供服务并由API Gateway或任何其他使用RESTful API的专有后端提供支持的SPA。

Nikhil Enmudi

Nikhil 是 亚马逊云科技 的高级解决方案架构师,专门研究无服务器解决方案。他与全球独立软件供应商 (ISV) 客户合作开启云之旅,并帮助他们为其软件产品构建解决方案。