Node.js
PhantomJS
lambda

AWS Lambda でパフォーマンス監視

More than 1 year has passed since last update.

サイトのパフォーマンスを監視するために、定期的にスクレイピングをしようと思い、

サーバーを立てるのも面倒なので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用関数を作成します。

以下、抜粋なので、これだけでは動きませんが雰囲気はわかると思います。


index.js

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内で実行された情報も取得します。


script.js

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で計測するかどうかを決定しています。


performanmce.js

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モジュールは以下


package.json

 "devDependencies": {

"phantomjs": "2.1.3",
"phantom-url": "1.0.0"
}


Uploadする

上をまとめてzipにしてアップロードします。

ただ、30MB近くになるので、ブラウザからアップロードしようとすると偶に失敗します。

ただ、境界に近かったのか、

npmをseleniumからphantomjsに変えたところ失敗する確率が大きく減りました。


New Relicを使う

簡単だと思って気軽にやってみたら、意外と面倒だったのでNew Relic のSynthesisを使うと良いかもしれません。

簡単に試したところ、seleniumも、performance APIも動いたので、手軽だと思います