NGINX PlusとNGINX JavaScript ModuleによるAPIリクエストのバッチ処理

NGINX | 2018年5月24日

The version of the NGINX JavaScript moduleのうち、NGINX Plus R15とともにリリースされたバージョンでは、サブリクエストを発行できるようになりました。つまり、JavaScriptコードからリクエストを開始できます。これにより、まったく新しいさまざまなユースケースに対応できます。

こうしたユースケースの1つがAPIリクエストのバッチ処理です。クライアントからの1つのAPIリクエストを一連のバックエンドサーバーへの複数のAPIリクエストに変換し、それぞれのレスポンスを集約して、1つのレスポンスとしてクライアントに返します。このブログでは、eコマースサイトを例に説明します。クライアントが特定の商品に関する情報をリクエストすると、カタログ、在庫、カスタマーレビューという3つのバックエンドサービスにサブリクエストが送信されます。

この記事では、NGINX Plus R15 announcementで紹介したサブリクエストの例を発展させます。この例では、サブリクエストを使用して同じリクエストを2台のバックエンドサーバーに送信し、最初のレスポンスのみをクライアントに返す方法を示しています。

[編集者注 – この記事は、NGINX JavaScriptモジュールのユースケースを紹介する一連の記事の1つです。完全な一覧については、「NGINX JavaScriptモジュールのユースケース」をご覧ください。

この記事は、 js_importディレクティブを使用するように更新されています。このディレクティブは、NGINX Plus R23以降でjs_includeディレクティブに代わるものです。詳細については、NGINX JavaScript moduleのリファレンスドキュメントを参照してください。Example Configurationセクションでは、NGINX設定ファイルとJavaScriptファイルの正しい構文を示しています。]

はじめに

この記事では、シンプルでわかりやすく、実際に動作するAPIリクエストのバッチ処理例を紹介します。これらの例は、以下の要件をすべて満たしています。

  • すべてのリクエストでHTTP GETメソッドを使用します。
  • すべてのレスポンスはJSON形式です。
  • 2種類の形式の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」で設定を動的に変更できます。

以下の図は、サポートされている2つのリクエスト形式を示しています。

最終要素リクエスト形式:

クライアントがAPIに最終要素形式のリクエストを送信

クエリ文字列によるリクエスト形式:

クライアントがAPIにクエリ文字列形式のリクエストを送信

どちらの例でも、クライアントはカタログ、在庫、レビューの各サービスから製品に関する情報を取得する必要があります。クライアントは、商品番号をURIの一部またはクエリ文字列として渡し、NGINX Plusに1件のリクエストを送信します。その後、3つのサービスにリクエストが送信され、各サービスからのレスポンスが集約されて、1件のレスポンスとしてクライアントに返されます。

これをNGINX Plusで動作させるには、次の2つのコンポーネントが必要です。JavaScriptプログラムとNGINX Plusの設定です。

JavaScriptプログラム

JavaScript関数batchAPI()は、クライアントリクエストごとに呼び出されます。この関数は、キー・バリューストアからAPI URIのカンマ区切りリストを取得して順に処理し、各URIに対してサブリクエストを実行するとともに、各レスポンスを処理するコールバック関数done()を指定します。末尾要素形式のリクエストでは、NGINX変数$uri_suffixに格納されたURIの最後の要素が、元のクライアントURIに含まれるその他のクエリ文字列引数とともに、各サブリクエストのクエリ文字列として渡されます。クエリ文字列形式のリクエストでは、クライアントリクエストのクエリ文字列が、各サブリクエストのクエリ文字列として渡されます。

呼び出しはキー・バリュー・ストアに記載されている順序で行われますが、非同期であるため、応答が返される順序は一定ではありません。応答は受信順に集約され、1つの応答としてクライアントに返されます。以下に、JavaScriptプログラムの最小構成版を示します。

コード例を読み込み中…

より充実したエラー処理、ログ記録、コメントを備えたJavaScriptプログラムのバージョンは、GitHub Gistリポジトリで「batch-api.js」として公開されています。

最小限のNGINX Plus構成

以下のNGINX Plus構成では、アップストリームサーバーのグループを使用して、前述の2つのリクエスト形式を実装します。簡潔にするため、この構成では、アップストリームサーバーが/myapi/service/item#形式の呼び出し(最終要素形式)を/myapi/service.php/?item=item#(クエリ文字列形式)に変換できることを前提としています。これは、サンプルのカタログ、在庫、レビューサービスで使用している言語であるPHPの「ネイティブ」URI形式です。

変換自体を実行する、より包括的なNGINX Plus構成については、以下の「URIを変換するためのNGINX構成の拡張」を参照してください。

構成ファイルをセクションごとに確認し、各部分の役割を説明します。

  • インポートする:JavaScriptファイル:

    コード例を読み込み中…

  • APIリクエストのリストを保持するための「キー・バリュー・ストア」を定義します。URIの最後の要素は、APIプログラムに渡す引数を示します。キーでは、$uri_prefix変数を使用して、URIの最後の/より前の部分を取得します。

    コード例を読み込み中…

    NGINX Plus R16以降では、さらに2つのキー・バリュー機能を利用できます。キー・バリュー・ストアの同期を設定する手順については、「NGINX Plus Admin Guide」を参照してください。
    • キーバリューストアのエントリに有効期限を設定するには、timeoutパラメーターをkeyval_zoneディレクティブに追加します。たとえば、バッチ処理された各URIを1日ごとに期限切れにするには、timeout=1dを追加します。
    • Synchronize the key‑value store across a cluster of NGINX Plus instances, by adding the syncパラメーターをkeyval_zoneディレクティブに追加して、NGINX Plusインスタンスのクラスター全体でキーバリューストアを同期します。この場合は、timeoutパラメーターも指定する必要があります。
  • クエリ文字列で引数を識別するAPI用に、別のキーバリューストアを定義します。ここでは、キー($uri)がクエリ文字列を除くURI全体を取得します。

    コード例を読み込み中…

  • 引数がURIの最後の要素となるリクエストを受け付けるAPI向けに、URIを2つの部分に分割する2つの「マップ」を定義します。変数「$uri_prefix」は最後の/より前のURI要素を取得し、「$uri_suffix」は最後の要素を取得します。

    コード例を読み込み中…

  • APIサーバーのアップストリームグループを定義します(ここでは、NGINX Plusとともにlocalhost上で実行されています)。

    コード例を読み込み中…

  • クライアントリクエストとサブリクエストを処理する仮想サーバーを定義します。

    コード例を読み込み中…

  • final‑element形式を使用するクライアントリクエストを処理するための「location」を定義します。この形式は、batchAPI関数に対して、$batch_api_arg_in_uri変数をonに設定することで指定します。

    コード例を読み込み中…

  • クエリ文字列形式を使用するクライアントリクエストを処理するlocationを定義します。この形式は、変数batchAPI関数に対して、$batch_api_arg_in_uriをoffに設定することで指定します。

    コード例を読み込み中…

  • サブリクエストを処理するlocationを定義します。

    コード例を読み込み中…

  • キーと値のストア内のデータを管理できるように、NGINX Plus APIを有効にします。

    コード例を読み込み中…

完全な設定は次のとおりです。

コード例を読み込み中…

バッチAPIリクエストの設定

NGINX Plusのキー・バリューストア機能を使用して、受信したクライアントリクエストを、実行する一連のAPIリクエストにマッピングします。前のセクションの設定では、2つのリクエスト形式に対して個別のキー・バリューストアを定義しています。

  • 最終要素スタイルのキーバリューストアの名前は「batch_api」で、キーには、リクエストURIの最後の「$uri_prefix」より前の要素を取得する/変数を使用します。
  • クエリ文字列形式のキー値ストアの名前は「batch_api2」で、キーは変数「$uri」です。この変数は、クエリ文字列を除く完全なリクエストURIを取得します。

どちらのキー・バリューストアでも、各キーに関連付けられた値は、実行時に変数「$batch_api」または「$batch_api2」で使用可能になるURIのカンマ区切りリストです。

最終要素形式のリクエストでは、/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#)に変換できるものとします。クエリ文字列形式はPHPプログラムの「ネイティブ」なURI形式であるため、変換する必要はありません。

すべてのサービスは、サービス名とitem引数を含むJSON形式のレスポンスを返します。たとえば、/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"}]]

URIを変換するためのNGINX Plus設定の拡張

前述のとおり、上記の最小限のNGINX Plus構成では、APIサーバーが次の形式のURIを変換できることを前提としています。/myapi/service/item# から /myapi/service.php/?item=item# への変換です。以下の変更を加えることで、この変換をNGINX Plus構成に実装することもできます。

  • サブリクエストを受信する新しい「upstreamグループ」を追加します。

    コード例を読み込み中…

  • 新しい「仮想サーバー」を追加し、「services」アップストリームグループが受信したリクエストを待ち受けるようにします。クエリ文字列形式のリクエストを受信した場合、URIにはすでに「.php」拡張子が付いているため、そのまま「api_servers」アップストリームグループにプロキシされます。最終要素形式のリクエストを受信した場合は、URIの最後の要素を削除してクエリ文字列引数に変換するようにURIが書き換えられます:

    コード例を読み込み中…

  • 新しいアップストリームグループにプロキシするように、/myapiのlocationを変更します。

    コード例を読み込み中…

完全な設定は次のとおりです。

コード例を読み込み中…

追加コンポーネント

サンプルのJavaScriptプログラムとNGINX Plus構成は、他の形式のAPIにも応用できます。このブログ記事の作成に使用したすべてのコンポーネントを使ってテストする場合は、PHPページを配信するためのNGINX Unit構成を含め、GitHubのGistリポジトリから入手できます。

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

まとめ

これらの例では、APIリクエストをバッチ処理し、集約したレスポンスをクライアントに返す方法を示します。NGINX Plusのキーバリューストアを使用すると、NGINX Plus APIを介してAPIリクエストを動的に設定できます。APIシステムの具体的な動作は多岐にわたるため、個々のAPIのニーズに合わせて、この例にはさまざまな機能強化を加えることができます。

NGINX JavaScriptのサブリクエストは、NGINX Open Sourceを使用してテストを開始できます。APIバッチ処理の例を試す場合や、キーバリューストアやNGINX Plus APIなど、NGINX Plusの機能を活用するその他のユースケースをテストする場合は、30日間の無料トライアルをお申し込みのうえ、お試しください。


共有

著者について

Rick Nelson
Rick Nelsonソリューションエンジニアリング担当リージョナルバイスプレジデント

Rick Nelsonのその他のブログ記事

関連するブログ記事

チュートリアル:NGINXを使用してアプリケーション向けにOpenTelemetryを構成する
NGINX | 2024/01/18

チュートリアル:NGINXを使用してアプリケーション向けにOpenTelemetryを構成する

OpenTelemetryを使用してNGINXサーバーの状態と健全性をより明確に把握することで、問題のトラブルシューティングを行い、Webアプリケーションのパフォーマンスを向上できます。

AkitaのNGINX Plus認定モジュールでAPIエンドポイントの問題を特定・修正
NGINX | 2023/05/31

AkitaのNGINX Plus認定モジュールでAPIエンドポイントの問題を特定・修正

Akita SoftwareのAPIオブザーバビリティ向け新しいNGINX Plus Certified Moduleを活用して、APIエンドポイントの問題をより迅速に特定し、修正できます。

JavaプロジェクトをスキャフォールディングしてKubernetesネイティブアプリを構築する方法
NGINX | 2023/02/09

JavaプロジェクトをスキャフォールディングしてKubernetesネイティブアプリを構築する方法

Red HatのシニアプリンシパルデベロッパーアドボケイトであるDaniel Oh氏が、Javaプロジェクトをゼロから構築するためのひな形を作成し、Kubernetesネイティブアプリケーションとしてビルドする方法を解説します。

F5 NGINXaaS for Azureのご紹介:Microsoft Azureでネイティブに利用できるSaaS型ロードバランシング
NGINX | 2022/05/24

F5 NGINXaaS for Azureのご紹介:Microsoft Azureでネイティブに利用できるSaaS型ロードバランシング

NGINXaaS for Azureは、NGINX PlusをAzureネイティブのSaaSとして利用できるサービスです。NGINXの設定を容易にリフト&シフトできるため、クラウド導入とモダンアプリの展開を加速できます。

SELinuxでNGINXおよびNGINX Plusを使用する
NGINX | 2018/08/17

SELinuxでNGINXおよびNGINX Plusを使用する

Red Hat Enterprise Linux(RHEL)および関連ディストリビューションでSecurity-Enhanced Linux(SELinux)を有効にすると、デフォルト設定によりNGINXとNGINX Plusの一部の操作が制限されます。この記事では、すべての機能を利用できるようにSELinuxの設定を変更する方法について説明します。

NGINX PlusによるAWS Auto Scalingグループの負荷分散
NGINX | 2017/03/06

NGINX PlusによるAWS Auto Scalingグループの負荷分散

新しいnginx-asg-sync統合ソフトウェアにより、NGINX PlusはAWS Auto Scalingグループの負荷を直接分散できるため、クラウドでの負荷分散がさらに柔軟になります