Из документации 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, чтобы завершить мастер.