Een realtime sporttoepassing maken met Node.js

Wat je gaat creëren

In het artikel van vandaag laat ik zien hoe je een webtoepassing kunt maken die live gamescores van de NHL weergeeft. De scores worden automatisch bijgewerkt naarmate de spellen vorderen.

Dit is een zeer interessant artikel voor mij, omdat het me de kans biedt om twee van mijn favoriete passies bij elkaar te brengen: ontwikkeling en sport.

De technologieën die zullen worden gebruikt om de applicatie te maken zijn:

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

Als u Node.js nog niet hebt geïnstalleerd, bezoekt u nu hun downloadpagina en stelt u deze in voordat u doorgaat.

Wat is Socket.io?

Socket.io is een technologie die een client met een server verbindt. In dit voorbeeld is de client een webbrowser en is de server de Node.js-toepassing. Op de server kunnen op elk gewenst moment meerdere clients zijn aangesloten.

Nadat de verbinding tot stand is gebracht, kan de server berichten verzenden naar alle clients of een individuele client. Als tegenprestatie kan de client een bericht naar de server sturen, waardoor bi-directionele real-time communicatie mogelijk is.

Vóór Socket.io gebruikten webtoepassingen gewoonlijk AJAX en zowel de client als de server zouden elkaar ondervragen op zoek naar gebeurtenissen. Bijvoorbeeld, elke 10 seconden zou er een AJAX-oproep plaatsvinden om te zien of er berichten te behandelen waren.

Polling voor berichten veroorzaakte een aanzienlijke hoeveelheid overhead op zowel de client als de server, omdat het voortdurend naar berichten op zoek zou zijn als er geen berichten waren.

Met Socket.io worden berichten onmiddellijk ontvangen, zonder dat u naar berichten hoeft te zoeken, waardoor de overhead wordt verminderd.

Voorbeeld Socket.io-toepassing

Voordat we de real-time sportgegevens gaan gebruiken, maken we een voorbeeldtoepassing om te laten zien hoe Socket.io werkt.

Om te beginnen ga ik een nieuwe Node.js-applicatie maken. In een consolevenster ga ik naar C: \ GitHub \ NodeJS, maak een nieuwe map voor mijn applicatie en maak een nieuwe applicatie aan:

cd \ GitHub \ NodeJS mkdir SocketExample cd SocketExample npm init

Ik heb alle standaardinstellingen gebruikt.

Omdat we een webtoepassing maken, ga ik een NPM-pakket gebruiken met de naam Express om de installatie te vereenvoudigen. Installeer het als volgt in een opdrachtprompt: npm install express --save

En natuurlijk moeten we het Socket.io-pakket installeren: npm install socket.io --save

Laten we beginnen met het maken van de webserver. Maak een nieuw bestand met de naam index.js en plaats de volgende code erin om de webserver te maken met behulp van Express:

var app = require ('express') (); var http = require ('http'.) Server (app); app.get ('/', functie (req, res) res.sendFile (__ dirname + '/index.html');); http.listen (3000, function () console.log ('HTTP-server gestart op poort 3000'););

Als u niet bekend bent met Express, bevat het bovenstaande codevoorbeeld de Express-bibliotheek en wordt een nieuwe HTTP-server gemaakt. In dit voorbeeld luistert de HTTP-server op poort 3000, b.v. http: // localhost: 3000. Een route wordt gemaakt aan de root van de site "/". Het resultaat van de route retourneert een HTML-bestand: index.html.

Voordat we het index.html-bestand maken, laten we de server afmaken door Socket.io in te stellen. Voeg het volgende toe aan uw index.js-bestand om de Socket-server te maken:

var io = vereisen ('socket.io') (http); io.on ('connection', function (socket) console.log ('Client-verbinding ontvangen'););

Net als bij Express, begint de code met het importeren van de Socket.io-bibliotheek. Dit wordt opgeslagen in een variabele genaamd io. Vervolgens gebruikt u de io variabele, er wordt een gebeurtenishandler gemaakt met de op functie. De gebeurtenis waarnaar wordt geluisterd is verbinding. Deze gebeurtenis wordt elke keer opgeroepen wanneer een client verbinding maakt met de server.

Laten we nu onze erg basale klant maken. Maak een nieuw bestand met de naam index.html en plaats de volgende code in:

   Socket.IO Voorbeeld      

De HTML hierboven laadt de Socket.io-client JavaScript en initialiseert een verbinding met de server. Begin uw Node-toepassing om het voorbeeld te zien: node index.js

Navigeer vervolgens in uw browser naar http: // localhost: 3000. Er verschijnt niets op de pagina; Als u echter naar de console kijkt waar de Node-toepassing wordt uitgevoerd, worden er twee berichten vastgelegd:

  1. HTTP-server gestart op poort 3000
  2. Client-verbinding ontvangen

Nu we een succesvolle socketverbinding hebben, laten we hem gebruiken. Laten we beginnen met het verzenden van een bericht van de server naar de client. Wanneer de client het bericht ontvangt, kan het vervolgens een antwoord terugsturen naar de server.

Laten we het afgekorte index.js-bestand bekijken:

io.on ('verbinding', functie (socket) console.log ('Client-verbinding ontvangen'); socket.emit ('sendToClient', hallo: 'world'); socket.on ('receivedFromClient', functie (data) console.log (data);););

De vorige io.on functie is bijgewerkt met een paar nieuwe regels code. De eerste, socket.emit, verzendt het bericht naar de client. De sendToClient is de naam van het evenement. Door gebeurtenissen een naam te geven, kunt u verschillende soorten berichten verzenden, zodat de client ze anders kan interpreteren. De tweede toevoeging is de socket.on, die ook een evenementnaam bevat: receivedFromClient. Hiermee wordt een functie gemaakt die gegevens van de client accepteert. In dit geval worden de gegevens vastgelegd in het consolevenster.

Hiermee zijn de wijzigingen aan de server voltooid; het kan nu gegevens verzenden en ontvangen van alle verbonden clients.

Laten we dit voorbeeld invullen door de client bij te werken om de sendToClient evenement. Wanneer het de gebeurtenis ontvangt, kan het reageren met de receivedFromClient event terug naar de server.

Dit is mogelijk in het JavaScript-gedeelte van de HTML, dus in het bestand index.html heb ik het JavaScript als volgt bijgewerkt:

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

Met behulp van de instantiated socketvariabele, hebben we zeer vergelijkbare logica op de server met een socket.on functie. Voor de klant luistert het naar de sendToClient evenement. Zodra de client is verbonden, verzendt de server dit bericht. Wanneer de client deze ontvangt, wordt deze in de browser bij de console aangemeld. De client gebruikt dan hetzelfde socket.emit dat de server de oorspronkelijke gebeurtenis heeft verzonden. In dit geval stuurt de client de receivedFromClient evenement naar de server. Wanneer de server het bericht ontvangt, wordt het vastgelegd in het consolevenster.

Probeer het zelf uit. Eerst voert u in een console uw Node-toepassing uit: node index.js. Laad vervolgens http: // localhost: 3000 in uw browser.

Controleer de webbrowserconsole en u zou de volgende JSON-gegevens moeten zien loggen: Hallo Wereld"

Vervolgens ziet u in de opdrachtprompt waar de knooppunttoepassing wordt uitgevoerd, het volgende:

HTTP-server gestart op ontvangen poort 3000-clientverbinding mijn: 'gegevens'

Zowel de client als de server kunnen de JSON-gegevens gebruiken om specifieke taken uit te voeren. We zullen daar meer over leren zodra we verbinding maken met de real-time sportgegevens.

Sport gegevens

Nu we ons hebben meester gemaakt van het verzenden en ontvangen van gegevens van en naar de client en server, kan dit worden gebruikt om real-time updates te bieden. Ik heb ervoor gekozen om sportgegevens te gebruiken, hoewel dezelfde theorie niet beperkt is tot sport. Voordat ik aan dit project begon, deed ik onderzoek naar verschillende sportgegevens. De oplossing waar ik genoegen mee heb genomen, omdat ze gratis ontwikkelaarsaccounts aanbieden, was MySportsFeeds (ik ben er op geen enkele manier mee verbonden). Om toegang te krijgen tot de real-time gegevens, heb ik me aangemeld voor een account en daarna een kleine donatie gedaan. Donaties beginnen bij $ 1 om de gegevens elke 10 minuten te laten bijwerken. Dit is goed voor het voorbeeld.

Nadat uw account is ingesteld, kunt u doorgaan met het instellen van toegang tot hun API. Om dit te helpen, ga ik hun NPM-pakket gebruiken: npm installeer mysportsfeeds-node --save

Nadat het pakket is geïnstalleerd, kunnen API-oproepen als volgt worden uitgevoerd:

var MySportsFeeds = require ("mysportsfeeds-node"); var msf = new MySportsFeeds ("1.2", waar); 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);

In het bovenstaande voorbeeld moet de oproep worden vervangen door de authenticatiefunctie met uw gebruikersnaam en wachtwoord.

De volgende code voert een API-oproep uit om het NHL-scorebord voor vandaag te krijgen. De Fordate variabele is wat vandaag aangeeft. Ik heb ook ingesteld dwingen naar waar zodat een antwoord altijd wordt geretourneerd, zelfs wanneer de gegevens niet zijn gewijzigd.

Met de huidige setup worden de resultaten van de API-aanroep geschreven naar een tekstbestand. In het laatste voorbeeld zal dit worden gewijzigd; voor demonstratiedoeleinden kan het resultatenbestand echter in een teksteditor worden bekeken om de inhoud van het antwoord te begrijpen. De resultaten bevatten een scorebordobject. Dit object bevat een array genaamd gameScore. Dit object slaat het resultaat van elke game op. Elk object bevat een genoemd kindobject spel. Dit object geeft informatie over wie er speelt.

Buiten het game-object zijn er een handvol variabelen die de huidige status van het spel bepalen. De gegevens veranderen op basis van de staat van het spel. Wanneer het spel bijvoorbeeld niet is gestart, zijn er slechts enkele variabelen die aangeven dat het spel niet aan de gang is en nog niet is gestart.

Wanneer het spel aan de gang is, worden aanvullende gegevens gegeven over de score, in welke periode het spel zich bevindt en hoeveel tijd er nog over is. We zullen dit in actie zien als we bij de HTML komen om het spel in de volgende sectie te laten zien.

Realtime updates

We hebben alle stukjes bij de puzzel, dus het is nu tijd om de puzzel samen te stellen om het laatste beeld te onthullen. Momenteel heeft MySportsFeeds beperkte ondersteuning voor het pushen van gegevens naar ons, dus we zullen de gegevens van hen moeten peilen. Gelukkig weten we dat de gegevens slechts één keer per 10 minuten worden gewijzigd, dus we hoeven geen overhead toe te voegen door te vaak naar wijzigingen te vragen. Zodra we de gegevens van hen peilen, kunnen we die updates van de server naar alle aangesloten clients pushen.

Om de polling uit te voeren, zal ik de JavaScript gebruiken setInterval functie om de API (in mijn geval) elke 10 minuten op te roepen om naar updates te zoeken. Wanneer de gegevens worden ontvangen, wordt een gebeurtenis verzonden naar alle verbonden clients. Wanneer de clients de gebeurtenis ontvangen, worden de spelscores bijgewerkt met JavaScript in de webbrowser.

MySportsFeeds wordt ook aangeroepen wanneer de Node-toepassing voor het eerst wordt opgestart. Deze gegevens worden gebruikt voor alle clients die verbinding maken vóór het eerste interval van 10 minuten. Dit wordt opgeslagen in een globale variabele. Dezezelfde globale variabele wordt bijgewerkt als onderdeel van de intervalopvraag. Dit zorgt ervoor dat wanneer nieuwe klanten verbinding maken na de polling, ze de nieuwste gegevens hebben.

Ik heb een nieuw bestand gemaakt met de naam data.js. om te helpen met wat code-netheid in het hoofdindex.js-bestand. Dit bestand bevat een functie die wordt geëxporteerd (beschikbaar in het bestand index.js) waarmee de vorige aanroep naar de MySportsFeeds-API wordt uitgevoerd. Hier zijn de volledige inhoud van dat bestand:

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

EEN gegevens verkrijgen functie wordt geëxporteerd en retourneert het resultaat van de aanroep, wat in dit geval een belofte is die in de hoofdtoepassing zal worden opgelost.

Laten we nu kijken naar de laatste inhoud van het index.js-bestand:

var app = require ('express') (); var http = require ('http'.) Server (app); var io = vereisen ('socket.io') (http); var data = require ('./ data.js'); // Algemene variabele om de nieuwste NHL-resultaten op te slaan var latestData; // Laad de NHL-gegevens in wanneer de client de eerste verbinding maakt // Dit wordt om de 10 minuten bijgewerkt data.getData (). Then ((result) => latestData = result;); app.get ('/', functie (req, res) res.sendFile (__ dirname + '/index.html');); http.listen (3000, function () console.log ('HTTP-server gestart op poort 3000');); io.on ('verbinding', functie (socket) // wanneer clients verbinding maken, verzendt de nieuwste gegevens socket.emit ('data', latestData);); // refresh data setInterval (function () data.getData (). then ((result) => // Update laatste resultaten voor wanneer nieuwe client's connect latestData = result; // stuur het naar alle aangesloten clients io.emit ( 'data', resultaat); console.log ('Laatst bijgewerkt:' + nieuwe datum ()););, 300000);

De eerste zeven regels code hierboven geven een indicatie van de vereiste bibliotheken en de globale latestData variabel. De uiteindelijke lijst met gebruikte bibliotheken is: Express, Http Server gemaakt met Express, Socket.io en het zojuist gemaakte data.js-bestand gemaakt.

Met de benodigdheden voor gezorgd, vult de toepassing de latestData voor clients die verbinding maken wanneer de server voor het eerst wordt gestart:

// Algemene variabele om de nieuwste NHL-resultaten op te slaan var latestData; // Laad de NHL-gegevens in wanneer de client de eerste verbinding maakt // Dit wordt om de 10 minuten bijgewerkt data.getData (). Then ((result) => latestData = result;);

De volgende regels stellen een route in voor de hoofdmap van de website (http: // localhost: 3000 /) en starten de HTTP-server om te luisteren op poort 3000.

Vervolgens is de Socket.io ingesteld om verbindingen te zoeken. Wanneer een nieuwe verbinding wordt ontvangen, zendt de server een gebeurtenis uit met de naam data met de inhoud van de latestData veranderlijk.

En tot slot maakt het laatste stuk code het polling-interval. Wanneer het interval optreedt, de latestData variabele wordt bijgewerkt met de resultaten van de API-aanroep. Deze gegevens stoten vervolgens dezelfde gegevensgebeurtenis uit naar alle clients.

// refresh data setInterval (function () data.getData (). then ((result) => // Update laatste resultaten voor wanneer nieuwe client's connect latestData = result; // stuur het naar alle aangesloten clients io.emit ( 'data', resultaat); console.log ('Laatst bijgewerkt:' + nieuwe datum ()););, 300000);

Mogelijk merkt u dat wanneer de client verbinding maakt en een gebeurtenis wordt uitgezonden, deze de gebeurtenis met de socketvariabele uitstraalt. Met deze aanpak wordt de gebeurtenis alleen naar die verbonden client verzonden. Binnen het interval, de globale io wordt gebruikt om de gebeurtenis uit te zenden. Hiermee wordt het evenement naar alle klanten verzonden.

Dat voltooit de server. Laten we werken aan de voorkant van de client. In een eerder voorbeeld heb ik een basic index.html-bestand gemaakt dat de clientverbinding instelt om gebeurtenissen van de server te loggen en terug te sturen. Ik ga dat bestand uitbreiden om het voltooide voorbeeld te bevatten.

Omdat de server ons een JSON-object stuurt, ga ik jQuery gebruiken en gebruikmaken van een jQuery-extensie met de naam JsRender. Dit is een sjablonerende bibliotheek. Hiermee kan ik een sjabloon maken met HTML die zal worden gebruikt om de inhoud van elk NHL-spel op een gebruiksvriendelijke en consistente manier weer te geven. In een oogwenk ziet u de kracht van deze bibliotheek. De uiteindelijke code is meer dan 40 regels code, dus ik ga deze opsplitsen in kleinere delen en vervolgens de volledige HTML aan het einde weergeven.

In dit eerste deel wordt de sjabloon gemaakt die wordt gebruikt om de gamegegevens te tonen:

De sjabloon wordt gedefinieerd met behulp van een scripttag. Het bevat de id van de sjabloon en een speciaal type script genaamd text / x-jsrender. De sjabloon definieert een container-div voor elk spel dat een klassenspel bevat om een ​​basisstijl toe te passen. In deze div begint het sjabloneren.

In de volgende div, worden het uit en thuis team weergegeven. Dit wordt gedaan door de stad en de teamnaam samen te voegen uit het game-object uit de MySportsFeed-gegevens.

: Game.awayTeam.City is hoe ik een object definieer dat zal worden vervangen door een fysieke waarde wanneer de sjabloon wordt weergegeven. Deze syntaxis wordt gedefinieerd door de JsRender-bibliotheek.

Zodra de teams worden weergegeven, heeft het volgende stuk code een voorwaardelijke logica. Wanneer het spel is afgespeelde, er zal een string worden uitgevoerd waar het spel begint :game tijd.

Wanneer het spel niet is voltooid, wordt de huidige score weergegeven: Huidige score: : awayScore - : homeScore. En tot slot, een lastige kleine logica om vast te stellen in welke periode het hockeyspel zich bevindt of in pauze zit.

Als de variabele currentIntermission wordt weergegeven in de resultaten, dan gebruik ik een functie die ik heb gedefinieerd genoemd ordinal_suffix_of, die het periodegetal zal omzetten in lezen: 1e (2e, 3e, etc.) pauze.

Als het niet onderbroken is, zoek ik naar de huidige periode waarde. Dit maakt ook gebruik van de ordinal_suffix_of  om te laten zien dat het spel zich in de 1e (2e, 3e, etc.) periode bevindt.

Hieronder, een andere functie die ik heb gedefinieerd genoemd tijd over wordt gebruikt om het aantal resterende seconden om te zetten in het aantal resterende minuten en seconden in de periode. Bijvoorbeeld: 10:12.

Het laatste deel van de code geeft de eindscore weer, omdat we weten dat de game is voltooid.

Hier is een voorbeeld van hoe het eruit ziet als er een mix is ​​van voltooide games, lopende games en games die nog niet zijn gestart (ik ben geen erg goede designer, dus het lijkt erop dat je zou verwachten wanneer een ontwikkelaar hun eigen gebruikersinterface).

De volgende is een stuk JavaScript dat de socket maakt, de helperfuncties ordinal_suffix_of en tijd over, en een variabele die verwijst naar de gemaakte jQuery-sjabloon.

Het laatste stukje code is de code om de socketgebeurtenis te ontvangen en de sjabloon weer te geven:

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

Ik heb een placeholder div met het ID van gegevens. Het resultaat van de sjabloonweergave (tmpl.render) schrijft de HTML naar deze container. Wat echt heel leuk is, is dat de JsRender-bibliotheek in dit geval een reeks gegevens kan accepteren data.scoreboard.gameScore, dat itereert door elk element in de array en maakt één game per element.

Hier is de laatste HTML en JavaScript allemaal bij elkaar:

   Socket.IO Voorbeeld   

Start de Node-toepassing en blader naar http: // localhost: 3000 om de resultaten voor uzelf te bekijken!

Elke X minuten stuurt de server een gebeurtenis naar de klant. De client zal de spelelementen opnieuw tekenen met de bijgewerkte gegevens. Dus wanneer je de site openlaat en er periodiek naar kijkt, zie je de gamegegevens vernieuwd worden wanneer games aan de gang zijn.

Conclusie

Het uiteindelijke product gebruikt Socket.io om een ​​server te maken waarmee clients verbinding maken. De server haalt gegevens op en verzendt deze naar de client. Wanneer de client de gegevens ontvangt, kan deze het scherm naadloos bijwerken. Dit vermindert de belasting van de server omdat de client alleen werk uitvoert wanneer deze een gebeurtenis van de server ontvangt.

Sockets zijn niet beperkt tot één richting; de client kan ook berichten naar de server sturen. Wanneer de server het bericht ontvangt, kan het enige verwerking uitvoeren.

Chattoepassingen werken gewoonlijk op deze manier. De server ontvangt een bericht van de client en verzendt vervolgens naar alle verbonden clients om aan te geven dat iemand een nieuw bericht heeft verzonden.

Hopelijk heb je genoten van dit artikel, want het was geweldig om deze real-time sporttoepassing voor een van mijn favoriete sporten te maken!