Help us understand the problem. What is going on with this article?

NeuStar VirtualUser Script 覚書

More than 1 year has passed since last update.

米国の Neustar 社が提供しているサービスの一つで、Webサイトに対する負荷試験が行える。

以下、VirtualUserでの試験実行でよく使うサンプルなど。

基本(GET)

単発でGET実行。POST等の他メソッドも概ね同じように利用可能。

var c = test.openHttpClient();

// Set Parameters
c.setCheckValidSSL(false);
c.setConnectionTimeout(30000);
c.setDNSCacheTimeout(0);
c.setEnableSNI(true);
c.setFollowRedirects(true);
c.setRequestTimeout(60000);
c.setRetryCount(0);
c.setSocketOperationTimeout(30000);
// Set Parameters

test.beginTransaction();
test.beginStep("Check Website");

c.get("https://google.com/");

test.endStep();
test.endTransaction();

HttpClient 設定変更

HttpClientに設定可能な値

Class AllowValues Deafult Note
setCheckValidSSL boolean false SSL証明書検証
setConnectionTimeout int 30000 接続タイムアウト、単位:ミリ秒
setDNSCacheTimeout int DNSキャッシュTTL、-1は永久、0は無効
setEnableSNI boolean true SNI有効化、'handshake alert: unrecognized_name:'が返される場合は false を指定
setFollowRedirects boolearn true リダイレクト追跡
setRequestTimeout int -1 リクエスト全体のタイムアウト(リトライも含む)
setRetryCount int 0 リトライ回数
setSocketOperationTimeout int 30000 ソケットタイムアウト、単位:ミリ秒
  • set〜Timeout を全て -1(無効) にすると永続化される
    • Operation timed out が別に設定おり、75秒程で切断は発生する模様

レスポンスのチェック

返却されるステータスコードの検証

  • リダイレクトは無効化する必要がある
var c = test.openHttpClient();
test.beginTransaction();
test.beginStep("Check Website");

c.setFollowRedirects(false);
c.get("http://google.com/xxx",302);
c.get("https://www.google.co.jp/",200);

test.endStep();
test.endTransaction();

Statusコードが異なる場合は失敗の扱いとなり、後続処理は行われない

Error Message: Expected status code of 302 but saw 404

レスポンス情報取得

レスポンスから各種情報を取得する

var c = test.openHttpClient();
test.beginTransaction();
test.beginStep("Check Website");

c.setFollowRedirects(false);
var res = c.get("https://www.google.co.jp/",200);
var info = res.getInfo();
test.log('Info BlockedTime = ' + info.getBlockedTime());
test.log('Info Browser = ' + info.getBrowser());
test.log('Info ConnectTime = ' + info.getConnectTime());
test.log('Info DnsLookupTime = ' + info.getDnsLookupTime());
test.log('Info End = ' + info.getEnd());
test.log('Info ErrorMessage = ' + info.getErrorMessage());
test.log('Info Host = ' + info.getHost());
test.log('Info Method = ' + info.getMethod());
test.log('Info Path = ' + info.getPath());
test.log('Info PostBody = ' + info.getPostBody());
test.log('Info PostParams = ' + info.getPostParams());
test.log('Info Protocol = ' + info.getProtocol());
test.log('Info QueryString = ' + info.getQueryString());
test.log('Info ReasonPhrase = ' + info.getReasonPhrase());
test.log('Info ReceiveTime = ' + info.getReceiveTime());
test.log('Info RedirectUrl = ' + info.getRedirectUrl());
test.log('Info RequestHeaders = ' + info.getRequestHeaders());
test.log('Info ResolvedIpAddress = ' + info.getResolvedIpAddress());
test.log('Info ResponseHeaders = ' + info.getResponseHeaders());
test.log('Info SendTime = ' + info.getSendTime());
test.log('Info SslHandshakeTime = ' + info.getSslHandshakeTime());
test.log('Info Start = ' + info.getStart());
test.log('Info StatusCode = ' + info.getStatusCode());
test.log('Info TimeActive = ' + info.getTimeActive());
test.log('Info TimeToFirstByte = ' + info.getTimeToFirstByte());
test.log('Info Url = ' + info.getUrl());
test.log('Info WebSocketMessages = ' + info.getWebSocketMessages());
test.log('Body = ' + res.getBody());
test.log('CharSet = ' + res.getCharSet());
test.log('ContentType = ' + res.getContentType());
test.log('ErrorMessage = ' + res.getErrorMessage());
test.log('Url = ' + res.getUrl());
test.log('StatusCode = ' + res.getStatusCode());
test.log('StatusText = ' + res.getStatusText());
res.getAllHeaders().forEach(function(header){
  test.log('Header = ' + header.name + " : " + header.value);
});

test.endStep();
test.endTransaction();
  • 取得例
=====================================================
Script Log
=====================================================
Info BlockedTime = 0
Info Browser = null
Info ConnectTime = 0
Info DnsLookupTime = 2016
Info End = Sun Feb 25 19:46:37 JST 2018
Info ErrorMessage = null
Info Host = www.google.co.jp
Info Method = GET
Info Path = /
Info PostBody = null
Info PostParams = {}
Info Protocol = https
Info QueryString = null
Info ReasonPhrase = OK
Info ReceiveTime = 580
Info RedirectUrl = null
Info RequestHeaders = {User-Agent=[Ljava.lang.String;@6fe643b9, Connection=[Ljava.lang.String;@25a1d233, Host=[Ljava.lang.String;@43afa9bc, Accept-Encoding=[Ljava.lang.String;@5494c28b}
Info ResolvedIpAddress = 172.217.26.35
Info ResponseHeaders = {X-Frame-Options=[Ljava.lang.String;@932a82b, Transfer-Encoding=[Ljava.lang.String;@2229c374, Cache-Control=[Ljava.lang.String;@1cf57cc4, Server=[Ljava.lang.String;@6e07754b, Alt-Svc=[Ljava.lang.String;@7268eb50, Content-Encoding=[Ljava.lang.String;@1eb6a40a, Set-Cookie=[Ljava.lang.String;@2992e844, Expires=[Ljava.lang.String;@7cea6d10, P3P=[Ljava.lang.String;@496d2bb6, X-XSS-Protection=[Ljava.lang.String;@62504065, Date=[Ljava.lang.String;@5c3e44a3, Content-Type=[Ljava.lang.String;@731320d9}
Info SendTime = 3
Info SslHandshakeTime = 0
Info Start = Sun Feb 25 19:46:34 JST 2018
Info StatusCode = 200
Info TimeActive = 2711
Info TimeToFirstByte = 112
Info Url = https://www.google.co.jp/
Info WebSocketMessages = []
Body = <!doctype html>
・・・略
</html>
CharSet = Shift_JIS
ContentType = text/html; charset=Shift_JIS
ErrorMessage = null
Url = https://www.google.co.jp/
StatusCode = 200
StatusText = OK
Header = Date : Sun, 25 Feb 2018 10:46:22 GMT
Header = Expires : -1
・・・略
Header = Transfer-Encoding : chunked

Cookie 取得

ログイン情報をPOSTし、Cookieを取得、使用する例

var c = test.openHttpClient();
test.beginTransaction();
test.beginStep("Check Website");

var req = c.newPost("https://test.example.com/login.html");
req.addRequestParameters({
  'username': 'hoge',
  'password': 'fuga',
});
res = req.execute();
cookie = res.getHeader('Set-Cookie');
test.log('cookie : ' + cookie);

var req = c.newGet("https://test.example.com/");
req.addRequestHeaders({
'Cookie': cookie,
});
var res = web.execute(req);

test.endStep();
test.endTransaction();

HttpClient 動作変更

remapHost

ドメイン名を置き換える。 /etc/hosts のような使い方が可能。

var c = test.openHttpClient();
c.remapHost(sourceDomain, domain);

BASIC認証

var domain = "test.example.com";
var user = "hoge";
var pass = "fuga";

c.autoBasicAuthorization(domain, user, pass);

UserAgent 書き換え

規程のUserAgent "WPM HttpClient/1.0" を任意の文字列に書き換える。

var c = test.openHttpClient();
c.addRequestInterceptor(function(req) {
    req.removeHeaders("User-Agent");
    req.addHeader("User-Agent", "iPhone; WPM HttpClient/1.0");
});

応用(GET)

  • WebBot
    • HTMLを解析し、ページ内リンクに沿ってダウンロードを実施する
  • HttpClient.newGet
    • リクエスト実行前にGET処理を書き換える

ページ内リンクダウンロード

HTMLを解析し、リンクを追従。
Javascript、CSSは解析対象外。

JMeterで言う所の HTTPリクエストの繰り返しダウンロードする

解析・ダウンロード無し

var c = test.openHttpClient();

test.beginTransaction();
test.beginStep("Check Website");

var req = c.newGet("https://www.google.co.jp/");
req.execute();

test.endStep();
test.endTransaction();
  • 指定URLのみが対象
Total Objects: 1

    12143b  200 1466ms  https://www.google.co.jp/

解析・ダウンロード有り

var c = test.openHttpClient();
var web = require('webbot');

test.beginTransaction();
test.beginStep("Check Website");

var req = c.newGet("https://www.google.co.jp/");
var res = web.execute(req);

test.endStep();
test.endTransaction();
  • HTML内のURLまで対象となる
Total Objects: 2

    12635b  200 846ms   https://www.google.co.jp/
    22151b  200 134ms   https://www.google.co.jp/logos/doodles/2018/girls-day-2018-5202534598705152.2-l.png

特定のドメイン・URLを無視する

  • 通常(リスト無し)の場合の返却値
    492231b 200 1533ms  https://www.youtube.com/
    5878b   200 47ms    https://www.youtube.com/yts/jsbin/scheduler-vfl3G47Co/scheduler.js
    ・・・
    13661b  200 485ms   https://i.ytimg.com/vi/7glUp6bDU-k/hqdefault.jpg?sqp=-oaymwEWCMQBEG5IWvKriqkDCQgBFQAAiEIYAQ==&rs=AOn4CLBTAJOcDbo4HIbx4ZfiuRgdqx8Reg
    ・・・
    2353b   200 289ms   https://yt3.ggpht.com/ES6oTwtm9iPPjKbkLSbFLpaRdbRObia30kmFKZdcCP1gBasaKEdyVYxrz6zkBC36m-_eYI3pRleRoMEMR3k=s88-nd-c-c0xffffffff-rj-k-no
    43b 200 30ms    https://www.youtube.com/yts/img/pixel-vfl3z5WfW.gif
    ・・・

blacklistRequests

正規表現に一致するHTTPリクエストには固定のステータスコードを返す。

var c = test.openHttpClient();
var web = require('webbot');

test.beginTransaction();
test.beginStep("Check Website");

c.blacklistRequests("https://i\\.ytimg\\.com/.*", 204);
c.blacklistRequests(".*yt3.ggpht.com.*", 204);

var req = c.newGet("https://www.youtube.com/");
var res = web.execute(req);

test.endStep();
test.endTransaction();
  • blacklistRequests での返却値
    514856b 200 1462ms  https://www.youtube.com/
    5878b   200 17ms    https://www.youtube.com/yts/jsbin/scheduler-vfl3G47Co/scheduler.js
    ・・・
    0b  204 0ms https://i.ytimg.com/vi/FYVn2ne7hJc/hqdefault.jpg?sqp=-oaymwEWCMQBEG5IWvKriqkDCQgBFQAAiEIYAQ==&rs=AOn4CLD35p4cHO4FZrVP8dzLpSQEn2PwoQ
    ・・・
    0b  204 0ms https://i.ytimg.com/vi/xyaFPwzWS20/hqdefault.jpg?sqp=-oaymwEWCMQBEG5IWvKriqkDCQgBFQAAiEIYAQ==&rs=AOn4CLAWZwsWBeYp_VTp6-0NJxyAVNyO5A
    43b 200 13ms    https://www.youtube.com/yts/img/pixel-vfl3z5WfW.gif
    ・・・

whitelistRequests

正規表現に一致するリストに無いHTTPリクエストには固定のステータスコードを返す。配列指定可。

var c = test.openHttpClient();
var web = require('webbot');

test.beginTransaction();
test.beginStep("Check Website");

c.whitelistRequests([
  ".*www.youtube.com.*",
], 204);

var req = c.newGet("https://www.youtube.com/");
var res = web.execute(req);
//req.execute();

test.endStep();
test.endTransaction();
  • whitelistRequests での返却値
    517492b 200 1374ms  https://www.youtube.com/
    5878b   200 24ms    https://www.youtube.com/yts/jsbin/scheduler-vfl3G47Co/scheduler.js
    ・・・
    0b  204 0ms https://i.ytimg.com/vi/FYVn2ne7hJc/hqdefault.jpg?sqp=-oaymwEWCMQBEG5IWvKriqkDCQgBFQAAiEIYAQ==&rs=AOn4CLD35p4cHO4FZrVP8dzLpSQEn2PwoQ
    ・・・
    0b  204 0ms https://i.ytimg.com/vi/x0zgWgzy8Ow/hqdefault.jpg?sqp=-oaymwEWCMQBEG5IWvKriqkDCQgBFQAAiEIYAQ==&rs=AOn4CLAhkXbjeKJ0s8iXTN4upWXXa5CGYA
    43b 200 14ms    https://www.youtube.com/yts/img/pixel-vfl3z5WfW.gif
    ・・・

その他

csv からの値取得

行番号指定かランダム読み込み。

test.getCSV(name) の name 指定はリテラルである必要がある。変数では展開されずエラーとなる。
(LocalValidator では成功するが、NeuStar上だとエラー)

Error: script-exception: "Problem fetching CSV file"
  • 例 data.csv
Name,Age,MailAddress
Yamada,22,t-yamada@example.com
Aikawa,28,s-aikawa@example.com
Endo,21,endo@@example.com

行番号読み込み

// readCsv
function readCsv(csv){
  var array = [];
  for (var i = 0; i < csv.size(); i++) {
    var row = csv.get(i);
    array[i] = row;

    var name = row.get("Name");
    var age = row.get("Age");
    var mail = row.get("MailAddress");
    test.log('Name: ' + name + ' / Age: ' + age + ' / Mail: ' + mail);
  }
  return array;
}
var csv  = test.getCSV("data.csv");
var data = readCsv(csv);
test.log(data);

ランダム読み込み

var csv  = test.getCSV("data.csv");
var csv_row = csv.random();
var name = csv_row.get("Name");
var age = csv_row.get("Age");
var mail = csv_row.get("MailAddress");
test.log('Name: ' + name + ' / Age: ' + age + ' / Mail: ');

一時停止

Step 間に一時停止時間を設定。ミリ秒単位。30秒制限有り。(Local Validator だと制限はない模様)

var c = test.openHttpClient();
var web = require('webbot');

test.beginTransaction();
test.beginStep("Check Website1");
var req = c.newGet("https://www.youtube.com/");
req.execute();
test.endStep();

test.pause(30000);

test.beginStep("Check Website2");
var req = c.newGet("https://google.com/");
req.execute();
test.endStep();

test.endTransaction();

assert (異常終了)

異常終了させたい場合に使用。

test.assertTrue(false, "Assert!!!");

  • 出力例
WARN mm/dd hh:mm:ss b.n.w.a.a.Webmetric~ - ERROR: assertTrue() failed! Assert!!! at line 8
  • 出力例(validation.txt)
***** ERROR DETECTED *****
Error Message: assertTrue() failed! Assert!!!
Error Line Num: 8
***** ERROR DETECTED *****

APIリファレンスを見ると他にも似たような関数( fail、 assertFalse )があるが、いずれも見つからないエラーとなる。処理は停止するが美しくはない。

  • 出力例(validation.txt)
***** ERROR DETECTED *****
Error Message: TypeError: Cannot find function fail in object biz.neustar.wpm.api.Test.
Error Line Num: 9
***** ERROR DETECTED *****

Validate 時間制限

NeuStar 上での Validate は Script 全体の処理時間が 60秒を超過するとエラーとなる。

Script took too long and reached timeout limit.

制限緩和は行えないため、Local Validator で Validate を行った後、Bypass Validation を有効にしてスキップさせる。

htnosm
うろ覚えを無くしていこうともがき苦しむ人の備忘録 - nullはナルじゃなくヌル - editorはEmacsじゃなくvi - gitはジットじゃなくギット - 饂飩より蕎麦 - 茸より筍 - 四季は秋
http://htnosm.hatenablog.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away