米国の Neustar 社が提供しているサービスの一つで、Webサイトに対する負荷試験が行える。
- 公式
- API Document
- スクリプト概要
以下、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 を有効にしてスキップさせる。