使用NGINX Plus和NGINX JavaScript模块批量处理API请求

NGINX | 2018年5月24日

随NGINX JavaScript 模块一同发布的NGINX Plus R15版本现在可以发起子请求,即可以在JavaScript代码中发起请求。这使其能够支持一系列全新的用例。

其中一个用例是批量处理API请求,将客户端发出的单个API请求转换为向一组后端服务器发出的多个API请求,再将各响应汇总为单个响应返回给客户端。本文以电子商务网站为例:当客户端请求某个特定产品的信息时,系统会向三个后端服务发出子请求,分别是产品目录、库存和客户评价。

本文以NGINX Plus R15 公告中的子请求示例为基础。该示例展示了如何使用子请求将同一请求发送到两台后端服务器,并仅将最先收到的响应返回给客户端。

[编辑注:本文是探讨NGINX JavaScript模块用例的系列文章之一。完整列表请参阅《NGINX JavaScript模块用例》。

本文已更新为使用 js_import指令,该指令在NGINX Plus R23及更高版本中取代了js_include指令。有关详细信息,请参阅NGINX JavaScript模块的参考文档,其中的配置示例部分展示了NGINX配置文件和JavaScript文件的正确语法。]

简介

本文旨在提供简单明了且可实际运行的API请求批处理示例。这些示例满足以下所有要求:

  • 所有请求均使用HTTP“GET”方法。
  • 所有响应均采用JSON格式。
  • 支持两种形式的API请求。请注意,无论采用哪种形式,查询字符串中都可以包含其他参数。
    • API程序的参数是URI的最后一个元素。我们将其称为“末元素请求样式”。在本例中,客户端对/batch-api/product/14286的请求会生成对/myapi/catalog/14286、/myapi/inventory/14286和/myapi/review/14286的请求。
    • 参数在URI的查询字符串中指定。我们将其称为“查询字符串请求样式”。在此示例中,对/batch-api2/product?item=14286的请求会生成对以下地址的请求:/myapi/catalog.php?item=14286、/myapi/inventory.php?item=14286和/myapi/review.php?item=14286。
  • 客户端请求触发的子请求集合可动态配置,这意味着无需手动编辑并重新加载NGINX Plus配置即可更改该集合。
  • 向后端服务器发出的API请求彼此独立。

解决方案概述

除了使用NGINX JavaScript的新子请求功能外,此解决方案还利用NGINX Plus的键值存储功能,从而可通过NGINX Plus API动态更改配置。

下图展示了支持的两种请求方式。

最终元素请求样式:

客户端向API发出最终元素式请求

查询字符串请求样式:

客户端向API发出查询字符串式请求

在这两个示例中,客户端都需要从目录、库存和评论服务获取某个产品的信息。客户端向NGINX Plus发送一个请求,并将商品编号作为URI的一部分或通过查询字符串传递。随后,请求会发送到这三个服务,并将各服务的响应聚合为一个响应返回给客户端。

要在NGINX Plus中实现此功能,需要两个组件:一个JavaScript程序和NGINX Plus配置。

JavaScript程序

对于每个客户端请求,都会调用JavaScript函数batchAPI()。该函数从键值存储中获取以逗号分隔的API URI列表并逐一遍历,为每个URI发出子请求,同时指定回调函数done()来处理每个响应。对于末尾元素样式的请求,URI的最后一个元素存储在NGINX变量$uri_suffix中,并作为每个子请求的查询字符串传递,同时还会传递原始客户端URI中的其他所有查询字符串参数。对于查询字符串样式的请求,客户端请求中的查询字符串会作为每个子请求的查询字符串传递。

调用按其在键值存储中的列出顺序发起,但由于调用是异步的,响应可能以任意顺序返回。系统按接收顺序聚合这些响应,并将其作为一个响应返回给客户端。以下是该JavaScript程序的精简版本:

正在加载代码示例…

GitHub Gist存储库中提供了该JavaScript程序的一个版本,其中包含更全面的错误处理、日志记录和注释,文件名为batch-api.js。

NGINX Plus 最简配置

以下NGINX Plus配置使用一组上游服务器,实现了上述两种请求样式。为简化说明,此配置假定上游服务器能够将以下形式的调用:/myapi/service/item#(末尾元素样式)转换为/myapi/service.php/?item=item#(查询字符串样式)。后者是PHP的“原生”URI格式;示例中的目录、库存和评论服务均使用PHP编写。

如需了解由NGINX Plus自行执行转换的更完整配置,请参阅下文的“扩展NGINX配置以转换URI”。

下面逐节查看配置文件,并说明各部分的作用:

  • 导入JavaScript文件:

    正在加载代码示例…

  • 定义一个键值存储,用于保存API请求列表,其中URI的最后一个元素用于标识API程序的参数。键使用$uri_prefix变量捕获URI中最后一个/之前的部分:

    正在加载代码示例…

    在NGINX Plus R16及更高版本中,可以使用另外两项键值功能。有关如何为键值存储设置同步的说明,请参阅NGINX Plus管理指南。
    • 为键值存储中的条目设置过期时间,方法是向timeout参数添加到keyval_zone指令中。例如,要让每个批处理URI每天过期,请添加timeout=1d。
    • 要在NGINX Plus实例集群中同步键值存储,请向sync参数添加到keyval_zone指令中。在这种情况下,还必须包含timeout参数。
  • 为参数通过查询字符串标识的API定义另一个键值存储。此处,键($uri)会捕获不含查询字符串的完整URI:

    正在加载代码示例…

  • 定义两个映射,将URI拆分为两部分,适用于接受参数位于URI最后一个元素中的请求的API。变量$uri_prefix捕获最后一个/之前的URI元素,$uri_suffix捕获最后一个元素:

    正在加载代码示例…

  • 定义API服务器的“上游组”(此处,这些服务器与NGINX Plus一同在localhost上运行):

    正在加载代码示例…

  • 定义用于处理客户端请求和子请求的虚拟服务器:

    正在加载代码示例…

  • 定义一个location,用于处理采用末尾元素样式的客户端请求。将变量batchAPI函数的$batch_api_arg_in_uri设置为on,即可向该函数指明此样式:

    正在加载代码示例…

  • 定义一个location,用于处理采用查询字符串样式的客户端请求。通过将batchAPI函数的$batch_api_arg_in_uri变量设置为off来指示此样式:

    正在加载代码示例…

  • 定义用于处理子请求的location:

    正在加载代码示例…

  • 启用NGINX Plus API,以便维护键值存储中的数据:

    正在加载代码示例…

完整配置如下:

正在加载代码示例…

配置批量API请求

我们使用NGINX Plus键值存储功能,将入站客户端请求映射到一组待执行的API请求。上一节中的配置为两种请求方式分别定义了键值存储:

  • 最终元素样式的键值存储名为batch_api,键为$uri_prefix变量,该变量会捕获请求URI中最后一个/之前的元素。
  • 查询字符串样式的键值存储名为batch_api2,键为$uri变量,该变量用于捕获不含查询字符串的完整请求URI。

在这两个键值存储中,与每个键关联的值都是以逗号分隔的URI列表,这些URI在运行时可通过变量$batch_api或$batch_api2使用。

对于最终元素样式的请求,我们希望将客户端对/batch-api/product的请求映射到以下3项服务的请求:/myapi/catalog、/myapi/inventory和/myapi/review。这样,对/batch-api/product/14286的请求将生成对以下服务的请求:/myapi/catalog/14286、/myapi/product/14286和/myapi/review/14286。

要将这些映射添加到batch_api键值存储,请向本地计算机上运行的NGINX Plus实例发送以下请求:

$ curl -iX POST -d '{"/batch-api/product":"/myapi/catalog,/myapi/inventory,/myapi/review"}' http://localhost/api/3/http/keyvals/batch_api

对于查询字符串形式的请求,我们希望将客户端对/batch-api2/product的请求映射到以下3个服务:/myapi/catalog.php、/myapi/inventory.php和/myapi/review.php。这样,对/batch-api2/product?item=14286的请求将生成对以下服务的请求:/myapi/catalog?item=14286、/myapi/product?item=14286和/myapi/review?item=14286。

要将这些映射添加到batch_api2键值存储中,我们发送以下请求:

$ curl -iX POST -d '{"/batch-api2/product":"/myapi/catalog.php,/myapi/inventory.php,/myapi/review.php"}' http://localhost/api/3/http/keyvals/batch_api2

运行示例

同一组PHP页面可处理以这两种请求样式发出的请求。为简单起见,我们假设后端服务器能够将末尾元素样式的URI(/myapi/service/item#)转换为查询字符串样式的URI(/myapi/service.php?item=item#)。无需转换查询字符串样式的URI,因为这是PHP程序的“原生”URI格式。

所有服务都会返回JSON格式的响应,其中包含服务名称和item参数。例如,请求/myapi/catalog.php?item=14286后,catalog.php会返回以下响应:

{"service":"Catalog","item":"14286"}

虽然这些示例中使用的PHP程序会在响应中包含服务名称,但并非所有服务都一定如此。为便于将响应与生成该响应的服务相匹配,JavaScript程序会在聚合响应中包含URI。

例如,对/batch-api/product/14286的请求所返回的聚合响应如下(其中3个组成响应的顺序可能有所不同):

[["/myapi/review/14286",{"service":"Review","item":"14286"}],["/myapi/inventory/14286",{"service":"Inventory","item":"14286"}],["/myapi/catalog/14286",{"service":"Catalog","item":"14286"}]]

扩展NGINX Plus配置以转换URI

如前所述,上文讨论的最简NGINX Plus配置假定API服务器能够将以下格式的URI转换为/myapi/service/item#:/myapi/service.php/?item=item#。也可以通过进行以下更改,在NGINX Plus配置中实现此转换。

  • 添加一个新的上游组,用于接收子请求:

    正在加载代码示例…

  • 添加一个新的虚拟服务器,用于侦听由services上游组接收的请求。收到查询字符串形式的请求时,只需将其代理到api_servers上游组,因为URI已包含 .php扩展名。收到末尾元素形式的请求时,将重写URI,移除URI中的最后一个元素,并将其转换为查询字符串参数:

    正在加载代码示例…

  • 将/myapi location更改为代理到新的上游组:

    正在加载代码示例…

完整配置如下:

正在加载代码示例…

其他组件

示例JavaScript程序和NGINX Plus配置可调整用于其他风格的API。如果您希望使用创建本博文时所用的全部组件进行测试,可以在我们位于GitHub的Gist存储库中获取这些组件,其中包括用于提供PHP页面的NGINX Unit配置:

batch-api.js
catalog.php
inventory.php
review.php
unit.config

结论

这些示例展示了如何批量处理API请求,并向客户端提供聚合响应。借助NGINX Plus键值存储,可以通过NGINX Plus API动态配置API请求。不同API系统的具体操作差异很大,因此可以对本示例进行多种改进,以满足特定API的需求。

可以使用NGINX Open Source开始测试NGINX JavaScript子请求。如果想尝试API批处理示例,或测试利用NGINX Plus功能(如键值存储和NGINX Plus API)的其他用例,请申请30天免费试用并开始体验。


分享

关于作者

Rick Nelson
Rick Nelson解决方案工程区域副总裁

Rick Nelson 的更多博客文章

相关博客文章

NGINX Plus R26 正式发布
NGINX | 2022/02/15

NGINX Plus R26 正式发布

NGINX Plus R26通过缓存JSON Web密钥集加快JWT验证,并强化TLS握手,从而提升安全性。NGINX JavaScript的新功能包括增强对异步函数的支持,以及实现WebCrypto API。

使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击
NGINX | 2022/02/10

使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击

gRPC具备开发人员设计和部署现代应用所需的速度与灵活性,但其开放性也使其极易受到第7层DoS攻击。NGINX App Protect DoS可保护gRPC应用免受此类破坏性攻击。

Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划
NGINX | 2021/09/13

Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划

了解选择错误的Ingress控制器可能带来的风险,以及如何从关键方面着手,让您的选择更好地适应未来需求。

NGINX Plus R19 正式发布
NGINX | 2019/09/03

NGINX Plus R19 正式发布

NGINX Plus R19扩展了监控功能,不仅提供更多指标,还新增了指标分析方式;增加试运行模式,用于测试限速效果;增强键值存储,以支持IP地址范围;并使限速更加动态。

NGINX现已正式加入F5
NGINX | 2019/05/09

NGINX现已正式加入F5

NGINX现已正式成为F5的一部分。Gus Robertson现任F5 NGINX业务部门负责人。他阐述了两家公司在人才、文化和愿景方面融合的价值,以及双方将如何携手增强NGINX的开源产品和商业产品。

使用NGINX和NGINX Plus实现高性能缓存
NGINX | 2016/08/24

使用NGINX和NGINX Plus实现高性能缓存

我将介绍NGINX在内容缓存和性能方面的工作原理,并分享一些控制NGINX内容缓存方式的技巧。