- PVSM.RU - https://www.pvsm.ru -
Не так давно проект, на котором я работаю в данный момент, начал использовать модульную систему ES2015. Я не буду заострять внимание на этой технологии JavaScript, т.к статья совсем не об этом, а о том как технология сподвигла меня к одной мысли.
Как многие знают, ES2015 Modules представляют собой импортирование/экспортирование скриптов крайне схожее по синтаксису с python
и многими другими языками программирования. Пример:
// Helper.js
export function includes(array, variable) {
return array.indexOf(variable) !== -1;
}
// main.js
import {includes} from 'Helper';
assets(includes([1,2,3], 2), true);
Все, кто интересовался модулями JavaScript знают, что импортирование и экспортирование возможно только на верхнем уровне модуля (файла с кодом).
Следующий грубый пример кода вызовет ошибки:
// sendEmail.js
export default function sendEmails(emails_list) {
import sender from 'sender';
export sender;
// сделать что-то
}
Exception: SyntaxError: import/export declarations may only appear at top level of a module
В отличие от ES2015 Modules — в модульной системе node.js
импортирование и экспортирование возможны на любом уровне вложенности.
Аналогичный код на node.js
не вызовет ошибку:
// sendEmail.js
module.exports = function sendEmails(emails_list) {
const sender = require('sender');
exports.sender = sender;
// сделать что-то
}
Преимущество такого способа в том, что модули необходимые в обработчике явно импортированы внутри и не засоряют пространство имен модуля (особенно актуально, если импортируемый модуль нужен только в одном обработчике). Так же появляется возможность отложенного экспортирования данных модуля.
Основные минусы:
lodash/object/defaults
и вы решили обновиться [1] до 4.x версии, где подключать нужно lodash/defaults
).В большинстве задач для которых используется node.js
— front-end или основной веб-сервер, и высокая нагрузка на node.js
частое явление. Пропуская способность вашего сервера должны быть максимально возможная.
Для измерения пропускной способности веб-сервера используется великолепная утилита от Apache — ab [2]. Если вы еще с ней не знакомы, то настоятельно рекомендую это сделать.
Код веб-сервера одинаков за исключением обработчиков.
Тест запускался на node.js 6.0
с использованием модуля ifnode
[3], сделанного на базе express
[4]
Код:
const app = require('ifnode')();
const RequireTestingController = app.Controller({
root: '/',
map: {
'GET /not_imported': 'notImportedAction'
}
});
RequireTestingController.notImportedAction = function(request, response, next) {
const data = {
message: 'test internal and external require'
};
const _defaults = require('lodash/object/defaults');
const _assign = require('lodash/object/assign');
const _clone = require('lodash/lang/clone');
response.ok({
_defaults: _defaults(data, {
lodash: 'defaults'
}),
_assign: _assign(data, {
lodash: 'assign'
}),
_clone: _clone(data)
});
};
Результат:
$ ab -n 15000 -c 30 -q "http://localhost:8080/not_imported"
Server Hostname: localhost
Server Port: 8080
Document Path: /not_imported
Document Length: 233 bytes
Concurrency Level: 30
Time taken for tests: 4.006 seconds
Complete requests: 15000
Failed requests: 0
Total transferred: 6195000 bytes
HTML transferred: 3495000 bytes
Requests per second: 3744.32 [#/sec] (mean)
Time per request: 8.012 [ms] (mean)
Time per request: 0.267 [ms] (mean, across all concurrent requests)
Transfer rate: 1510.16 [Kbytes/sec] received
Percentage of the requests served within a certain time (ms)
50% 6
66% 7
75% 8
80% 8
90% 10
95% 15
98% 17
99% 20
100% 289 (longest request)
Код:
const app = require('ifnode')();
const _defaults = require('lodash/object/defaults');
const _assign = require('lodash/object/assign');
const _clone = require('lodash/lang/clone');
const RequireTestingController = app.Controller({
root: '/',
map: {
'GET /already_imported': 'alreadyImportedAction'
}
});
RequireTestingController.alreadyImportedAction = function(request, response, next) {
const data = {
message: 'test internal and external require'
};
response.ok({
_defaults: _defaults(data, {
lodash: 'defaults'
}),
_assign: _assign(data, {
lodash: 'assign'
}),
_clone: _clone(data)
});
};
Результат:
$ ab -n 15000 -c 30 -q "http://localhost:8080/already_imported"
Server Hostname: localhost
Server Port: 8080
Document Path: /already_imported
Document Length: 233 bytes
Concurrency Level: 30
Time taken for tests: 3.241 seconds
Complete requests: 15000
Failed requests: 0
Total transferred: 6195000 bytes
HTML transferred: 3495000 bytes
Requests per second: 4628.64 [#/sec] (mean)
Time per request: 6.481 [ms] (mean)
Time per request: 0.216 [ms] (mean, across all concurrent requests)
Transfer rate: 1866.83 [Kbytes/sec] received
Percentage of the requests served within a certain time (ms)
50% 5
66% 6
75% 6
80% 7
90% 8
95% 14
98% 17
99% 20
100% 38 (longest request)
Импортирование модулей вначале файла уменьшило время одного запроса на ~23%(!) (в сравнение с импортированием непосредственно в обработчик), что весьма существенно.
Такая большая разница в результатах кроется в работе функции require
. Перед импортированием, require
обращается к алгоритму поиска абсолютного пути к запрашиваемому компоненту (алгоритм описан [5] в документации node.js). Когда путь был найден, то require
проверяет был ли закеширован модуль, и если нет — не делает ничего сверхестественного, кроме вызова обычного fs.readFileSync
[6] для .js
и .json
форматов, и недокументированного process.dlopen
[7] для загрузки C++ модулей.
Note: пробовал "прогревать" кеш для случая с непосредственным импортированием модулей в обработчик (перед запуском утилиты ab, модули были уже закешированы) — производительность улучшалась на 1-2%.
Если вы используете node.js, как сервер (нет разницы какой — TCP/UDP или HTTP(S)), то:
Автор: ilfroloff
Источник [9]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/134342
Ссылки в тексте:
[1] обновиться: https://github.com/lodash/lodash/wiki/Changelog#v400
[2] ab: https://httpd.apache.org/docs/2.4/programs/ab.html
[3] ifnode
: https://www.npmjs.org/package/ifnode
[4] express
: https://www.npmjs.com/package/express
[5] описан: https://nodejs.org/api/modules.html#modules_all_together
[6] вызова обычного fs.readFileSync
: https://github.com/nodejs/node/blob/master/lib/module.js#L548
[7] недокументированного process.dlopen
: https://github.com/nodejs/node/blob/624734e640717a826ab1a18845c083a638dc5ce6/src/node.cc#L2250
[8] главных анти-паттернов: http://stackoverflow.com/a/6081565
[9] Источник: https://habrahabr.ru/post/303352/?utm_source=habrahabr&utm_medium=rss&utm_campaign=sandbox
Нажмите здесь для печати.