Современные технологии разработки распределенных вычислительных систем. Учебное пособие
.pdf}
return true;
}); // end gatherResults()
function pushWorkItems(workItemsCount)
{
for(var i = 0; i < workItemsCount; ++i)
{
const task = {id: i, count: N}; redisClient.lpush(jobQueueKey, JSON.stringify(task));
}
}
function gatherResults(onResultItem)
{
function getResult(err, res)
{
const result = JSON.parse(res[1]); if(onResultItem(result))
{
redisClient.brpop(resultsQueueKey, 0, getResult);
}
else
{
redisClient.quit();
process.exit(0);
}
}
redisClient.brpop(resultsQueueKey, 0, getResult);
}
function showStats(results, runTime)
{
var pointsCount = 0; var average = 0;
const itemsCount = results.length; for(var i = 0; i < itemsCount; ++i)
{
31
const item = results[i]; pointsCount += item.value.count; average += item.value.estimation;
}
average /= itemsCount; var gigaPoints =
Math.round(pointsCount / runTime / 1e6) / 1e3; console.log(`Monte-Carlo Pi = ${average} (run time: ${runTime} s, ${gigaPoints} Gpts/s)`);
} // end showStats
Конец файла “dsmaster.js”
При запуске оба скрипта принимают аргумент командной строки,
позволяющий идентифицировать очереди, используемые для данной вычислительной сессии (по умолчанию – PI). Скрипт “dsmaster.js”
запускается в одном экземпляре, скрипт – в нескольких экземплярах (на нескольких компьютерах). Скрипт “dsmaster.js” вторым аргументом принимает количество заданий, отправляемых в очередь (по умолчанию – 4).
Скрипты можно запускать в любом порядке (но вычисления начнутся только после запуска мастер-узла).
Пример запуска мастер-узла (сессия MCPI, 16 заданий):
$>node dsmaster MCPI 16
Пример запуска узла-исполнителя для сессии MCPI:
$>node dsnode MCPI
32
4. Сервис PubNub – глобальная программируемая коммуникационная платформа (Programmable Network)
Сервис PubNub (www.pubnub.com, [3]) предоставляет разработчикам распределенных систем все необходимые средства для эффективного обмена сообщениями в сети Интернет в реальном времени. Кроме того, PubNub
реализует так называемую бессерверную вычислительную среду (serverless compute environment).
В основе концепции PubNub лежит хорошо известный архитектурный паттерн “Издатели – Подписчики” (Publishers – Subscribers). В качестве среды передачи информации выступает Интернет. Любой узел системы имеет возможность посылать сообщения в произвольные именованные каналы, а
также принимать сообщения из тех каналов, на которые этот узел подписан.
Инфраструктура PubNub глобально реплицирует (распространяет) сообщения от опубликовавшего их “издателя” ко всем “подписчикам”, заинтересованным в их получении. Управление в этой среде является событийно-ориентированным.
Каналы эквивалентны типу события, а сообщения, появляющиеся в канале – фактам возникновения событий этого типа. При получении сообщения вызывается алгоритмическая функция-обработчик, относящаяся к событиям данного типа.
В бессерверной вычислительной среде обработчики сообщений могут вызываться не только на клиентских узлах, подписанных на определенные каналы, но и непосредственно “в облаке”, на серверах платформы PubNub.
Посредством специального интерфейса прикладная система может зарегистрировать функцию (на языке JavaScript), которая будет автоматически вызываться при возникновении определенных событий. Таким функциям доступен развитый API, они могут использовать REST-запросы к любым веб-
сервисам, хранить временные данные в глобальном KV-хранилище,
публиковать сообщения в любые каналы, порождать новые события, активируя
33
новые обработчики и порождая тем самым цепочку процессов в бессерверной среде.
В системе PubNub функция-обработчик может вызываться фактом появления нового сообщения в определенном канале (так можно централизованно выполнять фильтрацию и преобразование сообщений определенного типа), либо посредством специального механизма fire,
аналогичного механизму публикации сообщения в именованный канал, но без глобальной репликации. Кроме того, доступна конфигурация, в которой вызов зарегистрированной функции осуществляется при подаче REST-запроса на определенный URI (реализация микросервисов).
Рассмотрим схему построения простейшей распределенной вычислительной системы на основе сервиса PubNub. Согласно вышеприведенной классификации (см. п. 2.2), этот вариант будет обозначен как
EO. Следовательно, система идентифицирует не логические процессоры, а
события в облачной среде. К такой системе может быть подключено неопределенное количество узлов-исполнителей, без ведения реестра.
При запуске программа, управляющая узлом-исполнителем, будет генерировать уникальный (случайный) идентификатор id, подписываться
(метод subscribe) на канал “задания для id”, после чего порождать (метод fire)
событие “запрос задания на обработку”, указывая в метаданных события идентификатор прослушиваемого им канала обратной связи. Система будет сконфигурирована таким образом, что в качестве реакции на это событие обработчик в бессерверной среде будет публиковать (метод publish) в
предоставленный канал обратной связи очередное задание с уникальным идентификатором task_id.
После получения соответствующего сообщения и по окончании выполнения задания узел-исполнитель будет порождать (также с помощью fire)
событие “задание выполнено”, в метаданных которого будет указан task_id. В
процедуре реакции на это событие обработчик в бессерверной среде получит
34
вычисленный результат, с возможностью соотнесения его с выданным заданием (посредством task_id).
Для определенности, как и в приведенном выше примере, будем вычислять число π методом Монте-Карло. Каждый узел-исполнитель будет вычислять свою собственную оценку этого числа, а накапливать и отображать усредненные результаты будет специальный клиентский узел системы
(аккумулятор). Аккумулятор будет подписан на канал “значения для аккумуляции”. Сообщения в этот канал будет публиковать обработчик события
“задание выполнено”.
Таким образом, программный код рассматриваемой системы состоит из четырех элементов: программа узла-исполнителя (файл “mcpi-node.html”),
программа узла-аккумулятора (файл “mcpi-acc.html”), процедура обработки события “запрос задания на обработку” (файл “mcpi-gettask-eh.js”),
процедура обработки события “задание выполнено”.
Начало файла “mcpi-node.html”
<html>
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js">
</script>
<script
src="https://cdn.pubnub.com/sdk/javascript/pubnub.4.15.1.min.js">
</script>
<script>
$(()=>
{
var pubnub = new PubNub ({
// use your own keys
publishKey: 'pub-c-XYXYXYXY-XYXY-XYXY-XYXY- XYXYXYXYXYXY',
35
subscribeKey: 'sub-c-XYXYXYXY-XYXY-XYXY-XYXY- XYXYXYXYXYXY',
restore: false, keepAlive: true
});
const id = PubNub.generateUUID(); const getTaskFireChannel = 'get_task';
const getTaskFeedbackChannel = 'task_for_' + id; const takeResultFireChannel = 'take_result'; const getTaskFire =
{
message: {feedbackChannel: getTaskFeedbackChannel}, channel: getTaskFireChannel
};
pubnub.subscribe({channels: [getTaskFeedbackChannel]}); pubnub.addListener
({
message: (m) =>
{
const msg = m.message;
const task_id = msg.task_id; const N = msg.count;
const mcpi = calcMonteCarloPi(N); const takeResultFire =
{
message: {task_id: task_id, mcpi: mcpi}, channel: takeResultFireChannel
};
pubnub.fire(takeResultFire); $('#info').html
(
'<p>task_id = ' + task_id + '<p>N = ' + N +
'<p>Pi = ' + mcpi
);
36
} // end of message handler
});
pubnub.fire(getTaskFire);
});
</script>
<body><div id='info'></div></body> </html>
Конец файла “mcpi-node.html”
Начало файла “mcpi-acc.html”
<html>
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js">
</script>
<script
src="https://cdn.pubnub.com/sdk/javascript/pubnub.4.15.1.min.js">
</script>
<script>
$(()=>
{
var pubnub = new PubNub ({
// use your own keys
publishKey: 'pub-c-XYXYXYXY-XYXY-XYXY-XYXY- XYXYXYXYXYXY',
subscribeKey: 'sub-c-XYXYXYXY-XYXY-XYXY-XYXY- XYXYXYXYXYXY',
restore: true, keepAlive: true
});
const accumulateResultsChannel = 'acc_results'; pubnub.subscribe({channels: [accumulateResultsChannel]}); var tasksCount = 0;
var totalPointsCount = 0;
37
var accPi = 0; pubnub.addListener ({
message: (m) =>
{
++tasksCount;
const msg = m.message; const N = msg.count; const mcpi = msg.mcpi; totalPointsCount += N; accPi += mcpi; $('#info').html
(
'<p>Pi = ' + (accPi / tasksCount) + ' (' + totalPointsCount + ' points)'
);
} // end of message handler
});
});
</script>
<body><div id='info'></div></body> </html>
Конец файла “mcpi-acc.html”
Начало файла “mcpi-gettask-eh.js” export default (request) =>
{
// this handler is on 'get_task' channel const kvstore = require('kvstore'); const comm = require('pubnub');
const utils = require('utils'); const N = 100000000; // 100 millions const TTL = 1; // 1 minute comm.time().then((task_id) =>
{
38
const feedbackChannel = request.message.feedbackChannel; if(feedbackChannel && (feedbackChannel.length > 0))
{
const msg =
{
task_id: task_id,
count: N + utils.randomInt(0, N/5)
};
comm.publish
({
message: msg,
channel: feedbackChannel
});
kvstore.set(task_id, msg, TTL);
}
});
return request.ok();
}
Конец файла “mcpi-gettask-eh.js”
Начало файла “mcpi-takeresult-eh.js” export default (request) =>
{
// this handler is on 'take_result' channel const kvstore = require('kvstore');
const comm = require('pubnub'); const accChannel = 'acc_results';
const task_id = request.message.task_id; const mcpi = request.message.mcpi; kvstore.get(task_id).then((value) =>
{
if(value)
{
const N = value.count; comm.publish
39
({
channel: accChannel,
message: {count: N, mcpi: mcpi}
});
//uncomment for console output:
//const info = 'task_id: ' + task_id +
//' mcpi: ' + mcpi + ' (N = ' + N + ')';
//console.log(info);
}
});
return request.ok();
}
Конец файла “mcpi-takeresult-eh.js”
Процедура регистрации обработчиков событий в среде PubNub
выполняется с помощью пользовательской консоли управления, которая позволяет в интерактивном режиме выбрать необходимые параметры.
При регистрации обработчиков в поле Event Type следует выбрать Before Publish or Fire. В поле Channels для обработчика запроса задания следует занести get_task, а для обработчика получения результата – take_result. При создании модулей и функций консоль управления предлагает заготовку типового программного текста, которую следует заменить актуальными прикладными программами (файлы “mcpi-gettask-eh.js” и “mcpi- takeresult-eh.js”).
После развертывания и старта модуля бессерверная среда PubNub готова к обработке указанных событий. Для готовности к отображению результатов следует загрузить в браузер файл “mcpi-acc.html” (достаточно единственного экземпляра). Распределенный вычислительный процесс начнется, когда в браузер будет загружен файл “mcpi-node.html” (это можно сделать сразу на множестве вычислительных устройств, подключенных к Интернет).
40
