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

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 を公開し(send は上記のサンプルコードで使用)、HTTP リクエストを実行します。どちらのメソッドも非同期的で、実際の 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', response1.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 を実行する同時リクエストが多すぎる場合、後続のリクエストが 500 Internal Server Error で失敗し、JavaScript ポリシーがタイムアウトする前でも「タイムアウト」エラー メッセージが表示されます。

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

影響

  1. JavaScript コードで waitForComplete() を実行する同時リクエストの数が事前定義された上限を超えると、API リクエストは 500 Internal Server Error で失敗し、エラー メッセージ「タイムアウト」が表示されます。
  2. 特定の JavaScript ポリシーの制限時間が経過していなくても JavaScript が「タイムアウト」エラーで失敗するので、問題の原因を診断しにくくなる可能性があります。

ベスト プラクティス

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

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

httpClient で callback を使用した 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);

関連情報