@luminati-io/luminati-proxy
Version:
A configurable local proxy for brightdata.com
130 lines (122 loc) • 4.19 kB
JavaScript
// LICENSE_CODE ZON ISC
'use strict'; /*jslint node:true, esnext:true*/
const cluster = require('cluster');
const _ = require('lodash4');
const etask = require('../util/etask.js');
const cluster_ipc = require('../util/cluster_ipc.js');
const logger = require('./logger.js').child({category: 'MNGR: stat'});
class Stat {
constructor(mgr){
this._data = {domain: {}, port: {}};
if (cluster.isWorker)
{
cluster_ipc.worker_on('get_stats',
this._send_to_master.bind(this));
return;
}
if (!mgr)
return;
this.mgr = mgr;
this._default_interval = 10000;
this.flush_stats = this.flush_stats.bind(this);
this._agg_stats = this._agg_stats.bind(this);
this.flush_stats_interval = etask.interval(this._default_interval,
this.flush_stats);
this.mgr.lpm_f.on('server_conf', server_conf=>{
const interval = (this.mgr.argv.zagent && server_conf.cloud ||
server_conf.client).send_stat_throttle||this._default_interval;
this.flush_stats_interval.return();
this.flush_stats_interval = etask.interval(interval,
this.flush_stats);
});
}
process(data={}){
const {success, lum_traffic, port} = data;
const bw = (data.in_bw||0)+(data.out_bw||0);
const domain = (data.hostname||'').replace(/\./g, '_');
if (!valid_domain_name(domain))
return logger.warn('invalid domain name: %s', data.hostname);
this.inc_partial(domain, port, bw, lum_traffic, success);
}
inc_partial(domain, port, bw, lum, success){
this.inc_key('domain', domain, bw, lum, success);
this.inc_key('port', port, bw, lum, success);
}
inc_key(key, val, bw, lum, success){
this._data[key][val] = this._data[key][val]||
{bw: 0, reqs: 0, lum_bw: 0, lum_reqs: 0, success: 0};
this._data[key][val].bw += bw;
this._data[key][val].reqs += 1;
if (lum)
{
this._data[key][val].lum_bw += bw;
this._data[key][val].lum_reqs += 1;
}
if (success)
this._data[key][val].success += 1;
}
flush_stats(){
const enable = _.get(this.mgr, 'server_conf.client.send_stat');
if (!enable)
return;
if (enable%1)
{
if (Math.random()>enable)
return;
}
const _this = this;
return etask(function*flush_stats(){
try {
let data = _this._agg_stats(yield etask.all({allow_fail: true},
cluster_ipc.call_all_workers('get_stats')));
if (!data)
return;
yield _this.mgr.lpm_f.send_stat(data);
} catch(e){
logger.warn('failed updating stats %s', e.message);
}
});
}
_send_to_master(){
const data = this._data;
this._data = {domain: {}, port: {}};
return data;
}
_agg_stats(arr){
let res;
for (let data of arr)
{
if (!data || !data.domain || !Object.keys(data.domain).length)
continue;
res = merge(res||{}, data);
}
return res;
}
}
const merge = (stat_a, stat_b)=>{
return {
domain: merge_bw_obj(stat_a.domain, stat_b.domain),
port: merge_bw_obj(stat_a.port, stat_b.port),
};
};
const empty_bw_obj = ()=>({bw: 0, reqs: 0, lum_bw: 0, lum_reqs: 0,
success: 0});
const add_bw_obj = (obj_a, obj_b)=>{
obj_a = obj_a||empty_bw_obj();
obj_b = obj_b||empty_bw_obj();
return {
bw: obj_a.bw+obj_b.bw,
reqs: obj_a.reqs+obj_b.reqs,
lum_bw: obj_a.lum_bw+obj_b.lum_bw,
lum_reqs: obj_a.lum_reqs+obj_b.lum_reqs,
success: obj_a.success+obj_b.success,
};
};
const merge_bw_obj = (obj_a={}, obj_b={})=>{
for (let key of Object.keys(obj_b))
obj_a[key] = add_bw_obj(obj_a[key], obj_b[key]);
return obj_a;
};
const valid_domain_name = domain=>!domain.startsWith('$');
module.exports = Stat;