Use dynamic pool of map instances to reduce waiting times (issue #3)

This commit is contained in:
Petr Sloup 2016-03-03 19:41:29 +01:00
parent 4f9b8c1a1b
commit dc096012ef
2 changed files with 76 additions and 78 deletions

View file

@ -12,11 +12,11 @@
}, },
"dependencies": { "dependencies": {
"async": "1.5.2", "async": "1.5.2",
"async-lock": "0.3.8", "advanced-pool": "0.3.1",
"clone": "1.0.2", "clone": "1.0.2",
"cors": "2.7.1", "cors": "2.7.1",
"express": "4.13.4", "express": "4.13.4",
"mapbox-gl-native": "3.0.2-earcut", "mapbox-gl-native": "3.0.2",
"mbtiles": "0.8.2", "mbtiles": "0.8.2",
"morgan": "1.7.0", "morgan": "1.7.0",
"nomnom": "1.8.1", "nomnom": "1.8.1",

View file

@ -1,7 +1,7 @@
'use strict'; 'use strict';
var async = require('async'), var async = require('async'),
asyncLock = require('async-lock'), advancedPool = require('advanced-pool'),
crypto = require('crypto'), crypto = require('crypto'),
fs = require('fs'), fs = require('fs'),
path = require('path'), path = require('path'),
@ -32,8 +32,6 @@ mbgl.on('message', function(e) {
}); });
module.exports = function(maps, options, prefix) { module.exports = function(maps, options, prefix) {
var lock = new asyncLock();
var app = express().disable('x-powered-by'), var app = express().disable('x-powered-by'),
domains = options.domains, domains = options.domains,
tilePath = '/{z}/{x}/{y}.{format}'; tilePath = '/{z}/{x}/{y}.{format}';
@ -46,9 +44,11 @@ module.exports = function(maps, options, prefix) {
sources: {}, sources: {},
tileJSON: {} tileJSON: {}
}; };
if (!maps[prefix]) {
var createRenderer = function(ratio) { var styleJSON;
return new mbgl.Map({ var createPool = function(ratio, min, max) {
var createRenderer = function(ratio, createCallback) {
var renderer = new mbgl.Map({
ratio: ratio, ratio: ratio,
request: function(req, callback) { request: function(req, callback) {
var protocol = req.url.split(':')[0]; var protocol = req.url.split(':')[0];
@ -115,60 +115,66 @@ module.exports = function(maps, options, prefix) {
} }
} }
}); });
renderer.load(styleJSON);
createCallback(null, renderer);
}; };
map.renderers[1] = createRenderer(1); return new advancedPool.Pool({
map.renderers[2] = createRenderer(2); min: min,
map.renderers[3] = createRenderer(3); max: max,
create: createRenderer.bind(null, ratio),
var styleJSON = require(path.join(rootPath, styleUrl)); destroy: function(renderer) {
renderer.release();
map.tileJSON = {
'tilejson': '2.0.0',
'name': styleJSON.name,
'basename': prefix.substr(1),
'minzoom': 0,
'maxzoom': 20,
'bounds': [-180, -85.0511, 180, 85.0511],
'format': 'png',
'type': 'baselayer'
};
Object.assign(map.tileJSON, options.options || {});
var queue = [];
Object.keys(styleJSON.sources).forEach(function(name) {
var source = styleJSON.sources[name];
var url = source.url;
if (url.lastIndexOf('mbtiles:', 0) === 0) {
// found mbtiles source, replace with info from local file
delete source.url;
queue.push(function(callback) {
var mbtilesUrl = url.substring('mbtiles://'.length);
map.sources[name] = new mbtiles(path.join(rootPath, mbtilesUrl), function(err) {
map.sources[name].getInfo(function(err, info) {
Object.assign(source, info);
source.basename = name;
source.tiles = [
// meta url which will be detected when requested
'mbtiles://' + name + tilePath.replace('{format}', 'pbf')
];
callback(null);
});
});
});
} }
}); });
};
async.parallel(queue, function(err, results) { styleJSON = require(path.join(rootPath, styleUrl));
map.renderers.forEach(function(renderer) {
renderer.load(styleJSON); map.tileJSON = {
'tilejson': '2.0.0',
'name': styleJSON.name,
'basename': prefix.substr(1),
'minzoom': 0,
'maxzoom': 20,
'bounds': [-180, -85.0511, 180, 85.0511],
'format': 'png',
'type': 'baselayer'
};
Object.assign(map.tileJSON, options.options || {});
var queue = [];
Object.keys(styleJSON.sources).forEach(function(name) {
var source = styleJSON.sources[name];
var url = source.url;
if (url.lastIndexOf('mbtiles:', 0) === 0) {
// found mbtiles source, replace with info from local file
delete source.url;
queue.push(function(callback) {
var mbtilesUrl = url.substring('mbtiles://'.length);
map.sources[name] = new mbtiles(path.join(rootPath, mbtilesUrl), function(err) {
map.sources[name].getInfo(function(err, info) {
Object.assign(source, info);
source.basename = name;
source.tiles = [
// meta url which will be detected when requested
'mbtiles://' + name + tilePath.replace('{format}', 'pbf')
];
callback(null);
});
});
}); });
}); }
});
maps[prefix] = map; async.parallel(queue, function(err, results) {
} else { // TODO: make pool sizes configurable
map = maps[prefix]; map.renderers[1] = createPool(1, 2, 8);
} map.renderers[2] = createPool(2, 2, 4);
map.renderers[3] = createPool(3, 2, 4);
});
maps[prefix] = map;
var tilePattern = tilePath var tilePattern = tilePath
.replace(/\.(?!.*\.)/, ':scale(' + SCALE_PATTERN + ')?.') .replace(/\.(?!.*\.)/, ':scale(' + SCALE_PATTERN + ')?.')
@ -186,29 +192,21 @@ module.exports = function(maps, options, prefix) {
return res.status(404).send('Invalid format'); return res.status(404).send('Invalid format');
} }
var mbglZ = Math.max(0, z - 1); var pool = map.renderers[scale];
var renderer = map.renderers[scale]; pool.acquire(function(err, renderer) {
var params = { var mbglZ = Math.max(0, z - 1);
/* var params = {
debug: { zoom: mbglZ,
tileBorders: true, center: [lon, lat],
parseStatus: true, width: width,
timestamps: true, height: height
collision: true };
}, if (z == 0) {
*/ params.width *= 2;
zoom: mbglZ, params.height *= 2;
center: [lon, lat], }
width: width,
height: height
};
if (z == 0) {
params.width *= 2;
params.height *= 2;
}
lock.acquire(renderer, function(done) {
renderer.render(params, function(err, data) { renderer.render(params, function(err, data) {
done(); pool.release(renderer);
if (err) console.log(err); if (err) console.log(err);
var image = sharp(data, { var image = sharp(data, {