1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

IBM API Connect Mapポリシーパフォーマンス

1
Last updated at Posted at 2019-11-19

はじめに

IBM API Connectでは、APIの動作をアセンブルで定義します。
そのアセンブリーで利用可能なビルトインの動作ポリシーの一つがMapポリシーです。
Mapポリシーは、XMLもしくはJSON形式のデータを編集するポリシーです。

本記事では、Mapポリシーは使い方によって、パフォーマンスがよくないケースがあり、それをご紹介します。

なお、本記事で作成したAPI定義は、ここからダウンロード可能です。

前提条件

検証には、2019/11 時点のIBM Cloud API Connectサービスを利用します。
IBM CloudのAPI Connectは v5ベースですが、ver2018でも本記事の考え方は適用するはずです(経験者談)。

ケース1. 処理対象の項目数が多いケース

配列の要素数が多いなど、処理対象の項目数が多いケースは、若干パフォーマンスに悪化が見られます。

1万個の要素をもつ配列を含むJSONを処理するMapポリシーのパフォーマンスを計測してみます。

処理対象のJSONは以下の通り。

request.json
{
  "itemStr": "def",
  "itemInt": 99,
  "itemArray": [
    {
      "arrayStr": "abc",
      "arrayInt": 12
    },
    {
      "arrayStr": "abc",
      "arrayInt": 13
    },
    # 1万要素続く
  ]
}

Mapポリシーのaction部分は以下の通り。

単純な項目移送に加え、配列の要素毎分の固定値を返すような処理になっています。

mapポリシーaction部分抜粋

actions:
  - set: res.resStr
    from: req.itemStr
  - set: res.resInt
    from: req.itemInt
  - set: res.resFixed
    default: $(api.name)
  - create: res.resArray
    foreach: req.itemArray
    from: req.itemArray
    actions:
      - set: resArrayStr
        from: arrayStr
        value: 'var strVal="aaaaa"; strVal;'
      - set: resArrayInt
        from: arrayInt
        value: var intVal=1+100; intVal;

ポリシー毎の処理時間は、アクティビティーログのlatency_infoで参照できます。
アクティビティーログのサンプルは以下の通りです(100回試行した平均値にもっとも近い値を掲載します)。
各行の末尾の数値が、各ポリシーの処理終了時の経過時間です(単位はミリ秒)。
Mapポリシーの処理時間は、120-16=104msになります。

activity_log-latency-info抜粋
[
  {""task"":""Initialization"",""ended"":0}, 
  {""task"":""Start"",""ended"":11}, 
  {""task"":""prep"",""ended"":16},
  {""task"":""map-foreach-with-value"",""ended"":120}
]

1万要素に100msを要するのは、妥当なのかは判断が難しいところです。
比較として、GatewayScriptポリシーでMapポリシーと同じ処理を実装し、処理時間を計測してみます。

GatewayScriptポリシーのコード部分は以下の通り。

GatewayScriptポリシー抜粋

// get request values
var req = apim.getvariable('message.body');

// add array element
var res = {};
res['resStr'] = req['reqStr'];
res['resStr'] = req['reqInt'];
res['resFixed'] = '$(api.name)';

// create output itemarray
var resArray = [];
for (const duumy of req['itemArray']) {
  let strVal="aaaaa";
  let intVal=1+100;
  var map = {};
  map['arrayStr'] = strVal;
  map['arrayInt'] = intVal;
  resArray.push(map);
}
res['resArray'] = resArray;
apim.setvariable('message.body', res);

平均値にもっとも近い処理時間のアクティビティーログのサンプルは以下の通りです。
GatewayScriptの処理時間は、15ms。
Mapポリシーの処理時間が約100msなので、かなり開きがあることがわかります。

activity_log-latency-info抜粋(GatewayScript版)
[
  {""task"":""Initialization"",""ended"":0}, 
  {""task"":""Start"",""ended"":11}, 
  {""task"":""prep"",""ended"":17},
  {""task"":""gatewayscript"",""ended"":32}
]

ケース2. 変数参照回数が多いケース

Mapポリシーでは、$(変数名)の形式で、APIプロパティーやAPICのコンテキスト変数の値を参照することができます。
この変数参照のパフォーマンスがよくありません。

ケース1と同じJSONを処理するMapポリシーで処理時間を計測してみます。
Mapポリシーの設定は、ケース1から若干変更しており、$(1)でリクエストデータを参照し、若干加工する設定にしています。

mapポリシーaction部分抜粋

actions:
  - set: res.resStr
    from: req.itemStr
  - set: res.resInt
    from: req.itemInt
  - set: res.resFixed
    default: $(api.name)
  - create: res.resArray
    foreach: req.itemArray
    from: req.itemArray
    actions:
      - set: resArrayStr
        from: arrayStr
        value: var strVal=$(1); strVal;  #ここを変更
      - set: resArrayInt
        from: arrayInt
        value: var intVal=$(1) + 100; intVal; #ここを変更

平均値にもっとも近い処理時間のアクティビティーログのサンプルは以下の通りです。
Mapポリシーの処理時間は、979ms。
ケース1の処理時間は約100ms。
変数参照するだけで、顕著にパフォーマンスが悪化しました。

activity_log-latency-info抜粋
[
 {""task"":""Initialization"",""ended"":0}, 
 {""task"":""Start"",""ended"":11}, 
 {""task"":""prep"",""ended"":37}, 
 {""task"":""map"",""ended"":1016}
]

ケース1と同じく、GatewayScriptポリシーでMapポリシーと同じ処理を実装し、処理時間を計測してみます。
まず、GatewayScriptの実装内容は以下の通りです。

GatewayScriptポリシー抜粋

// get request values
var req = apim.getvariable('message.body');

// add array element
var res = {};
res['resStr'] = req['reqStr'];
res['resStr'] = req['reqInt'];
res['resFixed'] = '$(api.name))';

// create output itemarray
var resArray = [];
for (const reqArrayItem of req['itemArray']) {
  let strVal= reqArrayItem['arrayStr'];
  let intVal= reqArrayItem['arrayInt'] + 100;
  var map = {};
  map['resArrayStr'] = strVal;
  map['resArrayInt'] = intVal;
  resArray.push(map);
}
res['resArray'] = resArray;
apim.setvariable('message.body', res); 

次に、平均値にもっとも近い処理時間のアクティビティーログのサンプルです。
GatewayScriptポリシーの処理時間は、103ms。
ケース1の処理時間は、15ms。
こちらもパフォーマンスは悪化しましたが、Mapポリシーよりはましといったところ。

activity_log-latency-info抜粋(GatewayScript版)
[
  {""task"":""Initialization"",""ended"":0},
  {""task"":""Start"",""ended"":11},
  {""task"":""prep"",""ended"":32},
  {""task"":""gatewayscript"",""ended"":135}
]

Mapポリシーにおける変数参照パフォーマンスの改善策?

APICのマニュアルでは、Mapポリシーの変数参照パフォーマンス改善用の設定として、APIプロパティーに「x-ibm-gateway-map-resolve-apic-variables=false」を設定せよと記載があります(マニュアルリンク)。

が、ケース2のパターンに設定を追加すると、なぜかMapポリシーの処理時間が約2倍になるという結果に。。。 うーん、謎。

まとめ

Mapポリシーのパフォーマンスについて、2点取り上げました。
Mapポリシーを利用シーンとしては、入力値を処理に利用することが多いと思われるので、ケース2が多いと思われます。

本記事と同じようなケースで、Mapポリシーのパフォーマンスに悩まれている方は、GatewayScriptポリシーへの移行を考えてみては如何でしょうか?

参考ですが、API Connect v5->v2018への移行メモが公開されており、その中でアセンブルのベストプラクティスが掲載されています。
上記記事にも、シンプルなMap処理は、GatewayScriptでの実装を検討するように記載さています。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?