
Neste artigo, mostrarei como criar um aplicativo Web usando o Node.js, que permite acompanhar os resultados das correspondências da NHL em tempo real. Os indicadores são atualizados de acordo com as mudanças na pontuação durante os jogos.
Gostei muito de escrever este artigo, porque trabalhar nele incluía duas coisas que eu adorava: desenvolvimento de software e esportes.
No decorrer do trabalho, usaremos as seguintes ferramentas:
- Node.js;
- Socket.io;
- MySportsFeed.com.
Se você não possui o Node.js instalado, visite a página de download e instale, e continuaremos.
O que é o socket.io?
Essa é a tecnologia que conecta o cliente ao servidor. No exemplo atual, o cliente é um navegador da Web e o servidor é o Node.js. O servidor pode trabalhar simultaneamente com vários clientes a qualquer momento.
Depois que a conexão é estabelecida, o servidor pode enviar mensagens para todos os clientes ou apenas um deles. Isso, por sua vez, pode enviar mensagens para o servidor, fornecendo comunicação em duas direções.
Antes do Socket.io, os aplicativos da Web geralmente eram executados no AJAX. Seu uso previa a necessidade de o cliente pesquisar o servidor e vice-versa em busca de novos eventos. Por exemplo, essas pesquisas podem ser realizadas a cada 10 segundos para verificar novas mensagens.
Isso deu um ônus adicional, pois a busca por novas mensagens foi realizada mesmo quando elas não eram de todo.
Ao usar o Socket.io, as mensagens são recebidas em tempo real sem a necessidade de verificar constantemente sua presença, o que reduz a carga.
Aplicativo de amostra Socket.io
Antes de começarmos a coletar dados da concorrência em tempo real, vamos criar um aplicativo de exemplo para demonstrar como o Socket.io funciona.
Primeiro, vou criar um aplicativo Node.js. Na janela do console, você precisa ir para o diretório C: \ GitHub \ NodeJS, criar uma nova pasta para o aplicativo e nele um novo aplicativo:
cd \GitHub\NodeJS mkdir SocketExample cd SocketExample npm init
Deixei as configurações padrão, você pode fazer o mesmo.
Como estamos construindo um aplicativo Web, usarei um pacote NPM chamado Express para simplificar a instalação. No prompt de comando, execute os seguintes comandos: npm install express --save.
Obviamente, também devemos instalar o pacote Socket.io: npm install socket.io --save
Agora você precisa iniciar o servidor web. Para fazer isso, crie um novo arquivo index.js e coloque a seguinte seção de código:
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'); });
Se o Express não lhe é familiar, o exemplo de código acima inclui a biblioteca Express, aqui estamos criando um novo servidor HTTP. No exemplo, o servidor HTTP escuta na porta 3000, ou seja,
localhost : 3000. O caminho vai para a raiz "/". O resultado é retornado como um arquivo index.html HTML.
Antes de criar este arquivo, vamos concluir a inicialização do servidor configurando o Socket.io. Para criar um servidor Socket, execute os seguintes comandos:
var io = require('socket.io')(http); io.on('connection', function(socket){ console.log('Client connection received'); });
Como no Express, o código começa importando a biblioteca Socket.io. Isso é indicado pela variável io. Em seguida, usando essa variável, crie um manipulador de eventos com a função on. Este evento é acionado toda vez que um cliente se conecta ao servidor.
Agora vamos criar um cliente simples. Para fazer isso, crie o arquivo index.html e coloque o seguinte código dentro:
<!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>
O HTML acima carrega o cliente JavaScript Socket.io e inicializa a conexão com o servidor. Para ver um exemplo, execute Node: node index.js.
Em seguida, no navegador, digite
localhost : 3000. A página permanecerá em branco, mas se você olhar para o console enquanto o Node estiver em execução, verá duas mensagens:
Servidor HTTP iniciado na porta 3000
Conexão do cliente recebidaAgora que nos conectamos com sucesso, vamos continuar o trabalho. Por exemplo, envie uma mensagem para o cliente do servidor. Então, quando o cliente receber, uma mensagem de resposta será enviada:
io.on('connection', function(socket){ console.log('Client connection received'); socket.emit('sendToClient', { hello: 'world' }); socket.on('receivedFromClient', function (data) { console.log(data); }); });
A função io.on anterior foi atualizada para incluir várias novas linhas de código. O primeiro, socket.emit, envia uma mensagem para o cliente. sendToClient é o nome do evento. Ao nomear eventos, você pode enviar diferentes tipos de mensagens, para que o cliente possa interpretá-las de maneira diferente. Outra atualização é socket.on, que também possui um nome de evento: receivedFromClient. Tudo isso cria uma função que recebe dados do cliente. Nesse caso, eles também são gravados na janela do console.
As etapas concluídas concluem a preparação do servidor. Agora ele pode receber e enviar dados de qualquer cliente conectado.
Vamos terminar este exemplo atualizando o cliente recebendo o evento sendToClient. Quando um evento é recebido, uma resposta é recebidaFromClient.
Isso conclui a parte JavaScript do HTML. Em index.html, adicione:
var socket = io(); socket.on('sendToClient', function (data) { console.log(data); socket.emit('receivedFromClient', { my: 'data' }); });
Usando a variável de soquete embutida, obtemos uma lógica semelhante no servidor com a função socket.on. O cliente escuta o evento sendToClient. Assim que o cliente estiver conectado, o servidor envia esta mensagem. O cliente, recebendo-o, registra o evento no console do navegador. Depois disso, o cliente usa o mesmo socket.emit que o servidor usado para enviar o evento original. Nesse caso, o cliente envia o evento FromClient recebido para o servidor. Quando ele recebe uma mensagem, ela é registrada na janela do console.
Tente você mesmo. Primeiro, no console, inicie o aplicativo Node: node index.js. Em seguida, carregue
localhost : 3000 no navegador.
Verifique o console do navegador e você verá o seguinte nos logs JSON: {hello: "world"}
Em seguida, enquanto o aplicativo Node estiver em execução, você verá o seguinte:
Servidor HTTP iniciado na porta 3000
Conexão do cliente recebida
{my: 'data'}O cliente e o servidor podem usar dados JSON para executar tarefas específicas. Vamos ver como você pode trabalhar com dados da concorrência em tempo real.
Informações sobre a competição
Depois de entendermos os princípios de envio e recebimento de dados pelo cliente e pelo servidor, vale a pena tentar garantir que as atualizações sejam executadas em tempo real. Usei os dados da competição, embora o mesmo possa ser feito não apenas com informações esportivas. Mas, como estamos trabalhando com isso, precisamos encontrar a fonte. Eles servirão o
MySportsFeeds . O serviço é pago - a partir de US $ 1 por mês, lembre-se.
Depois que sua conta estiver configurada, você poderá começar a usar a API deles. Você pode usar o pacote NPM para isso: npm install mysportsfeeds-node --save.
Depois de instalar o pacote, conectamos a 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 });
No exemplo acima, substitua meus dados pelos seus.
O código faz uma chamada de API para obter os resultados atuais da competição da NHL. A variável fordate é o que define a data. Também usei force e true para recuperar dados, mesmo que os resultados sejam os mesmos.
Com a configuração atual, os resultados da chamada da API são gravados em um arquivo de texto. No último exemplo, mudaremos isso; para fins de demonstração, o arquivo de resultados pode ser visualizado em um editor de texto para entender o conteúdo da resposta. Em nosso resultado, vemos o objeto da tabela de resultados. Este objeto contém uma matriz chamada gameScore. Ele salva o resultado de cada jogo. Cada objeto, por sua vez, contém um objeto filho chamado jogo. Este objeto fornece informações sobre quem está jogando.
Fora do objeto do jogo, existem várias variáveis que exibem o estado atual do jogo. Os dados são alterados dependendo dos resultados. Quando o jogo ainda não começou, são usadas variáveis que fornecem informações sobre quando isso acontecerá. Quando o jogo começa, são fornecidos dados adicionais sobre os resultados, incluindo informações sobre o período atual e quanto tempo resta. Para entender melhor o que está em jogo, vamos para a próxima seção.
Atualizações em tempo real
Temos todas as peças do quebra-cabeça, então vamos montá-lo! Infelizmente, o MySportsFeeds tem suporte limitado para a emissão de dados, então você precisa solicitar informações constantemente. Há um ponto positivo aqui: sabemos que os dados mudam apenas uma vez a cada 10 minutos; portanto, não é necessário interrogar o serviço com muita frequência. Os dados recebidos podem ser enviados do servidor para todos os clientes conectados.
Para obter os dados necessários, usarei a função JavaScript setInterval, que permite acessar a API (no meu caso) a cada 10 minutos para encontrar atualizações. Quando os dados chegam, o evento é enviado a todos os clientes conectados. Os resultados são atualizados via JavaScript em um navegador da web.
MySportsFeeds também é chamado quando o aplicativo Node é iniciado primeiro. Os resultados serão usados para qualquer cliente que se conectar antes do primeiro intervalo de 10 minutos. Informações sobre isso são armazenadas em uma variável global. Por sua vez, é atualizado como parte de uma pesquisa de intervalo. Isso garante que cada um dos clientes tenha resultados relevantes.
Para que tudo fique bem no arquivo index.js principal, criei um novo arquivo chamado data.js. Ele contém uma função exportada do index.js que faz uma chamada anterior para a API MySportsFeeds. Aqui está o conteúdo completo deste arquivo:
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 }); };
A função getData é exportada e retorna os resultados da chamada. Vejamos o que temos no arquivo index.js.
var app = require('express')(); var http = require('http').Server(app); var io = require('socket.io')(http); var data = require('./data.js');
As sete primeiras linhas de código acima inicializam as bibliotecas necessárias e chamam a variável global latestData. A lista mais recente de bibliotecas usadas é: Express, servidor HTTP, criado usando o Express, Socket.io mais o arquivo data.js recém-criado.
Levando em consideração as necessidades, o aplicativo preenche os dados mais recentes (latestData) para clientes que se conectam quando o servidor é iniciado pela primeira vez:
As próximas linhas definem o caminho da página principal do site (no nosso caso
localhost : 3000 /) e instruem o servidor HTTP a escutar na porta 3000.
O Socket.io é então configurado para procurar novas conexões. Quando são detectados, o servidor envia dados do evento com o conteúdo da variável latestData.
E, finalmente, o último trecho de código cria o intervalo de pesquisa necessário. Quando é detectada, a variável latestData é atualizada com os resultados da chamada da API. Esses dados passam o mesmo evento para todos os clientes.
Como vemos, quando um cliente se conecta e um evento é definido, ele é emitido com uma variável de soquete. Isso permite que você envie um evento para um cliente conectado específico. Dentro do intervalo, o io global é usado para despachar o evento. Ele envia dados para todos os clientes. A configuração do servidor está concluída.
Como ficará
Agora vamos trabalhar na interface do cliente. Em um exemplo anterior, criei o arquivo index.html base, que estabelece uma conexão com o cliente para registrar eventos do servidor e enviá-los. Agora vou expandir os recursos do arquivo.
Como o servidor nos envia um objeto JSON, usarei o jQuery e uma extensão do jQuery chamada JsRender. Esta é uma biblioteca de modelos. Isso permitirá que eu crie um modelo HTML que será usado para exibir o conteúdo de cada jogo da NHL de uma maneira conveniente. Agora você pode ver a amplitude de seus recursos. O código contém mais de 40 linhas, por isso vou dividi-lo em várias seções menores e, no final, mostrarei todo o conteúdo HTML.
Aqui está o que é usado para exibir dados do jogo:
<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>
O modelo é definido usando a tag de script. Ele contém um identificador de modelo e um tipo de script especial chamado text / x-jsrender. O modelo define um contêiner div para cada jogo que contém uma classe de jogo para aplicar um estilo base específico. Dentro desta div está o início do modelo.
A próxima div exibe a equipe convidada e a equipe host. Isso é implementado combinando o nome da cidade e o nome da equipe com o objeto de jogo dos dados do MySportsFeeds.
{{: game.awayTeam.City}} é como eu defino um objeto que será substituído por um valor físico ao renderizar o modelo. Essa sintaxe é definida pela biblioteca JsRender.
Quando o jogo não é reproduzido, aparece uma linha na qual o jogo começa com {{: game.time}}.
Até o jogo terminar, a pontuação atual é exibida: {{: awayScore}} - {{: homeScore}}. E, finalmente, um pequeno truque que determinará qual é o período agora e para esclarecer se há uma pausa agora.
Se a variável currentIntermission aparecer nos resultados, utilizarei a função I, definida por ordinal_suffix_of, que converte o número do período no seguinte texto: 1ª (2ª, 3ª, etc.) Interrupção.
Quando não há interrupção, procuro o valor de currentPeriod. Ordinal_suffix_of também é usado aqui para mostrar que o jogo está no 1º (2º, 3º etc.) períodos.
Além disso, outra função que defini como time_left é usada para converter o número de segundos restantes até o final do período. Por exemplo: 10:12.
A última parte do código exibe o resultado final quando o jogo é concluído.
Aqui está um exemplo de como fica quando há uma lista mista de jogos concluídos, jogos que ainda não foram finalizados e jogos que ainda não foram iniciados (eu não sou um designer muito bom, portanto, o resultado parece o que deveria ser quando o desenvolvedor cria um jogo personalizado interface do aplicativo faça você mesmo):

A seguir, há um trecho de JavaScript que cria um soquete, as funções auxiliares ordinal_suffix_of e time_left e uma variável que se refere ao modelo jQuery gerado.
<script> var socket = io(); var tmpl = $.templates("#gameTemplate"); var helpers = { ordinal_suffix_of: function(i) { var j = i % 10, k = i % 100; if (j == 1 && k != 11) { return i + "st"; } if (j == 2 && k != 12) { return i + "nd"; } if (j == 3 && k != 13) { return i + "rd"; } return i + "th"; }, time_left: function(time) { var minutes = Math.floor(time / 60); var seconds = time - minutes * 60; return minutes + ':' + ('0' + seconds).slice(-2); } }; </script>
O último fragmento é o código para receber o evento do soquete e renderizar o modelo:
socket.on('data', function (data) { console.log(data); $('#data').html(tmpl.render(data.scoreboard.gameScore, helpers)); });
Eu tenho um delimitador com uma identificação de dados. O resultado da renderização do modelo (tmpl.render) grava HTML neste contêiner. O que é muito legal - a biblioteca JsRender pode receber uma matriz de dados, neste caso, data.scoreboard.gameScore, que itera através de cada elemento da matriz e cria um jogo por elemento.
Aqui está a versão final prometida acima, onde HTML e JavaScript são reunidos:
<!doctype html> <html> <head> <title>Socket.IO Example</title> </head> <body> <div id="data"> </div> <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 src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jsrender/0.9.90/jsrender.min.js"></script> <script src="/socket.io/socket.io.js"></script> <script> var socket = io(); var helpers = { ordinal_suffix_of: function(i) { var j = i % 10, k = i % 100; if (j == 1 && k != 11) { return i + "st"; } if (j == 2 && k != 12) { return i + "nd"; } if (j == 3 && k != 13) { return i + "rd"; } return i + "th"; }, time_left: function(time) { var minutes = Math.floor(time / 60); var seconds = time - minutes * 60; return minutes + ':' + ('0' + seconds).slice(-2); } }; var tmpl = $.templates("#gameTemplate"); socket.on('data', function (data) { console.log(data); $('#data').html(tmpl.render(data.scoreboard.gameScore, helpers)); }); </script> <style> .game { border: 1px solid #000; float: left; margin: 1%; padding: 1em; width: 25%; } </style> </body> </html>
Agora é a hora de iniciar o aplicativo Node e abrir o
localhost : 3000 para ver o resultado!
A cada X minutos, o servidor envia um evento para o cliente. O cliente, por sua vez, redesenha os elementos do jogo com dados atualizados. Portanto, quando você deixar o site aberto, os resultados dos jogos serão atualizados constantemente.
Conclusão
O produto final usa o Socket.io para criar um servidor ao qual os clientes se conectam. O servidor recupera os dados e os envia para o cliente. Quando um cliente recebe dados, ele pode atualizar os resultados gradualmente. Isso reduz a carga no servidor porque o cliente age apenas quando recebe um evento do servidor.
O servidor pode enviar mensagens para o cliente, e o cliente, por sua vez, para o servidor. Quando o servidor recebe a mensagem, ele executa o processamento de dados.
Os aplicativos de bate-papo funcionam da mesma maneira. O servidor receberá uma mensagem do cliente e depois transmitirá todos os dados para os clientes conectados para indicar que alguém enviou uma nova mensagem.
Espero que você tenha gostado deste artigo, porque quando desenvolvi esse aplicativo esportivo em tempo real, tive uma montanha de prazer que queria compartilhar com meu conhecimento. Afinal, o hóquei é um dos meus esportes favoritos!
