アンチパターン: JavaScript コードで waitForComplete() を使用する

現在、ApigeeApigee ハイブリッドのドキュメントを表示しています。
Apigee Edge のドキュメントを表示する。

JavaScript ポリシーを使用すると、API プロキシフローのコンテキスト内で実行されるカスタムコードを追加できます。たとえば、JavaScript ポリシーのカスタムコードは次の目的で使用できます。

  • フロー変数を取得して設定する
  • カスタム ロジックを実行し、障害処理を実施する
  • リクエストまたはレスポンスからデータを抽出する
  • バックエンドのターゲット URL を動的に編集する
  • リクエストまたはレスポンスに対して動的にヘッダーの追加または削除を行う
  • JSON レスポンスを解析する

HTTP クライアント

HTTP クライアントは、JavaScript ポリシーの高度な機能です。HTTP クライアント(httpClient オブジェクト)を使用すると、1 つ以上のバックエンド サービスまたは外部サービスを呼び出すことができます。HTTP クライアントは、複数の外部サービスを呼び出して、レスポンスを単一の API でマッシュアップする必要がある場合に特に便利です。

httpClient オブジェクトを使用してバックエンドを呼び出す JavaScript コードの例

var headers = {'X-SOME-HEADER' : 'some value' };
var myRequest = new Request("http://www.example.com","GET",headers);
var exchange = httpClient.send(myRequest);

httpClient オブジェクトは 2 つのメソッド getsend を公開して HTTP リクエストを行います(上記のサンプルコードでは send を使用)。どちらのメソッドも非同期で、実際の HTTP リクエストが完了する前に exchange オブジェクトを返します。

HTTP リクエストには数秒から数分かかる場合があります。HTTP リクエストが行われた後、リクエストからのレスポンスを処理できるように、完了したタイミングを知ることが重要です。HTTP リクエストが完了したタイミングを判断する最も一般的な方法の 1 つは、exchange オブジェクトの waitForComplete() メソッドを呼び出すことです。

waitForComplete()

waitForComplete() メソッドは、HTTP リクエストが完了し、レスポンス(成功 / 失敗)が返されるまでスレッドを一時停止します。これにより、バックエンドまたは外部サービスからのレスポンスが処理できるようになります。

waitForComplete() を使用した JavaScript コードの例

var headers = {'X-SOME-HEADER' : 'some value' };
var myRequest = new Request("http://www.example.com","GET",headers);
var exchange = httpClient.send(myRequest);
// Wait for the asynchronous GET request to finish
exchange.waitForComplete();

// Get and Process the response
if (exchange.isSuccess()) {
    var responseObj = exchange.getResponse().content.asJSON;
    return responseObj.access_token;
} else if (exchange.isError()) {
    throw new Error(exchange.getError());
}

アンチパターン

JavaScript コードで HTTP リクエストを送信した後に waitForComplete() を使用すると、パフォーマンスに影響を及ぼします。

HTTP リクエストの送信後に waitForComplete() を呼び出す次の JavaScript コードを考えてみましょう。

sample.js のコード

// Send the HTTP request
var exchangeObj = httpClient.get("http://example.com");
// Wait until the request is completed
exchangeObj.waitForComplete();
// Check if the request was successful
if (exchangeObj.isSuccess())  {

    response = exchangeObj.getResponse();
    context.setVariable('example.status', response.status);
} else {
   error = exchangeObj.getError();
   context.setVariable('example.error', 'Woops: ' + error);
}

この例で:

  1. JavaScript コードは HTTP リクエストをバックエンド API に送信します。
  2. 次に、リクエストが完了するまで waitForComplete() を呼び出して実行を一時停止します。

    waitForComplete() API では、バックエンドがリクエストの処理を完了してレスポンスを返すまで、JavaScript コードを実行しているスレッドがブロックされます。

Message Processor で JavaScript コードを同時に実行できるスレッド数には常に上限(30%)があります。この上限に達すると、JavaScript コードを実行できるスレッドがなくなります。そのため、JavaScript コードで waitForComplete() API を実行する同時リクエストが多すぎる場合、JavaScript ポリシーがタイムアウトする前であっても、後続のリクエストは 500 Internal Server Error となり、Timed out エラー メッセージが表示されます。

一般に、このシナリオは、バックエンドがリクエストの処理に時間がかかる場合や、トラフィックが多い場合に発生する可能性があります。

影響

  1. JavaScript コードで waitForComplete() を実行する同時リクエストの数が事前定義の上限を超えると、API リクエストは 500 Internal Server Error となり、エラー メッセージ Timed out が表示されます。
  2. 特定の JavaScript ポリシーの制限時間が経過していなくても JavaScript が Timed out エラーで失敗するため、問題の診断が難しい場合があります。

ベスト プラクティス

HTTP クライアントでコールバックを使用し、コールアウト コードを簡素化してパフォーマンスを改善します。また JavaScript コードで waitForComplete() を使用しないようにします。このメソッドは、HTTP リクエストが完了するまで JavaScript を実行するスレッドがブロックされないようにします。

コールバックが使用されると、スレッドは HTTP リクエストを JavaScript コードで送信し、プールに戻ります。スレッドがブロックされなくなったため、他のリクエストも処理できるようになります。HTTP リクエストが完了し、コールバックを実行する準備が整うと、タスクが作成され、タスクキューに追加されます。プールのスレッドの 1 つが、タスクの優先度に基づいてコールバックを実行します。

httpClient でコールバックを使用する JavaScript コードの例

function onComplete(response,error) {
 // Check if the HTTP request was successful
    if (response) {
      context.setVariable('example.status', response.status);
     } else {
      context.setVariable('example.error', 'Woops: ' + error);
     }
}
// Specify the callback Function as an argument
httpClient.get("http://example.com", onComplete);

関連情報