Come realizzare un'applicazione sportiva in tempo reale usando Node.js

Cosa starai creando

Nell'articolo di oggi mostrerò come creare un'applicazione web che visualizzerà i punteggi dei giochi dal vivo della NHL. I punteggi si aggiorneranno automaticamente all'avanzamento dei giochi.

Questo è un articolo molto eccitante per me, in quanto mi dà la possibilità di mettere insieme due delle mie passioni preferite: lo sviluppo e lo sport.

Le tecnologie che verranno utilizzate per creare l'applicazione sono:

  1. Node.js
  2. Socket.io
  3. MySportsFeed.com

Se non hai installato Node.js, visita la loro pagina di download ora e configuralo prima di continuare.

Cos'è Socket.io?

Socket.io è una tecnologia che collega un client a un server. In questo esempio, il client è un browser Web e il server è l'applicazione Node.js. Il server può avere più client connessi ad esso in qualsiasi momento.

Una volta stabilita la connessione, il server può inviare messaggi a tutti i client o a un singolo client. In cambio, il client può inviare un messaggio al server, consentendo la comunicazione bidirezionale in tempo reale.

Prima di Socket.io, le applicazioni web usavano comunemente AJAX e sia il client che il server si interrogavano l'un l'altro alla ricerca di eventi. Ad esempio, ogni 10 secondi si verifica una chiamata AJAX per verificare se ci sono messaggi da gestire.

Il polling per i messaggi ha causato una notevole quantità di sovraccarico sia sul client che sul server in quanto sarebbe costantemente alla ricerca di messaggi quando non ce n'erano.

Con Socket.io, i messaggi vengono ricevuti istantaneamente, senza bisogno di cercare messaggi, riducendo il sovraccarico.

Esempio di applicazione Socket.io

Prima di consumare i dati sportivi in ​​tempo reale, creiamo un'applicazione di esempio per dimostrare come funziona Socket.io.

Per iniziare, creerò una nuova applicazione Node.js. In una finestra della console, ho intenzione di navigare in C: \ GitHub \ NodeJS, creare una nuova cartella per la mia applicazione e creare una nuova applicazione:

cd \ GitHub \ NodeJS mkdir SocketExample cd SocketExample npm init

Ho usato tutte le impostazioni predefinite.

Poiché stiamo creando un'applicazione web, utilizzerò un pacchetto NPM chiamato Express per semplificare l'installazione. In un prompt dei comandi, installarlo come segue: npm install express --save

E ovviamente avremo bisogno di installare il pacchetto Socket.io: npm install socket.io --save

Iniziamo creando il server web. Crea un nuovo file chiamato index.js e inserisci il seguente codice al suo interno per creare il server web usando Express:

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 ('server HTTP avviato sulla porta 3000'););

Se non si ha familiarità con Express, l'esempio di codice precedente include la libreria Express e crea un nuovo server HTTP. In questo esempio, il server HTTP è in ascolto sulla porta 3000, ad es. http: // localhost: 3000. Una rotta viene creata nella radice del sito "/". Il risultato del percorso restituisce un file HTML: index.html.

Prima di creare il file index.html, finiamo il server impostando Socket.io. Aggiungi il seguente file index.js per creare il server Socket:

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

Simile a Express, il codice inizia importando la libreria Socket.io. Questo è memorizzato in una variabile chiamata io. Quindi, utilizzando il io variabile, un gestore di eventi viene creato con il sopra funzione. L'evento che viene ascoltato è la connessione. Questo evento viene chiamato ogni volta che un client si connette al server.

Creiamo ora il nostro client di base. Crea un nuovo file chiamato index.html e inserisci il seguente codice all'interno di:

   Socket.IO Esempio      

L'HTML sopra carica il client Socket.io JavaScript e inizializza una connessione al server. Per vedere l'esempio, avvia la tua applicazione Node: nodo index.js

Quindi, nel browser, accedere a http: // localhost: 3000. Nulla apparirà sulla pagina; tuttavia, se si guarda la console su cui è in esecuzione l'applicazione Node, vengono registrati due messaggi:

  1. Il server HTTP è stato avviato sulla porta 3000
  2. Connessione client ricevuta

Ora che abbiamo una connessione socket di successo, mettiamola in uso. Iniziamo inviando un messaggio dal server al client. Quindi, quando il client riceve il messaggio, può inviare una risposta al server.

Diamo un'occhiata al file index.js abbreviato:

io.on ('connection', function (socket) console.log ('Client connection received'); socket.emit ('sendToClient', hello: 'world'); socket.on ('receivedFromClient', funzione (dati) console.log (dati);););

Il precedente io.on la funzione è stata aggiornata per includere alcune nuove righe di codice. Il primo, socket.emit, invia il messaggio al cliente. Il sendToClient è il nome dell'evento. Denominando eventi, è possibile inviare diversi tipi di messaggi in modo che il client possa interpretarli in modo diverso. La seconda aggiunta è la socket.on, che contiene anche un nome di evento: receivedFromClient. Questo crea una funzione che accetta i dati dal client. In questo caso, i dati vengono registrati nella finestra della console.

Questo completa gli emendamenti lato server; ora può inviare e ricevere dati da qualsiasi client connesso.

Completiamo questo esempio aggiornando il client per ricevere il sendToClient evento. Quando riceve l'evento, può rispondere con receivedFromClient evento al server.

Questo è realizzato nella parte JavaScript dell'HTML, quindi nel file index.html ho aggiornato JavaScript come segue:

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

Usando la variabile socket istanziata, abbiamo una logica molto simile sul server con a socket.on funzione. Per il cliente, sta ascoltando il sendToClient evento. Non appena il client è connesso, il server invia questo messaggio. Quando il client lo riceve, viene registrato nella console nel browser. Il client quindi usa lo stesso socket.emit che il server utilizzato per inviare l'evento originale. In questo caso, il client restituisce il receivedFromClient evento al server. Quando il server riceve il messaggio, viene registrato nella finestra della console.

Provalo tu stesso Innanzitutto, in una console, esegui l'applicazione Node: nodo index.js. Quindi caricare http: // localhost: 3000 nel browser.

Controlla la console del browser web e dovresti vedere i seguenti dati JSON registrati: Ciao mondo"

Quindi, nel prompt dei comandi in cui è in esecuzione l'applicazione Node, dovresti vedere quanto segue:

Server HTTP avviato sulla porta 3000 Connessione client ricevuta my: "data"

Sia il client che il server possono utilizzare i dati JSON ricevuti per eseguire attività specifiche. Ne sapremo di più una volta che ci colleghiamo ai dati sportivi in ​​tempo reale.

Dati sportivi

Ora che abbiamo imparato come inviare e ricevere dati da e verso il client e il server, questo può essere sfruttato per fornire aggiornamenti in tempo reale. Ho scelto di utilizzare i dati sportivi, sebbene la stessa teoria non si limiti agli sport. Prima di iniziare questo progetto, ho ricercato diversi dati sportivi. Quello su cui ho optato, perché offrono account di sviluppatori gratuiti, era MySportsFeeds (non sono affiliato con loro in alcun modo). Per accedere ai dati in tempo reale, mi sono registrato per un account e ho fatto una piccola donazione. Le donazioni partono da $ 1 per aggiornare i dati ogni 10 minuti. Questo andrà bene per l'esempio.

Una volta impostato il tuo account, puoi procedere alla configurazione dell'accesso alla loro API. Per aiutare con questo, ho intenzione di utilizzare il loro pacchetto NPM: npm installa mysportsfeeds-node --save

Dopo aver installato il pacchetto, le chiamate API possono essere effettuate come segue:

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);

Nell'esempio sopra, assicurati di sostituire la chiamata alla funzione di autenticazione con il tuo nome utente e password.

Il seguente codice esegue una chiamata API per ottenere il tabellone NHL per oggi. Il fordate variabile è ciò che specifica oggi. Ho anche impostato vigore a vero in modo che venga sempre restituita una risposta, anche quando i dati non sono cambiati.

Con la configurazione corrente, i risultati della chiamata API vengono scritti in un file di testo. Nell'esempio finale, questo sarà cambiato; tuttavia, a scopo dimostrativo, il file dei risultati può essere rivisto in un editor di testo per comprendere il contenuto della risposta. I risultati contengono un oggetto tabellone. Questo oggetto contiene un array chiamato gameScore. Questo oggetto memorizza il risultato di ogni gioco. Ogni oggetto contiene un oggetto figlio chiamato gioco. Questo oggetto fornisce le informazioni su chi sta giocando.

Al di fuori dell'oggetto di gioco, ci sono una manciata di variabili che forniscono lo stato attuale del gioco. I dati cambiano in base allo stato del gioco. Ad esempio, quando il gioco non è iniziato, ci sono solo alcune variabili che ci dicono che il gioco non è in corso e non è stato avviato.

Quando il gioco è in corso, vengono forniti dati aggiuntivi sul punteggio, in quale periodo si trova il gioco e quanto tempo rimane. Vedremo questo in azione quando arriveremo all'HTML per mostrare il gioco nella prossima sezione.

Aggiornamenti in tempo reale

Abbiamo tutti i pezzi del puzzle, quindi è giunto il momento di mettere insieme il puzzle per rivelare l'immagine finale. Attualmente, MySportsFeeds ha un supporto limitato per spingere i dati a noi, quindi dovremo interrogare i dati da loro. Fortunatamente, sappiamo che i dati cambiano solo una volta ogni 10 minuti, quindi non è necessario aggiungere overhead eseguendo il polling delle modifiche troppo frequentemente. Una volta che abbiamo eseguito il polling dei dati da loro, possiamo inviare tali aggiornamenti dal server a tutti i client connessi.

Per eseguire il polling, userò il JavaScript setInterval funzione per chiamare l'API (nel mio caso) ogni 10 minuti per cercare gli aggiornamenti. Quando i dati vengono ricevuti, un evento viene inviato a tutti i client connessi. Quando i clienti ricevono l'evento, i punteggi del gioco verranno aggiornati con JavaScript nel browser web.

MySportsFeeds verrà chiamato anche quando l'applicazione Node si avvia per la prima volta. Questi dati verranno utilizzati per tutti i client che si connettono prima del primo intervallo di 10 minuti. Questo è memorizzato in una variabile globale. Questa stessa variabile globale viene aggiornata come parte dell'intervallo di polling. Ciò garantirà che quando i nuovi client si connetteranno dopo il polling, avranno gli ultimi dati.

Per aiutare con la pulizia del codice nel file index.js principale, ho creato un nuovo file chiamato data.js. Questo file conterrà una funzione che viene esportata (disponibile nel file index.js) che esegue la precedente chiamata all'API MySportsFeeds. Ecco i contenuti completi di quel file:

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 (oggi .getMonth () + 1)). slice (-2) + ('0' + today.getDate ()). slice (-2), force: true); ;

UN getData la funzione viene esportata e restituisce il risultato della chiamata, che in questo caso è una promessa che verrà risolta nell'applicazione principale.

Ora diamo un'occhiata al contenuto finale del file index.js:

var app = require ('express') (); var http = require ('http'). Server (app); var io = require ('socket.io') (http); var data = require ('./ data.js'); // Variabile globale per memorizzare gli ultimi risultati NHL var latestData; // Carica i dati NHL per la prima connessione del client // Questo verrà aggiornato ogni 10 minuti data.getData (). Then ((result) => latestData = result;); app.get ('/', function (req, res) res.sendFile (__ dirname + '/index.html');); http.listen (3000, function () console.log ('server HTTP avviato sulla porta 3000');); io.on ('connection', function (socket) // quando i client si connettono, invia gli ultimi dati socket.emit ('data', latestData);); // aggiorna i dati setInterval (function () data.getData (). then ((result) => // Aggiorna i risultati più recenti per quando il nuovo client si connette latestData = result; // invialo a tutti i client collegati io.emit ( 'data', risultato); console.log ('Ultimo aggiornamento:' + new Date ()););, 300000);

Le prime sette righe di codice sopra istanziano le librerie richieste e quelle globali latestData variabile. L'elenco finale delle librerie utilizzate è: Express, Http Server creato con Express, Socket.io e il file data.js appena descritto appena creato.

Con le necessità curate, l'applicazione popola il latestData per i client che si connetteranno quando il server viene avviato per la prima volta:

// Variabile globale per memorizzare gli ultimi risultati NHL var latestData; // Carica i dati NHL per la prima connessione del client // Questo verrà aggiornato ogni 10 minuti data.getData (). Then ((result) => latestData = result;);

Le prossime righe impostano un percorso per la pagina principale del sito web (http: // localhost: 3000 /) e avvia il server HTTP per l'ascolto sulla porta 3000.

Successivamente, Socket.io è impostato per cercare le connessioni. Quando viene ricevuta una nuova connessione, il server emette un evento chiamato dati con i contenuti del latestData variabile.

E infine, il blocco finale del codice crea l'intervallo di polling. Quando si verifica l'intervallo, il latestData la variabile viene aggiornata con i risultati della chiamata API. Questi dati quindi emettono lo stesso evento di dati per tutti i client.

// aggiorna i dati setInterval (function () data.getData (). then ((result) => // Aggiorna i risultati più recenti per quando il nuovo client si connette latestData = result; // invialo a tutti i client collegati io.emit ( 'data', risultato); console.log ('Ultimo aggiornamento:' + new Date ()););, 300000);

Si può notare che quando il client si connette e viene emesso un evento, sta emettendo l'evento con la variabile socket. Questo approccio invierà l'evento solo a quel cliente connesso. All'interno dell'intervallo, il globale io è usato per emettere l'evento. Questo invierà l'evento a tutti i clienti.

Questo completa il server. Lavoriamo sul front-end del cliente. In un esempio precedente, ho creato un file index.html di base che impostava la connessione client che registrava gli eventi dal server e ne restituiva uno. Ho intenzione di estendere tale file per contenere l'esempio completato.

Poiché il server ci invia un oggetto JSON, utilizzerò jQuery e sfrutterò un'estensione jQuery chiamata JsRender. Questa è una libreria di modelli. Mi consentirà di creare un modello con HTML che verrà utilizzato per visualizzare i contenuti di ogni gioco NHL in modo semplice e coerente. In un attimo, vedrai il potere di questa libreria. Il codice finale è più di 40 righe di codice, quindi ho intenzione di scomporlo in blocchi più piccoli e quindi visualizzare l'intero codice HTML insieme alla fine.

Questa prima parte crea il modello che verrà utilizzato per mostrare i dati del gioco:

Il modello è definito utilizzando un tag script. Contiene l'id del modello e un tipo di script speciale chiamato text / x-jsrender. Il modello definisce un contenitore div per ogni gioco che contiene un gioco di classe per applicare alcuni stili di base. All'interno di questo div, inizia il modello.

Nel successivo div, vengono visualizzate la squadra di casa e di casa. Questo viene fatto concatenando la città e il nome della squadra insieme dall'oggetto del gioco dai dati MySportsFeed.

: Game.awayTeam.City è come definisco un oggetto che verrà sostituito con un valore fisico quando il modello viene renderizzato. Questa sintassi è definita dalla libreria JsRender.

Una volta visualizzate le squadre, il prossimo pezzo di codice fa qualche logica condizionale. Quando il gioco è unplayed, verrà emessa una stringa a cui inizierà il gioco :tempo di gioco.

Quando il gioco non è completato, viene visualizzato il punteggio corrente: Punteggio attuale: : awayScore - : homeScore. E infine, una piccola e complicata logica per identificare in quale periodo si trova la partita di hockey o se è in intervallo.

Se la variabile currentIntermission viene fornito nei risultati, quindi utilizzo una funzione definita dall'utente chiamata ordinal_suffix_of, che convertirà il numero del periodo da leggere: 1 ° (2 °, 3 °, ecc.) Intermission.

Quando non è in intervallo, cerco il periodo attuale valore. Questo utilizza anche il ordinal_suffix_of  per mostrare che il gioco è nel 1 ° (2 °, 3 °, ecc.) periodo.

Sotto questo, un'altra funzione che ho definito chiamata tempo rimasto è usato per convertire il numero di secondi rimanenti nel numero di minuti e secondi rimanenti nel periodo. Ad esempio: 10:12.

La parte finale del codice mostra il punteggio finale perché sappiamo che il gioco è stato completato.

Ecco un esempio di ciò che appare quando c'è un mix di giochi finiti, giochi in corso e giochi che non sono ancora iniziati (non sono un ottimo designer, quindi sembra che ci si aspetterebbe quando uno sviluppatore fa la loro interfaccia utente).

Il prossimo è un blocco di JavaScript che crea il socket, le funzioni di supporto ordinal_suffix_of e tempo rimasto, e una variabile che fa riferimento al modello jQuery creato.

L'ultimo pezzo di codice è il codice per ricevere l'evento socket e renderizzare il modello:

socket.on ('data', function (data) console.log (data); $ ('# data'). html (tmpl.render (data.scoreboard.gameScore, helper)););

Ho un div di segnaposto con l'id dei dati. Il risultato del rendering del modello (tmpl.render) scrive l'HTML in questo contenitore. Ciò che è veramente accurato è che la libreria JsRender può accettare una matrice di dati, in questo caso data.scoreboard.gameScore, che scorre attraverso ogni elemento dell'array e crea un gioco per elemento.

Ecco l'HTML finale e JavaScript tutti insieme:

   Socket.IO Esempio   

Avvia l'applicazione Nodo e vai a http: // localhost: 3000 per vedere i risultati!

Ogni X minuti, il server invierà un evento al client. Il client ridisegna gli elementi del gioco con i dati aggiornati. Quindi, quando lasci il sito aperto e lo guardi periodicamente, vedrai il refresh dei dati di gioco quando i giochi sono attualmente in corso.

Conclusione

Il prodotto finale utilizza Socket.io per creare un server al quale i client si connettono. Il server recupera i dati e li invia al client. Quando il client riceve i dati, può aggiornare senza problemi il display. Ciò riduce il carico sul server perché il client esegue il lavoro solo quando riceve un evento dal server.

Le prese non sono limitate a una direzione; il client può anche inviare messaggi al server. Quando il server riceve il messaggio, può eseguire alcune elaborazioni.

Le applicazioni di chat normalmente funzionano in questo modo. Il server riceverà un messaggio dal client e quindi trasmetterà a tutti i client connessi per mostrare che qualcuno ha inviato un nuovo messaggio.

Spero che questo articolo ti sia piaciuto perché mi sono divertito tantissimo a creare questa applicazione sportiva in tempo reale per uno dei miei sport preferiti!