Из документации Node.js : один экземпляр Node выполняется в одном потоке. Чтобы воспользоваться преимуществами многоядерных систем, пользователь иногда захочет запустить кластер процессов Node для обработки нагрузки. Сегодня я покажу вам, как использовать Cluster for Node.js для предоставления простой статической ленты ATOM.
Примечание. В настоящее время кластер (на момент написания этой статьи) помечен как : Стабильность: 1 Экспериментальный — радикальные изменения в будущих версиях . Так что имейте в виду, что изменения в приведенном ниже коде, вероятно, потребуются в будущем. Для ознакомления с тем, куда движется Кластер, вы уже можете просмотреть (будущие) документы, расположенные здесь .
Для начала я позаимствовал некоторый код из документации по кластеру Node.js , этого блога и репозитория GitHub . Затем я изменил и в некоторых случаях исправил код для работы, как ожидалось.
Сначала я изменил ATOM.js отсюда . Имейте в виду, что вы можете добавить еще много всего, чтобы сделать приведенный ниже файл более надежным и поддерживать дополнительные функции ATOM. Кроме того, я изначально использовал NPM для его установки, но столкнулся с несколькими проблемами, поэтому я просто скопировал фактический файл в папку своего проекта.
/*
Borrowed from: https://github.com/dylang/node-atom/blob/master/lib/atom.js
Author: https://github.com/dylang
*/
var XML = require('xml');
function ATOM (options, items) {
options = options || {};
this.title = options.title || 'Untitled ATOM Feed';
this.description = options.description || '';
this.feed_url = options.feed_url;
this.site_url = options.site_url;
this.image_url = options.image_url;
this.author = options.author;
this.items = items || [];
this.item = function (options) {
options = options || {};
var item = {
title: options.title || 'No title',
description: options.description || '',
url: options.url,
guid: options.guid,
categories: options.categories || [],
author: options.author,
date: options.date
};
this.items.push(item);
return this;
};
this.xml = function(indent) {
return '<?xml version="1.0" encoding="UTF-8"?>\n'
+ XML(generateXML(this), indent);
}
}
function ifTruePush(bool, array, data) {
if (bool) {
array.push(data);
}
}
function generateXML (data){
var feed = [
{ _attr: {
'xmlns': 'http://www.w3.org/2005/Atom',
'xml:lang': 'en-US'
} },
{ id: 'urn:uuid:90c76b31-d399-21d2-b93C-0004449e0ca7' },
{ link: { _attr: { type: 'text/html', rel: 'alternate', href: data.site_url } } },
{ link: { _attr: { type: 'application/atom+xml', rel: 'self', href: data.feed_url } } },
{ title: data.title },
{ updated: new Date().toISOString() }
];
data.items.forEach(function(item) {
var entry = [
{ id: 'urn:uuid:77c76b31-d549-21d2-b93C-8829942e4b5f' }
];
ifTruePush(item.date, entry, { published: new Date(item.date).toISOString() });
ifTruePush(item.updated, entry, { updated: new Date(item.updated).toISOString() });
ifTruePush(item.link, entry, { link: { _attr: { type: 'text/html', rel: 'alternate', href: item.url } } });
ifTruePush(item.title, entry, { title: item.title });
ifTruePush(item.description, entry, { content: { _attr: { type: 'xhtml', 'xml:lang': 'en' }, _cdata: item.description } });
//ifTruePush(item.author || data.author, entry, { 'dc:creator': { _cdata: item.author || data.author } });
feed.push({ entry: entry });
});
return { feed: feed };
}
module.exports = ATOM;
Опять же, имейте в виду, что это только для примера, который я строю, очевидно, вы не хотели бы иметь жестко закодированные идентификаторы и т. Д.
Убедитесь в том , чтобы сохранить файл как выше atom.js .
Следующим шагом является использование NPM для установки пакета XML (локально для папки моего проекта).
$ npm install xml
Создайте файл в папке вашего проекта с именем app.js и добавьте в него следующий код:
var ATOM = require('./atom');
var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;
var feed = new ATOM({
title: 'ATOM Feed',
description: 'This is an ATOM feed',
feed_url: 'http://localhost:8888/',
site_url: 'http://localhost:8888/',
author: 'GiantFlyingSaucer'
});
feed.item({
title: 'ATOM Entry',
description: 'Hello World',
url: 'http://localhost:8888/',
guid: 'urn:uuid:60a76c80-d399-11d2-b93C-0003939e0cf1',
author: 'Guest Author',
date: '2012-05-20T21:50:02Z'
});
var xml = feed.xml();
var workers = [];
var server = require('http').createServer(function (req,res){
if(req.url == '/favicon.ico') return;
res.writeHead(200);
res.end(xml);
console.log('HTTP request answered by Worker (PID): ' + process.pid);
// Kill the process after a second
setInterval(function() {
process.exit(0);
}, 1000);
});
if(cluster.isMaster){
for (var i = 0; i < numCPUs; i++) {
var worker = cluster.fork();
workers.push(worker);
console.log('Starting Worker (PID): ' + worker.pid);
}
cluster.on('death', function (worker){
console.log('Worker (PID) ' + worker.pid + ' has stopped');
for (var i = 0; i < workers.length; i++) {
var tmpWorker = workers[i];
if(worker.pid === tmpWorker.pid) {
workers.splice(i, 1);
console.log('Workers array length is now: ' + workers.length);
}
}
});
}else{
server.listen(8888);
}
Ну, это много кода, так что давайте пройдемся по нему.
Я создаю простой канал ATOM с одной записью:
var feed = new ATOM({
title: 'ATOM Feed',
description: 'This is an ATOM feed',
feed_url: 'http://localhost:8888/',
site_url: 'http://localhost:8888/',
author: 'GiantFlyingSaucer'
});
feed.item({
title: 'ATOM Entry',
description: 'Hello World',
url: 'http://localhost:8888/',
guid: 'urn:uuid:60a76c80-d399-11d2-b93C-0003939e0cf1',
author: 'Guest Author',
date: '2012-05-20T21:50:02Z'
});
Это было бы минимум в лучшем случае, но достаточно для этого примера. Оттуда я создаю массив для хранения всех рабочих, которые будут запущены при вызове cluster.fork () для каждого процессора. Я работаю на Mac Mini с 4 ядрами, поэтому моя машина раскрутит четырех рабочих. Ваш компьютер будет отличаться в зависимости от количества ядер вашего процессора.
var workers = [];
Здесь происходит настоящее волшебство:
if(cluster.isMaster){
for (var i = 0; i < numCPUs; i++) {
var worker = cluster.fork();
workers.push(worker);
console.log('Starting Worker (PID): ' + worker.pid);
}
cluster.on('death', function (worker){
console.log('Worker (PID) ' + worker.pid + ' has stopped');
for (var i = 0; i < workers.length; i++) {
var tmpWorker = workers[i];
if(worker.pid === tmpWorker.pid) {
workers.splice(i, 1);
console.log('Workers array length is now: ' + workers.length);
}
}
});
}else{
server.listen(8888);
}
Проверка выполняется, чтобы увидеть, являемся ли мы мастером, если это так, то вызывается цикл и запускается в зависимости от того, сколько у меня процессорных ядер. Я храню тех новых работников для последующего использования. Событие «смерть» добавляется в код для захвата любых работников, которые внезапно умирают по любой причине. В этом случае я удаляю их из массива рабочих и записываю некоторую информацию. Если это не мастер, тогда мы просто раскручиваем базовый HTTP-сервер и ждем входящих запросов. При использовании Cluster все работники могут использовать один и тот же порт, в данном случае 8888.
Чтобы сделать это лучшим примером, я добавил код для завершения процесса после того, как он отвечает на запрос HTTP. Вы можете удалить эту часть, но я бы порекомендовал сначала посмотреть, как она работает. Кроме того, вы можете наблюдать за событием «смерти» и просто раскручивать замещающего работника, когда он умирает. В моем сценарии я просто позволил ему умереть.
Время запустить пример:
$ node app.js
Ожидайте результатов, когда я периодически нажимаю обновить по следующему URL: http: // localhost: 8888 /
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US">
<id>urn:uuid:90c76b31-d399-21d2-b93C-0004449e0ca7</id>
<link type="text/html" rel="alternate" href="http://localhost:8888/"/>
<link type="application/atom+xml" rel="self" href="http://localhost:8888/"/>
<title>ATOM Feed</title>
<updated>2012-04-21T16:16:32.759Z</updated>
<entry>
<id>urn:uuid:77c76b31-d549-21d2-b93C-8829942e4b5f</id>
<published>2012-05-20T21:50:02.000Z</published>
<title>ATOM Entry</title>
<content type="xhtml" xml:lang="en">
<![CDATA[ Hello World ]]>
</content>
</entry>
</feed>
Выход терминала:
Starting Worker (PID): 311 Starting Worker (PID): 312 Starting Worker (PID): 313 Starting Worker (PID): 314 HTTP request answered by Worker (PID): 314 Worker (PID) 314 has stopped Workers array length is now: 3 HTTP request answered by Worker (PID): 313 Worker (PID) 313 has stopped Workers array length is now: 2 HTTP request answered by Worker (PID): 311 Worker (PID) 311 has stopped Workers array length is now: 1 HTTP request answered by Worker (PID): 312 Worker (PID) 312 has stopped Workers array length is now: 0
Просто нажмите CTRL-C, чтобы завершить мастер.