サイトのパフォーマンスを監視するために、定期的にスクレイピングをしようと思い、
サーバーを立てるのも面倒なのでAWSのLambdaでしのごうとしたら意外と大変だったのでメモ
要約
- Nodejsが古い(v0.10.36)
- seleniumは使わないで、child_processから直接phantomjs(最新: v2.1.1)起動
- performance API には頼らない
- 金があるならNewRelic
Nodejsが古い
最初はSeleniumでやればすぐにできるだろうと考えていたのですが、
Lambdaで使用できるNode.jsは0.10.36と古く、Seleniumではサポート対象外です。
selenium-webdriverのバージョンを2.44.0ぐらいにすると動かせますが、phantomjsとの相性が悪いようで、
seleniumからphantomjsを実行しようとすると、サーバーとつながらない旨のエラーをはいてきます。
おそらくphantomjsのバージョンがあっていないのだと思いますが、どのバージョンが正しいか見つけるのが面倒だったので、
直接phantomjsを起動しました。
Phantomjsでperformance API
phantomjsでも、performance APIはサポートされていますが、
performance.timing
がないため、情報が片手落ちです。
そのため、onResourceRequested
を使用して、開始時のリクエストなど、必要な情報を取得します。
また、mark()
もないため、 new Date().getTime()
で代用します。
Lambdaにアップロードする
これをまとめると、
Phantomjsを起動するLmbda用関数を作成します。
以下、抜粋なので、これだけでは動きませんが雰囲気はわかると思います。
exports.handler = function(event, context) {
var childProcess = require('child_process');
function callPhantom(callback) {
var childArgs = ["script.js"]
process.env['LD_WARN'] = 'true';
console.log("calling...");
childProcess.exec("./phantomjs ./script.js", function(err, stdout) {
console.log("stdout", stdout);
console.log("err", err);
context.done(err, stdout)
});
}
console.log("start");
callPhantom(function() {
console.log("finish");
context.done();
});
};
phantomjsに渡すファイルでは、
開始時間などperformance.timing
でとれるような情報を取得しつつ
phantomjs内で実行された情報も取得します。
phantom.onError = function(msg, trace) {
console.log("error", msg);
var result = {"msg": msg};
console.error(JSON.stringify(result));
phantom.exit(1);
};
page.onResourceRequested = function(requestData, networkRequest) {
//stop GA access not to affect user
if(/.*google-analytics.*/.test(requestData.url)) {
networkRequest.abort();
return;
}
};
resTime = {};
reqTime = {};
realAccessUrl= "http://www.example.com";
page.onResourceReceived = function(response) {
if (response.stage === "start") {
//ほしい情報もろもろを取得する
if(response.url == realAccessUrl) {
resTime['page_request_start'] = response.time.getTime();
reqTime[response.id] = "page_response_end";
}
} else if(response.stage == "end") {
if (response.id in reqTime) {
resTime[reqTime[response.id]] = response.time.getTime();
}
}
};
function onPageReady() {
var result = page.evaluate(function() {
return JSON.stringify(PerformanceMonitor.getResult());
});
var times = JSON.parse(result);
times["page_request_start"] = resTime["page_request_start"];
var elapseTimes = {};
var keys = Object.keys(times);
var startTime = resTime['page_request_start'];
for(var i = 0; i < keys.length; i++) {
var key = keys[i];
elapseTimes[key] = times[key] - startTime;
}
var phantomResult = {
"elapseTimes": elapseTimes
};
console.log(JSON.stringify(phantomResult));
phantom.exit();
}
パフォーマンス計測用に、サイトに埋め込むjsファイルです。
performance.mark()がないので、Date.getTime()を使用し、
Cookieで計測するかどうかを決定しています。
document.PerformanceMonitor = new function() {
var performanceResults = {};
this.mark = function(name) {
if(isMonitorable == false) return;
var markName = markPrefix + name;
//performance.mark(markName);
if (performanceResults[markName] == undefined || performanceResults[markName] == null) {
performanceResults[markName] = new Date().getTime();
}
};
this.getResult = function() {
if(isMonitorable == false) return {};
return performanceResults;
};
var isMonitorable = false;
initIsMonitorable();
function initIsMonitorable() {
isMonitorable = /monitor_enable=true[;\s]?/.test(document.cookie);
}
}()
npmモジュールは以下
"devDependencies": {
"phantomjs": "2.1.3",
"phantom-url": "1.0.0"
}
Uploadする
上をまとめてzipにしてアップロードします。
ただ、30MB近くになるので、ブラウザからアップロードしようとすると偶に失敗します。
ただ、境界に近かったのか、
npmをseleniumからphantomjsに変えたところ失敗する確率が大きく減りました。
New Relicを使う
簡単だと思って気軽にやってみたら、意外と面倒だったのでNew Relic のSynthesisを使うと良いかもしれません。
簡単に試したところ、seleniumも、performance APIも動いたので、手軽だと思います