- PVSM.RU - https://www.pvsm.ru -
Добрый день, уважаемые читатели ! Данная статья рассчитана на новичков, которые только открывают мир JS, коим являюсь и я. В процессе изучения и проектирования сервера на Node.js разработчик постоянно сталкивается с необходимостью перезагрузки приложения. А в случае, если над проектом работает несколько человек, получаем довольную сложную задачу.
Задача — поднять сервер и обрабатывать несколько url, например http://127.0.0.1/habr [1] и http://127.0.0.1/habrahabr [2]. Сервер должен обрабатывать исключения, а также проект рассчитан на высокую нагрузку.
Цель статьи – разобраться, как создать высоконагруженное приложение, удобное для командной работы и понятное для новичков.
Первое что необходимо сделать, это поднять сервер на Node.js [3]
var http = require('http');
var file = new static.Server('.');
http.createServer(function(req, res) {
file.serve(req, res);
}).listen(80);
Проблема в том, что сервер работает только на одном процессе системы. Немного переработаем код, добавив кластеризацию, для этого используем стандартный модуль cluster [4]:
const cluster = require('cluster');
const http = require('http');
const domain = require('domain');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', function(worker, code, signal){
console.log('worker ' + worker.process.pid +' died');
cluster.fork();
});
cluster.on('online', function(worker) {
console.log('Worker ' + worker.process.pid + ' is online');
});
} else {
http.createServer(function(req, res){
// Создаем домен
var d = domain.create();
// Вешаем обработчик ошибки, который вернет 500й статус и текст проблемы
d.on('error', function(err) {
res.statusCode = 500;
res.setHeader('content-type', 'text/plain');
res.end('Ошибка!n'+ err.stack);
});
// Добавляем наши переменные, которые тоже могут сгенерировать ошибки самостоятельно
d.add(req);
d.add(res);
// Запускаем потенциально опасный код внутри домена
d.run(function () {
var route_json = require('./application/route.json');
if( route_json[req.url] !== undefined){//Пользователь вручную задал контроллер
console.log(route_json[req.url].controller);
}else{
url = urlapi.parse(decodeURI(req.url), true);//парсим url
url_arr = url.pathname.slice(1).split('/');//Преобразуем url в массив
}
res.end('hello world');
});
}).listen(3031).on('connection', function(socket) {
socket.setNoDelay(); // Отключаем алгоритм Нагла.
});
}
С основным кодом сервера мы разобрались, теперь у нас есть сервер с асинхронным обработчиком исключений, кластеризацией и обработкой url. Так как мы используем парадигму MVC, то за эталон возьмем codeigniter. Структура файлов выглядит следующим образом:
Описание структуры:
Требуется обработка кода контроллера. Для решения данной задачи, существует несколько методов:
Из документации видно, что vm выполняется в контексте, можно запустить в новом контексте, либо в текущем. Наиболее правильным вариантом решения будет выполнять код в новом контексте.
Полный код примера:
const cluster = require('cluster');
const http = require('http');
const domain = require('domain');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', function(worker, code, signal){
console.log('worker ' + worker.process.pid +' died');
cluster.fork();
});
cluster.on('online', function(worker) {
console.log('Worker ' + worker.process.pid + ' is online');
});
} else {
http.createServer(function(req, res){
// Создаем домен
var d = domain.create();
// Вешаем обработчик ошибки, который вернет 500й статус и текст проблемы
d.on('error', function(err) {
res.statusCode = 500;
res.setHeader('content-type', 'text/plain');
res.end('Ошибка!n'+ err.stack);
});
// Добавляем наши переменные, которые тоже могут сгенерировать ошибки самостоятельно
d.add(req);
d.add(res);
// Запускаем потенциально опасный код внутри домена
d.run(function () {
var route_json = require('./application/route.json');
var fs = require('fs');//библиотека для работы с файлами
if( route_json[req.url] !== undefined){//Пользователь вручную задал контроллер
var path = './application/controller/'+route_json[req.url].controller+'.js';
}else{
var urlapi = require('url');//Подключаем библиотеку для парсинга url
var url = urlapi.parse(decodeURI(req.url), true);//парсим url
var url_arr = url.pathname.slice(1).split('/');//Преобразуем url в массив
var path = './application/controller/'+url_arr[0]+'.js';
}
//Читаем код контроллера из папки
fs.readFile(path, 'utf8',
function(err, code) {
var vm = require('vm');
var timestart = parseInt(new Date().getTime());
var pid = cluster.worker.process.pid;
var context = {
// -- подключаемые объекты к контексту
pid:pid,
res:res,
req:req,
timestart:timestart,
require: require,
console: console
};
var vmContext = vm.createContext(context);
var script = vm.Script(code);
script.runInNewContext(vmContext);
});
});
}).listen(3031).on('connection', function(socket) {
socket.setNoDelay(); // Отключаем алгоритм Нагла.
});
}
Пример кода контроллера:
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'origin, content-type, accept');
res.setHeader("Cache-Control", "no-cache, must-revalidate");
res.writeHead(200, {"Content-Type": "text/plain"});
res.write('CONTROLLER RUN');
res.end();
Таким образом, у нас есть каркас приложения, который загружает и выполняет код без перезагрузки основного приложения (исполняет код в песочнице).
Данное решение отлично подойдет для командной разработки больших приложений. В данной статье мы рассмотрели cluster и vm, домены в Node.js.
Ссылки:
Автор: pan-alexey
Источник [11]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/204999
Ссылки в тексте:
[1] http://127.0.0.1/habr: http://127.0.0.1/habr
[2] http://127.0.0.1/habrahabr: http://127.0.0.1/habrahabr
[3] сервер на Node.js: https://learn.javascript.ru/ajax-nodejs
[4] cluster: https://nodejs.org/api/cluster.html
[5] ru.wikipedia.org/wiki/Model-View-Controller: https://ru.wikipedia.org/wiki/Model-View-Controller
[6] code-igniter.ru/user_guide/libraries/uri.html: http://code-igniter.ru/user_guide/libraries/uri.html
[7] ru.wikipedia.org/wiki/JSON: https://ru.wikipedia.org/wiki/JSON
[8] nodejs.org/api/domain.html: https://nodejs.org/api/domain.html
[9] nodejs.org/api/vm.html: https://nodejs.org/api/vm.html
[10] https://github.com/pan-alexey/nodeigniter: https://github.com/pan-alexey/nodeigniter
[11] Источник: https://habrahabr.ru/post/314186/?utm_source=habrahabr&utm_medium=rss&utm_campaign=sandbox
Нажмите здесь для печати.