Главная » Софт » [Перевод] Пример создания на Node.js спортивного приложения, работающего в режиме реального времени

[Перевод] Пример создания на Node.js спортивного приложения, работающего в режиме реального времени

Показатели обновляются в соответствии с изменениями счета по ходу игр. В этой статье я покажу, как создать веб-приложение с использованием Node.js, которое позволяет отслеживать результаты матчей NHL в реальном времени.

Мне очень понравилось писать эту статью, поскольку работа над ней включала две любимые мною вещи: разработку ПО и спорт.
В ходе работы мы будем использовать следующие инструменты:

  • Node.js;
  • Socket.io;
  • MySportsFeed.com.

Если у вас нет установленного Node.js, посетите страницу загрузки и установите, а потом мы продолжим.

Что такое Socket.io?

Это технология, которая соединяет клиента с сервером. В текущем примере клиент — веб-браузер, а сервер — приложение Node.js. Сервер может работать одновременно с несколькими клиентами в любое время.

Тот, в свою очередь, может отправлять сообщениея на сервер, обеспечивая связь в двух направлениях. Как только соединение установлено, сервер может отправлять сообщения всем клиентам или же всего одному из них.

Его использование предусматривало необходимость опроса клиентом сервера и наоборот в поисках новых событий. До Socket.io веб-приложения обычно работали на AJAX. К примеру, такие опросы могли выполняться каждые 10 секунд для проверки наличия новых сообщений.

Это давало дополнительную нагрузку, поскольку поиск новых сообщений велся даже тогда, когда их не было вовсе.

При использовании Socket.io сообщения получаются в режиме реального времени без необходимости постоянно проверять их наличие, что снижает нагрузку.

Пример приложения Socket.io

Прежде чем мы начнем собирать данные соревнований в реальном времени, давайте сделаем приложение-пример для демонстрации принципа работы Socket.io.

В окне консоли нужно перейти к каталогу C:\GitHub\NodeJS, создать новую папку для приложения и в ней — новое приложение: Вначале я собираюсь создать приложение Node.js.

cd \GitHub\NodeJS
mkdir SocketExample
cd SocketExample
npm init

Я оставил настройки по умолчанию, вы можете поступить так же.

В командной строке выполняем следующие команды: npm install express --save. Поскольку мы создаем веб-приложение, для упрощения установки я буду использовать пакет NPM, который называется Express.

Конечно, мы должны установить и пакет Socket.io package: npm install socket.io --save

Для этого создаем новый файл index.js и размещаем следующий участок кода: Теперь нужно запустить веб-сервер.

var app = require('express')();
var http = require('http').Server(app); app.get('/', function(req, res){ res.sendFile(__dirname + '/index.html');
}); http.listen(3000, function(){ console.log('HTTP server started on port 3000');
});

Если Express вам не знаком, то пример кода выше включает библиотеку Express, здесь идет создание нового HTTP-сервера. В примере HTTP-сервер слушает порт 3000, т.е. localhost:3000. Путь идет к корню, “/”. Результат возвращается в виде HTML-файла index.html.

Для создания сервера Socket выполняем следующие команды: Прежде чем создать этот файл, давайте завершим запуск сервера, настроив Socket.io.

var io = require('socket.io')(http); io.on('connection', function(socket){ console.log('Client connection received');
});

Как и в случае с Express, код начинается с импорта библиотеки Socket.io. Это указывается переменной io. Далее, используя эту переменную, создаем обработчик события с функцией on. Это событие вызывается каждый раз, когда клиент подключается к серверу.

Для этого нужно сделать файл index.html и разместить внутри такой код: Теперь давайте создадим простенький клиент.

<!doctype html>
<html> <head> <title>Socket.IO Example</title> </head> <body> <script src="/socket.io/socket.io.js"></script> <script> var socket = io(); </script> </body>
</html>

HTML выше загружает клиент Socket.io JavaScript и инициализирует подключение к серверу. Для того, чтобы увидеть пример, запускаем Node: node index.js.

Страница останется пустой, но если вы посмотрите на консоль во время выполнения Node, то увидите два сообщения: Далее в браузере вводим localhost:3000.

HTTP server started on port 3000
Client connection received

Например, отправим сообщение в клиент с сервера. Сейчас, когда мы успешно соединились, давайте продолжим работу. Затем, когда клиент получит его, отправится ответное сообщение:

io.on('connection', function(socket)); socket.on('receivedFromClient', function (data) { console.log(data); });
});

Предыдущая функция io.on обновлена включением нескольких новых строк кода. Первая, socket.emit, отправляет сообщение клиенту. sendToClient — имя события. Именуя события, вы получаете возможность отправлять разные типы сообщений, так что клиент сможет интерпретировать их по-разному. Еще одно обновление — socket.on, где тоже есть название события: receivedFromClient. Все это создает функцию, которая принимает данные от клиента. В этом случае они также записываются в окне консоли.

Теперь он может принимать и отправлять данные от любого подключенного клиента. Выполненные действия завершают подготовку сервера.

Когда событие получено, дается ответ receivedFromClient. Давайте завершим этот пример, обновив клиент путем получения ивента sendToClient.

В index.html добавляем: На этом завершаем JavaScript-часть HTML.

var socket = io(); socket.on('sendToClient', function (data) { console.log(data); socket.emit('receivedFromClient', { my: 'data' });
});

Используя встроенную переменную сокета, получаем похожую логику на сервере с функцией socket.on. Клиент прослушивает событие sendToClient. Как только клиент подключен, сервер отправляет это сообщение. Клиент, получая его, записывает событие в консоли браузера. После этого клиент использует тот же socket.emit, что ранее сервер для отправки оригинального события. В этом случае клиент отправляет полученное событие FromClient на сервер. Когда тот получает сообщение, это логируется в окне консоли.

Сначала, в консоли, запустите ваше Node-приложение: node index.js. Попробуйте сами. Затем загрузите в браузере localhost:3000.

Проверьте консоль браузера, и вы увидите в логах JSON следующее: {hello: «world»}

Затем, пока выполняется приложение Node, вы увидите следующее:

HTTP server started on port 3000
Client connection received
{ my: 'data' }

Давайте посмотрим, как можно работать с данными соревнований в реальном времени. Как клиент, так и сервер могут использовать JSON-данные для выполнения специфических задач.

Информация с соревнований

После того как мы поняли принципы отправки и получения данных клиентом и сервером, стоит попробовать обеспечить выполнение обновлений в реальном времени. Я использовал данные соревнований, хотя то же самое можно проделывать не только с информацией спортивного характера. Но раз уж работаем с ней, то нужно найти источник. Им послужит MySportsFeeds. Сервис платный — от $1 в месяц, имейте в виду.

Для этого можно использовать пакет NPM: npm install mysportsfeeds-node --save. Как только учетная запись настроена, вы можете начать работу с их API.

После установки пакета подключаем API:

var MySportsFeeds = require("mysportsfeeds-node"); var msf = new MySportsFeeds("1.2", true);
msf.authenticate("********", "*********"); var today = new Date(); msf.getData('nhl', '2017-2018-regular', 'scoreboard', 'json', { fordate: today.getFullYear() + ('0' + parseInt(today.getMonth() + 1)).slice(-2) + ('0' + today.getDate()).slice(-2), force: true
});

В примере выше замените мои данные своими.

Переменная fordate — то, что определяет дату. Код выполняет вызов API для получения сегодняшних результатов соревнований NHL. Также я использовал force и true для того, чтобы получать в ответ данные даже в том случае, если результаты прежние.

В последнем примере мы это поменяем; для демонстрационных же целей файл результатов можно просмотреть в текстовом редакторе, чтобы понять содержимое ответа. При текущей настройке результаты вызова API записываются в текстовый файл. Этот объект содержит массив, называемый gameScore. В нашем результате мы видим объект таблицы результатов. Каждый объект в свою очередь содержит дочерний объект, называемый игрой. Он сохраняет результат каждой игры. Этот объект предоставляет информацию о том, кто играет.

Данные меняются в зависимости от ее результатов. За пределами игрового объекта имеется несколько переменных, которые обеспечивают отображение текущего состояния игры. Когда игра началась, предоставляются дополнительные данные о результатах, включая информацию о том, какой сейчас период и сколько времени осталось. Когда игра еще не началась, используются переменные, которые позволяют получить информацию о том, когда это случится. Для того чтобы лучше понять, о чем идет речь, давайте перейдем к следующему разделу.

Обновления в режиме реального времени

У нас есть все части пазла, поэтому давайте его собирать! К сожалению, у MySportsFeeds ограниченная поддержка выдачи данных, поэтому придется постоянно запрашивать информацию. Здесь есть положительный момент: мы знаем, что данные меняются только раз в 10 минут, поэтому слишком часто опрашивать сервис не нужно. Получаемые данные можно отправлять с сервера на все подключенные клиенты.

Когда данные приходят, событие отправляется всем подключенным клиентам. Для получения нужных данных я буду использовать функцию setInterval JavaScript, которая позволяет обращаться к API (в моем случае) каждые 10 минут для поиска обновлений. Далее результаты обновляются посредством JavaScript в веб-браузере.

Полученные результаты будут использоваться для любых клиентов, которые подключаются до первого 10-минутного интервала. К MySportsFeeds также идет обращение, когда приложение Node запускается первым. Она, в свою очередь, обновляется как часть интервального опроса. Информация об этом сохраняется в глобальной переменной. Это дает гарантию того, что у каждого из клиентов будут актуальные результаты.

Он содержит функцию, экспортирующуюся из index.js, которая выполняет предыдущий вызов API MySportsFeeds. Для того, чтобы в основном файле index.js все было хорошо, я создал новый файл с именем data.js. Вот полное содержание этого файла:

var MySportsFeeds = require("mysportsfeeds-node"); var msf = new MySportsFeeds("1.2", true, null);
msf.authenticate("*******", "******"); var today = new Date(); exports.getData = function() { return msf.getData('nhl', '2017-2018-regular', 'scoreboard', 'json', { fordate: today.getFullYear() + ('0' + parseInt(today.getMonth() + 1)).slice(-2) + ('0' + today.getDate()).slice(-2), force: true }); };

Функция getData экспортируется и возвращает результаты вызова. Давайте посмотрим на то, что у нас содержится в файле index.js.

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var data = require('./data.js'); // Global variable to store the latest NHL results
var latestData; // Load the NHL data for when client's first connect
// This will be updated every 10 minutes
data.getData().then((result) => { latestData = result;
}); app.get('/', function(req, res){ res.sendFile(__dirname + '/index.html');
}); http.listen(3000, function(){ console.log('HTTP server started on port 3000');
}); io.on('connection', function(socket){ // when clients connect, send the latest data socket.emit('data', latestData);
}); // refresh data
setInterval(function() { data.getData().then((result) => { // Update latest results for when new client's connect latestData = result; // send it to all connected clients io.emit('data', result); console.log('Last updated: ' + new Date()); });
}, 300000);

Первые семь строк кода выше обеспечивают инициализацию требуемых библиотек и вызов глобальной переменной latestData. Последний список используемых библиотек таков: Express, Http Server, созданный с помощью Express, Socket.io плюс только что созданный файл data.js.

С учетом потребностей приложение заполняет последние данные (latestData) для клиентов, которые подключатся при первом запуске сервера:

// Global variable to store the latest NHL results
var latestData;
// Load the NHL data for when client's first connect
// This will be updated every 10 minutes
data.getData().then((result) => { latestData = result;
});

Следующие несколько строк устанавливают путь для главной страницы сайта (в нашем случае localhost:3000/) и дают команду HTTP-серверу слушать порт 3000.

Когда они обнаруживаются, сервер отправляет данные событий с содержимым переменной latestData. Затем Socket.io настраивается для поиска новых подключений.

Когда он обнаружен, переменная latestData обновляется с результатами вызова API. И, наконец, последний фрагмент кода создает необходимый интервал опроса. Эти данные затем передают одно и то же событие всем клиентам.

// refresh data
setInterval(function() { data.getData().then((result) => { // Update latest results for when new client's connect latestData = result; // send it to all connected clients io.emit('data', result); console.log('Last updated: ' + new Date()); });
}, 300000);

Как мы видим, когда клиент подключается и определяется событие, оно выдается с переменной сокета. Это позволяет отправить событие определенному подключившемуся клиенту. Внутри интервала глобальная io используется для отправки события. Она отправляет данные всем клиентам. Настройка сервера завершена.

Как это будет выглядеть

Теперь поработаем над фронтендом клиента. В раннем примере я создал базовый файл index.html, который устанавливает связь с клиентом, чтобы записывать события сервера и отправлять их. Теперь я расширю возможности файла.

Это шаблонная библиотека. Поскольку сервер отправляет нам объект JSON, я буду использовать jQuery и расширение jQuery, которое называется JsRender. Сейчас вы сможете убедиться в широте ее возможностей. Она позволит мне создать шаблон c HTML, который будет использоваться для отображения содержимого каждой игры NHL в удобной форме. Код содержит более 40 строк, поэтому я разделю его на несколько меньших участков, а в конце покажу все содержимое HTML.

Вот что используется для отображения игровых данных:

<script id="gameTemplate" type="text/x-jsrender">
<div class="game"> <div> {{:game.awayTeam.City}} {{:game.awayTeam.Name}} at {{:game.homeTeam.City}} {{:game.homeTeam.Name}} </div> <div> {{if isUnplayed == "true" }} Game starts at {{:game.time}} {{else isCompleted == "false"}} <div>Current Score: {{:awayScore}} - {{:homeScore}}</div> <div> {{if currentIntermission}} {{:~ordinal_suffix_of(currentIntermission)}} Intermission {{else currentPeriod}} {{:~ordinal_suffix_of(currentPeriod)}}<br/> {{:~time_left(currentPeriodSecondsRemaining)}} {{else}} 1st {{/if}} </div> {{else}} Final Score: {{:awayScore}} - {{:homeScore}} {{/if}} </div>
</div>
</script>

Шаблон определяется с помощью тега script. Он содержит идентификатор шаблона и специальный тип сценария, называемый text / x-jsrender. Шаблон определяет контейнер div для каждой игры, который содержит класс игры для применения определенного базового стиля. Внутри этого div и содержится начало шаблона.

Это реализовано объединением названия города и имени команды вместе с игровым объектом из данных MySportsFeeds. В следующем div отображаются гостевая команда и команда-хозяйка.


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Всемирная организация здравоохранения официально признала существование игровой зависимости

В новую версию Международной классификации болезней, которую разрабатывает Всемирная организация здравоохранения, наряду с зависимостью от азартных игр вошла зависимость от онлайн- и офлайн-видеоигр как психическое расстройство. Отдельный идентификатор получили «опасные игры», которые увеличивают риск нанесения физического или психического вреда пациенту ...

[Перевод] Конструкция async/await в JavaScript: сильные стороны, подводные камни и особенности использования

Конструкция async/await появилась в стандарте ES7. Её можно считать замечательным улучшением в сфере асинхронного программирования на JavaScript. Она позволяет писать код, который выглядит как синхронный, но используется для решения асинхронных задач и не блокирует главный поток. Несмотря на то, что ...