アウトバウンド REST Web サービス の RESTMessageV2 および SOAPMessageV2 の execute() と executeAsync() の比較Issue RESTMessageV2 および SOAPMessageV2 は、ServiceNow のお客様が ServiceNow から SOAP または REST スタイルの Web サービス呼び出しを作成するために使用できる Javascript API です。API は、execute() メソッドまたは executeAsync() メソッドで使用できます。API は、MID サーバーと共に使用することも、MID サーバーなしで使用することもできます。このドキュメントでは、これら 2 つのオプションをさまざまな組み合わせで使用した場合の動作と、その結果受けるパフォーマンスへの影響について説明します。 この記事を読む前に理解しておく必要がある主要な概念は、ServiceNow の「セマフォ」と「ワーカースレッド」です。これらについては多くの場所で説明されていますが、基本的に ServiceNow では、受信 UI 要求、Web サービストラフィック、およびバックグラウンドジョブ/操作を処理するために使用できるスレッドの数を制限します。(ServiceNow から何らかのリモートエンドポイントへの) 送信 Web サービスを設計する場合、これらのスレッドプールの内、どれがどのくらいの時間使用されているかを考慮し、長所と短所のバランスを取る必要があります。 [診断] > [統計] (/stats.do) を開くと、次のセクションでノードのスレッドプールの現在のステータスを確認できます。すべての ServiceNow インスタンスには複数のノードがあり、すべてのユーザーセッションは特定のノードに関連付けられます。 1.「デフォルト」のセマフォプールは UI 要求を処理します。ノードごとに 16 個* のデフォルトスレッドがあります。 2.「API_INT」セマフォプールは、Web サービス要求を処理します。ノードごとに 4 個* の API_INT スレッドがあります。 3.「スケジューラーワーカー」は、バックグラウンドジョブ/操作を処理します。ノードごとに 8 個* のスケジューラーワーカースレッドがあります。 * これらの数値は、2020 年、ServiceNow の Orlando リリース時点でのすべてのインスタンスのデフォルト設定です。 ServiceNow の /stats.do ページの詳細については、次の記事を参照してください。 13 reasons why the stats.do page is your best friend for troubleshooting (stats.do ページがトラブルシューティングの強力な味方である 13 の理由) 注: 1.可能な限り、外部統合エンドポイントで UI を待機させるなどのソリューションは避けてください。これには、送信 Web サービス呼び出しを行うアクティビティの前にタイマーがないワークフローが含まれます (KB0647534 - How does a Workflow Timer help? (KB0647534 - ワークフロータイマーはどのように役立ちますか?を参照)。 2.外部統合エンドポイントで UI を待機させないようにする方法がない場合は、非常に低いタイムアウトしきい値を設定し、direct execute() メソッドを使用する必要があります。応答を待たなければならない理由 (少なくとも、システムが外部ソースを待機していることと、システムが応答するまでの推定時間を示す簡単なメッセージ) をエンドユーザーに通知することをお勧めします。 3.ここに示す図では、すべて RESTMessageV2 を例にしていますが、同じ動作が SOAPMessageV2 にも適用されます。 4.ServiceNow の New York リリース時点で、スケジューラースレッド (各ノードで実行) が 1 秒ごとに新しいジョブを要求するようになったため、スケジュール済みジョブの作成/実行の遅延が大幅に削減されました。これは良いことです。これは、スケジュール済みジョブを活用する以下の非同期メソッドで、自動遅延時間が 5 〜 9 秒短くなることを意味します。大規模なお客様 (片側 5 つ以上のノードを持つ顧客) の場合、各ノードが個別にジョブを要求するため、これは決して無視できない遅延時間でした。 5.getBody() や getStatusCode() などの RESTResponseV2/SOAPResponseV2 getter メソッドは、waitForResponse(ms) を呼び出した場合と同じように、SOAPMessageV2/RESTMessageV2 に応答を待機させます。したがって、これらのメソッドを使用すると、HTTP 応答本文または HTTP 応答状態コードの入力を待機している間、開始スレッドが待ち状態となることに注意してください。 タイムアウト 接続タイムアウト 接続タイムアウトは、クライアント (ServiceNow インスタンスまたは MID サーバーなど) が Web サービスエンドポイントとの TCP 接続が確立されるまで待機する最大時間です。これは、setHttpTimeout(milliseconds) メソッドを使用して、要求ごとに手動で設定できます。このタイムアウト値は、同期要求と非同期要求の両方に適用されます。 ECC タイムアウト ECC タイムアウトは、executeAsync() メソッドが使用された場合に、getStatusCode、getBody、または waitForResponse が応答を待機する時間です。ECC タイムアウトは、HTTP Web サービス要求自体のタイムアウトにも、setEccTopic() または setEccCorrelator() が使用されている場合の ECC 応答処理のタイムアウトにも影響しないことに注意してください。 ECC タイムアウトは、さまざまな要因の影響を受けます。 プロパティ「glide.rest.outbound.ecc_response.timeout」と「glide.soap.outbound.ecc_response.timeout」はそれぞれ、executeAsync を使用する REST 要求と SOAP 要求 (ECC 経由で処理される要求) の特定のタイムアウトを制御します。プロパティ「glide.http.outbound.max_timeout.enabled」が true に設定されている場合 (既定値)、最大 ECC タイムアウトはプロパティ「glide.http.outbound.max_timeout」によって適用されます。この最大 ECC タイムアウトはデフォルトで 30 秒であり、30 秒を超える値に設定することはできません。上記のプロパティは、ミリ秒単位で引数を受け取る waitForResponse(seconds) に渡される値によって上書きできます。ただし、「glide.http.outbound.max_timeout.enabled」が true に設定されている限り、タイムアウトは 30 秒を超えることはできません。注:上記のように、executeAsync を使用した後に応答を待つことは理想的とはいえません。これにより、システムが再び同期されます。開始スレッドは、非同期スレッドが完了するまで待機する必要があります。これについては、この記事の後半で詳しく説明します。 タイムアウトのプロパティ タイムアウト関連の sys_properties は次のとおりです。 glide.http.connection_timeout 接続タイムアウト (上記の「タイムアウト」セクションを参照)setHttpTimeout(milliseconds) を介して要求レベルで上書きできます。デフォルト:10000 (ミリ秒単位) glide.http.timeout ソケットタイムアウト (上記の「タイムアウト」セクションを参照)setHttpTimeout(milliseconds) を介して要求レベルで上書きできます。デフォルト:175000 (ミリ秒単位) glide.http.outbound.max_timeout.enabled ecc_queue からの応答を待機しているときに適用されます。有効にすると、waitForResponse(seconds) で、より大きい値を上書きしようとしても、glide.http.outbound.max_timeout の値が使用されます。無効にすると、waitForResponse(seconds) または glide.rest.outbound.ecc_response.timeout の大きい方の値が使用されます。デフォルト:true glide.http.outbound.max_timeout ecc_queue からの応答を待機しているときに適用されます。glide.http.outbound.max_timeout.enabled が true の場合、ユーザーが waitForResponse(seconds) をより大きな値で呼び出した場合でも、ecc_queue からの応答を待機する最大時間を指定します。glide.http.outbound.max_timeout.enabled が false の場合、このプロパティは無視されます。デフォルト:30 (秒単位) -- 注:このプロパティの上限は 30 秒 glide.rest.outbound.ecc_response.timeout ecc_queue からの応答を待機しているときに適用されます。glide.http.outbound.max_timeout.enabled が false の場合、ユーザーが waitForResponse(seconds) をより大きな値で呼び出した場合でも、ecc_queue からの応答を待機する最大時間を指定します。glide.http.outbound.max_timeout.enabled が true の場合、このプロパティは無視されます。デフォルト:300 (秒単位) インスタンスから直接呼び出します。 これはデフォルトの動作です。MID サーバーを使用するには、使用する MID サーバーを明示的に指定する必要があります。 この場合、HTTP 要求は ServiceNow インスタンスから指定した Web サーバーエンドポイントに送信されます。 execute() でシンプルに保つ:MID サーバーなし、直接 REST 呼び出し、execute() execute() メソッドを使用するということは、Web サービス呼び出しが現在実行中のスレッドの ServiceNow インスタンスから直接実行され、その応答が返されるのを待つことを意味します。 execute() メソッドが使用されている場合、スレッドは応答を受信するまで一時停止します。 このメソッドは、要求が処理されて ServiceNow に応答が返されるまでにかかる時間によっては問題が生じる可能性があります。ServiceNow が応答を待っている間、呼び出しを開始したスレッドはすべてフリーズ状態となります。例えば、呼び出しがエンドユーザーのアクティビティによって開始された場合、応答が返されるまでエンドユーザーの画面は操作を受け付けない状態になります。 メリット:execute() は簡単に使用できます。メソッドを呼び出すと、1 行のコードで応答オブジェクトが返されます。 次に、応答オブジェクト API (SOAPResponseV2 または RESTResponseV2) に対して応答処理を実行します。 欠点:実行中のスレッドは、応答が返されるまでフリーズ状態になります。直接呼び出しが最も簡単な方法ですが、Web サービス呼び出しが UI から開始されている場合は、これが問題になる可能性があります。 executeAsync() で単調な同期サイクルから抜け出します。 通常、executeAsync() は MID サーバーと共に使用されます。しかし、MID サーバーなしでも使用できます。MID サーバーなしで executeAsync() メソッドを使用すると、SOAP/REST 要求がスケジュール済みジョブ (sys_trigger テーブル) を介して送信され、REST 要求に対する応答の詳細が ECC キュー (ecc_queue テーブル) に保存されます。 これは、現在実行中のスレッドは、executeAsync() が呼び出された直後に次の行に移動し、応答が返されるまでそのコード行で待つ必要がないことを意味します。これにより、UI によってトリガーされる要求のパフォーマンス時間を改善できます。 RESTMessageV2:MID サーバーなし、executeAsync()、応答処理なし 応答処理メソッドを使用せずに executeAsync() を使用することができます。ただし、この方法にはいくつかの欠点があります。応答を処理できないこと (処理の中止につながる可能性がある) に加えて、executeAsync() は、スケジュール済みジョブと ECC キューを使用するために、いくつかの複雑なレイヤーを導入します。要求はスケジュール済みジョブで実行されるようになったため、ユーザーが UI を操作してから応答を得るまでのレイテンシは、スケジュール済みジョブのキューに依存するようになります。これは、スケジュール済みジョブのキューが、遅い Web サービス要求呼び出しや他のジョブでいっぱいになった場合、問題になる可能性があります。また、この方法は、スケジュール済みジョブの作成と実行のために、わずかなオーバーヘッド (New York リリースでは約 0 ~ 1 秒) が発生します。これは、数ミリ秒以内に実行する必要がある Web サービスでは、パフォーマンスに顕著な影響を及ぼす可能性があります。元のスレッドを開始したユーザーが待つ必要がなくなるのは良いことですが、統合全体として見た場合の応答時間は影響を受けます。そのため、ニアリアルタイムで実行する必要がある統合には、executeAsync() は適していません。ただし、各リクエストに数秒のレイテンシを許容できる統合では、executeAsync() がうまく機能するでしょう。 開始スレッドからスケジュール済みジョブに処理を移行するのは良いのですが、スケジュール済みスレッドは応答を待っている間フリーズし、ジョブのスケジューリングのために遅延が生じることになります。 Web サービスの応答を待っている間に UI はフリーズしなくなったため、上記の方法では、いくつかの点で状況が改善されました。 しかし、応答を待っているわけではないため、応答を処理する方法がないことになります。 イーサネットを介して送信された Web 要求が処理されたかどうか (どのような応答が返ってきたか) をどのように知ることができるでしょうか?地上管制よりトム少佐へ。トム少佐、こちらの声が聞こえますか? RESTMessageV2:MID サーバーなし、executeAsync()、waitForResponse() 応答を処理できないという問題を克服するために、waitForResponse(seconds) メソッドが使用される場合があります。 しかし、MID サーバーを使用していない限り、この方法はあまりお勧めできません。 なぜでしょうか?その理由は次のとおりです。waitForResponse() メソッドでも、やはり開始スレッドがフリーズするため、開始スレッドの応答時間が遅くなり、非同期にすることで回避しようとしていた問題が発生してしまいます。 実際、waitForResponse() と組み合わせて使用すると、executeAsync() は非同期ではなくなります。 この設定は決して良い方法ではありません 。 スケジュール済みジョブを作成して処理するための遅延が生じるだけでなく、2 つのインスタンススレッドが常にフリーズすることになります。代わりに direct execute() メソッドを使用します。そうすれば、遅延をスキップして 1 つのスレッドだけを使用できます。 しかし心配する必要はありません。executeAsync を使用し、さらに開始スレッドをフリーズさせることなく応答を処理できるソリューションがあります。アナウンサーの声:ここで、(一般的なファンファーレ音の合図) setEccTopic() メソッドをご紹介します。 RESTMessageV2:MID サーバーなし、executeAsync()、setEccTopic() setEccTopic() メソッドでは若干の開発作業が必要になりますが、真の非同期の要求処理を可能にすると同時に、応答を処理するカスタム「センサー」を定義できるというメリットがあります。しかし、この方法にはまだいくつかの欠点があります。ここでは、作成、スケジューリング、取得、および処理する必要がある 2 つのスケジュール済みジョブがあるため、応答の処理を開始する前に少なくとも 1 ~ 2 秒の待機時間が追加されます (この図で 6 ~ 10 と表示されているのは、New York リリースでスケジュール済みジョブの開始待機時間が改善される前に作成されたためであることに注意してください)。ただし、Web サービスが追加の待機時間を許容でき、開始要求とは異なるスレッドで応答を処理できる限り、全般的に見て、これは優れたソリューションです。下の図を参照してください。また、このメソッドの使用方法については、KB0563615 - RESTMessageV2 API EccTopic Support (KB0563615 - RESTMessageV2 API EccTopic サポート) で詳しく説明しています。 1 つのジョブではなく 2 つのジョブを使用するようになったため、スケジュール済みジョブキューの遅延が増大しており、ワーカースレッドはフリーズします。しかし、開始スレッドはフリーズすることなく応答が処理されます。 サマリー waitForResponse() が使用されていない場合、executeAsync() メソッドを使用することで、実行スレッドは応答を待つことなく即座に続行できます。これにより動作はかなりスピードアップします。これは、Web サービスが UI によってトリガーされる場合、または何らかの理由で時間的な制約を伴う依存関係がある場合に重要です。一方、応答処理が必要な場合は、オプションを比較検討する必要があります。オプションの一覧を次に示します。 1.setEccTopic() メソッドを executeAsync() と組み合わせて使用し、新しいバックグラウンドスレッドを起動して応答を処理します (詳細は後述)。setEccTopic() は非同期の応答処理を可能にしますが、executeAsync() メソッドを実装する場合と同様に、ジョブのスケジューリングに要する固有の遅延と、他のジョブによるキューの過負荷が引き起こす遅延の対応に苦慮する可能性があります。また、これはすぐに表示する必要があるソリューション、つまり最初に RESTMessageV2 クラスをトリガーするコードでは機能しません。 2.別のオプションは、いわゆるポーリング設計を行って、UI がサーバーにすぐに要求を行い、探している結果が利用可能かどうかを確認するようにすることです。 欠点:ほとんどの Web サービス実装では、送信される要求に対して何らかのタイプの応答処理が必要です。waitForResponse() メソッドを使用すると、要求を処理できますが、非同期で処理することによるパフォーマンス上のメリットが失われます。 MID サーバーの使用 MID サーバーを使用すると、ServiceNow のお客様のネットワーク内のポイントから Web サービス要求を開始できます。MID サーバーは、ネットワークファイアウォールの内側に設置され、SOAP 統合を介して [主に] ServiceNow インスタンスと通信する Java 仮想マシンです。 MID サーバーを使用するように SOAP または REST Web サービスを構成するには 2 つの方法があります。 RESTMessageV2.setMIDServer(string) メソッドを使用して javascript で直接実行する方法、または RESTMessage メソッドを定義する場合に sys_rest_message_fn テーブルの use_mid_server フィールドを使用して実行する方法です。 execute() または executeAsync() を使用します。これらは、同じように動作します。 MID サーバーを使用する場合、execute() コマンドは暗黙的に非同期になり、どちらのメソッドも同じように動作します。 RESTMessageV2:MID サーバー、executeAsync()、待機なし ネットワーク内から REST/SOAP 要求を開始する場合は、MID サーバーと executeAsync() を使用します。応答を待つ必要がない場合、または応答を取得する他の方法 (例えば、相関 ID を使った双方向統合など) がある場合は、これはよい選択肢になるかもしれません。これにより、開始スレッドをすぐに解放し、ServiceNow セマフォリソース (デフォルト/API_INT) を最適化できます。 RESTMessageV2:MID サーバー、executeAsync()、waitForResponse() MID サーバー、executeAsync()、waitForResponse() を併せて使用した場合、開始スレッドをフリーズさせてしまいます。これは、パフォーマンスの観点から最悪のシナリオになる可能性があります。最も遅延時間が長く、最も障害となるポイントが多く、応答を待機しているスレッドが最もフリーズします。 これは本当に避けなければなりません。幸いなことに、setEccTopic メソッドと同様に、MID サーバーで使用できるメソッドが 2 つあり、真に非同期の方法で応答を処理できます。 RESTMessageV2:MID サーバー、executeAsync()、setEccParameter('skip_sensor', 'true'), setEccCorrelator() MID サーバーを介してネットワーク内から Web サービス呼び出しを開始し、応答を効率的に処理したい場合は、以下の方法を使用することで、目標をより効率的に達成できる場合があります。ただし、障害となるポイントが複数あり、各ステップで多少の遅延が発生することを考慮する必要があります。適切なタイムアウト/再試行の設定を使用し、例外処理を記述して、失敗した操作が確実に通知されるようにします。 MID を介してルーティングされる要求は、作成時に「setEccParameter('skip_sensor', 'true')」および「setEccCorrelator('[任意文字列]')」メソッドを使用する必要があります。これらのパラメーターは、どちらにも重要な機能があります。 出力 ecc_queue レコードの「トピック」を変更する代わりに、setEccCorrelator() はトピックを「RESTProbe」のままにします (これは MID が認識します) が、「agent_correlator」フィールドに任意の文字列を追加します。MID はこの要求を通常どおりに処理し、「agent_correlator」フィールドに同じ値を持つ入力 ecc_queue レコードとして対応するペイロードを書き戻します。デフォルトでは、MID サーバーによって書き込まれるすべての入力 ecc_queue レコードには、インスタンス内でそのレコードを処理するための対応する検出センサーがあることが想定されています。非同期 REST 要求の場合、これは当てはまりません (ecc_queue テーブルのカスタムビジネスルールなどを使用して処理を実行する必要があるため)。「skip_sensor = true」に設定しない場合、非同期 REST 要求は期待どおりに機能しますが、入力 ecc_queue レコードは「エラー」ステータスに設定され、「定義されたセンサーがありません」というメッセージが表示されます。「skip_sensor = true」を使用すると、これを回避できます。 MID サーバー経由でルーティングされた (そして、setEccCorrelator() を使用して) 非同期 REST 要求への応答を処理するには、ecc_queue テーブルに対して挿入後のビジネスルールを作成する必要があります。 これは、次のことを行う必要があります。 agent_correlator フィールドで特定の値を持つ入力レコードのみを処理するように、次のような条件を持たせます。 current.agent_correlator == "[任意の文字列]" && current.topic == "RESTProbe" && current.queue == "input" && current.state == "ready" スクリプトに、入力レコードのステータスを処理済みに設定するコードを含めます。 current.state = "processed"; current.processed = gs.nowDateTime(); current.update(); ビジネスルールが再帰的になることはないため、current.update() を「挿入」ビジネスルールで使用しても問題ありません。「更新」ビジネスルールでは、ビジネスルールが自動的にトリガーされて循環してしまうため、このタイプの動作を避ける必要があります。 Related Links KB0659194 - Troubleshooting outbound REST web services (KB0659194 - アウトバウンド REST Web サービスのトラブルシューティング)KB0563615 - RESTMessageV2 API EccTopic Support (KB0563615 - RESTMessageV2 API EccTopic サポート)KB0716391 - Best practices for RESTMessageV2 and SOAPMessageV2 (KB0716391 - RESTMessageV2 および SOAPMessageV2 のベストプラクティス)Developer site: Direct RESTMessageV2 API (開発者サイト:Direct RESTMessageV2 API)