Contoh membuat aplikasi olahraga waktu nyata di Node.js



Pada artikel ini saya akan menunjukkan cara membuat aplikasi web menggunakan Node.js, yang memungkinkan Anda melacak hasil pertandingan NHL secara real time. Indikator diperbarui sesuai dengan perubahan skor selama pertandingan.

Saya sangat menikmati menulis artikel ini, karena mengerjakannya termasuk dua hal yang saya sukai: pengembangan perangkat lunak dan olahraga.

Dalam perjalanan kerja, kami akan menggunakan alat-alat berikut:

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

Jika Node.js Anda belum diinstal, kunjungi halaman unduh dan instal, lalu kami akan melanjutkan.

Apa itu socket.io?


Ini adalah teknologi yang menghubungkan klien ke server. Dalam contoh saat ini, klien adalah browser web, dan server adalah Node.js. Server dapat bekerja secara bersamaan dengan beberapa klien kapan saja.

Setelah koneksi dibuat, server dapat mengirim pesan ke semua klien atau hanya salah satunya. Itu, pada gilirannya, dapat mengirim pesan ke server, menyediakan komunikasi dalam dua arah.

Sebelum ke Socket.io, aplikasi web biasanya dijalankan pada AJAX. Penggunaannya disediakan untuk kebutuhan klien untuk polling server dan sebaliknya mencari acara baru. Misalnya, jajak pendapat tersebut dapat dilakukan setiap 10 detik untuk memeriksa pesan baru.

Ini memberi beban tambahan, karena pencarian pesan baru dilakukan bahkan ketika mereka tidak sama sekali.

Saat menggunakan Socket.io, pesan diterima secara real time tanpa perlu terus-menerus memeriksa keberadaannya, yang mengurangi beban.

Contoh aplikasi Socket.io


Sebelum kita mulai mengumpulkan data kompetisi waktu nyata, mari kita buat contoh aplikasi untuk menunjukkan cara kerja Socket.io.

Pertama, saya akan membuat aplikasi Node.js. Di jendela konsol, Anda harus pergi ke direktori C: \ GitHub \ NodeJS, membuat folder baru untuk aplikasi dan di dalamnya aplikasi baru:

cd \GitHub\NodeJS mkdir SocketExample cd SocketExample npm init 

Saya meninggalkan pengaturan default, Anda dapat melakukan hal yang sama.

Karena kami sedang membangun aplikasi web, saya akan menggunakan paket NPM yang disebut Express untuk menyederhanakan instalasi. Pada prompt perintah, jalankan perintah berikut: npm install express --save.

Tentu saja, kita juga harus menginstal paket Socket.io: npm install socket.io --save

Sekarang Anda harus memulai server web. Untuk melakukan ini, buat file index.js baru dan letakkan bagian kode berikut:

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

Jika Express tidak asing bagi Anda, maka contoh kode di atas termasuk perpustakaan Express, di sini kami membuat server HTTP baru. Dalam contoh tersebut, server HTTP mendengarkan pada port 3000, mis. localhost : 3000. Path menuju ke root, "/". Hasilnya dikembalikan sebagai file HTML index.html.

Sebelum membuat file ini, mari kita selesaikan startup server dengan mengkonfigurasi Socket.io. Untuk membuat server Socket, jalankan perintah berikut:

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

Seperti dengan Express, kode dimulai dengan mengimpor perpustakaan Socket.io. Ini ditunjukkan oleh variabel io. Selanjutnya, menggunakan variabel ini, buat pengendali event dengan fungsi on. Acara ini dipecat setiap kali klien terhubung ke server.

Sekarang mari kita buat klien sederhana. Untuk melakukan ini, buat file index.html dan tempatkan kode berikut di dalam:

 <!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 di atas memuat klien Socket.io JavaScript dan menginisialisasi koneksi ke server. Untuk melihat contoh, jalankan Node: node index.js.

Selanjutnya, di browser, masukkan localhost : 3000. Halaman akan tetap kosong, tetapi jika Anda melihat konsol saat Node berjalan, Anda akan melihat dua pesan:

Server HTTP dimulai pada port 3000
Koneksi klien diterima

Sekarang kita telah berhasil terhubung, mari kita lanjutkan pekerjaan. Misalnya, mengirim pesan ke klien dari server. Kemudian, ketika klien menerimanya, pesan respons akan dikirim:

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

Fungsi io.on sebelumnya telah diperbarui untuk menyertakan beberapa baris kode baru. Yang pertama, socket.emit, mengirim pesan ke klien. sendToClient adalah nama acara tersebut. Dengan menamai acara, Anda mendapatkan kemampuan untuk mengirim berbagai jenis pesan, sehingga klien akan dapat menafsirkannya secara berbeda. Pembaruan lainnya adalah socket.on, yang juga memiliki nama acara: acceptFromClient. Semua ini menciptakan fungsi yang menerima data dari klien. Dalam hal ini, mereka juga direkam di jendela konsol.

Langkah-langkah yang diselesaikan menyelesaikan persiapan server. Sekarang dapat menerima dan mengirim data dari klien yang terhubung.

Mari kita akhiri contoh ini dengan memperbarui klien dengan menerima acara sendToClient. Ketika suatu peristiwa diterima, suatu tanggapan diterima dari Klien.

Ini menyimpulkan bagian JavaScript dari HTML. Di index.html tambahkan:

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

Menggunakan variabel soket bawaan, kami mendapatkan logika serupa di server dengan fungsi socket.on. Klien mendengarkan acara sendToClient. Segera setelah klien terhubung, server mengirim pesan ini. Klien, menerimanya, merekam acara di konsol browser. Setelah itu, klien menggunakan socket.emit yang sama dengan server yang digunakan untuk mengirim acara asli. Dalam hal ini, klien mengirim acara FromClient yang diterima ke server. Ketika dia menerima pesan, pesan itu akan dicatat di jendela konsol.

Coba sendiri. Pertama, di konsol, luncurkan aplikasi Node Anda: node index.js. Kemudian muat localhost : 3000 di browser.

Periksa konsol browser Anda dan Anda akan melihat yang berikut ini di log JSON: {hello: "world"}

Kemudian, ketika aplikasi Node sedang berjalan, Anda akan melihat yang berikut:

Server HTTP dimulai pada port 3000
Koneksi klien diterima
{my: 'data'}

Baik klien dan server dapat menggunakan data JSON untuk melakukan tugas tertentu. Mari kita lihat bagaimana Anda dapat bekerja dengan data kompetisi secara real time.

Informasi Persaingan


Setelah kami memahami prinsip-prinsip pengiriman dan penerimaan data oleh klien dan server, ada baiknya mencoba memastikan bahwa pembaruan dilakukan secara real time. Saya menggunakan data kompetisi, meskipun hal yang sama dapat dilakukan tidak hanya dengan informasi olahraga. Tetapi karena kita sedang bekerja dengannya, kita perlu menemukan sumbernya. Mereka akan melayani MySportsFeeds . Layanan ini dibayar - dari $ 1 per bulan, perlu diingat.

Setelah akun Anda diatur, Anda dapat memulai dengan API mereka. Anda dapat menggunakan paket NPM untuk ini: npm instal mysportsfeeds-node --save.

Setelah menginstal paket, kami menghubungkan 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 }); 

Pada contoh di atas, ganti data saya dengan milik Anda.

Kode membuat panggilan API untuk mendapatkan hasil kompetisi NHL hari ini. Variabel fordate adalah yang menentukan tanggal. Saya juga menggunakan kekuatan dan true untuk mendapatkan kembali data bahkan jika hasilnya sama.

Dengan pengaturan saat ini, hasil panggilan API ditulis ke file teks. Dalam contoh terakhir, kami akan mengubah ini; untuk tujuan demonstrasi, file hasil dapat dilihat dalam editor teks untuk memahami konten jawabannya. Dalam hasil kami, kami melihat objek tabel hasil. Objek ini berisi larik yang disebut gameScore. Ini menyimpan hasil dari setiap permainan. Setiap objek pada gilirannya berisi objek anak yang disebut game. Objek ini memberikan informasi tentang siapa yang bermain.

Di luar objek gim, ada beberapa variabel yang menampilkan kondisi gim saat ini. Perubahan data tergantung pada hasilnya. Ketika permainan belum dimulai, variabel digunakan yang memberikan informasi kapan ini akan terjadi. Ketika permainan telah dimulai, data tambahan diberikan pada hasil, termasuk informasi tentang periode apa sekarang dan berapa banyak waktu yang tersisa. Untuk lebih memahami apa yang dipertaruhkan, mari kita beralih ke bagian selanjutnya.

Pembaruan waktu nyata


Kami memiliki semua potongan puzzle, jadi mari kita rakit! Sayangnya, MySportsFeeds memiliki dukungan terbatas untuk mengeluarkan data, jadi Anda harus terus meminta informasi. Ada poin positif di sini: kita tahu bahwa data berubah hanya sekali setiap 10 menit, jadi tidak perlu terlalu sering menginterogasi layanan. Data yang diterima dapat dikirim dari server ke semua klien yang terhubung.

Untuk mendapatkan data yang diperlukan, saya akan menggunakan fungsi setInterval JavaScript, yang memungkinkan Anda mengakses API (dalam kasus saya) setiap 10 menit untuk menemukan pembaruan. Ketika data tiba, acara dikirim ke semua klien yang terhubung. Hasilnya kemudian diperbarui melalui JavaScript di browser web.

MySportsFeeds juga dipanggil ketika aplikasi Node diluncurkan pertama kali. Hasil akan digunakan untuk klien yang terhubung sebelum interval 10 menit pertama. Informasi tentang ini disimpan dalam variabel global. Ini, pada gilirannya, diperbarui sebagai bagian dari survei interval. Ini memastikan bahwa masing-masing klien akan memiliki hasil yang relevan.

Agar semuanya baik-baik saja di file index.js utama, saya membuat file baru bernama data.js. Ini berisi fungsi yang diekspor dari index.js yang membuat panggilan sebelumnya ke MySportsFeeds API. Berikut ini isi lengkap file ini:

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

Fungsi getData diekspor dan mengembalikan hasil panggilan. Mari kita lihat apa yang kita miliki di file 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); 

Tujuh baris pertama kode di atas menginisialisasi pustaka yang diperlukan dan memanggil variabel global data terbaru. Daftar perpustakaan terbaru yang digunakan adalah: Express, Http Server, dibuat menggunakan Express, Socket.io plus file data.js yang baru saja dibuat.

Dengan mempertimbangkan kebutuhan, aplikasi mengisi data terbaru (data terbaru) untuk klien yang terhubung saat server pertama kali dimulai:

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

Beberapa baris berikutnya mengatur jalur untuk halaman utama situs (dalam kasus kami localhost : 3000 /) dan memerintahkan server HTTP untuk mendengarkan pada port 3000.

Socket.io kemudian dikonfigurasi untuk mencari koneksi baru. Ketika mereka terdeteksi, server mengirimkan data acara dengan konten dari variabel Data terbaru.

Dan akhirnya, potongan kode terakhir menciptakan interval pemungutan suara yang diperlukan. Ketika terdeteksi, variabel data terbaru diperbarui dengan hasil panggilan API. Data ini kemudian melewati acara yang sama untuk semua klien.

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

Seperti yang kita lihat, ketika klien menghubungkan dan suatu peristiwa didefinisikan, itu dikeluarkan dengan variabel soket. Ini memungkinkan Anda untuk mengirim acara ke klien terhubung tertentu. Di dalam interval, global io digunakan untuk mengirimkan acara. Ini mengirim data ke semua klien. Pengaturan server selesai.

Bagaimana kelihatannya


Sekarang mari kita bekerja di frontend klien. Dalam contoh awal, saya membuat file base index.html, yang membuat koneksi dengan klien untuk merekam peristiwa server dan mengirimkannya. Sekarang saya akan memperluas kemampuan file.

Karena server mengirimi kami objek JSON, saya akan menggunakan jQuery dan ekstensi jQuery yang disebut JsRender. Ini adalah pustaka template. Ini akan memungkinkan saya untuk membuat template HTML yang akan digunakan untuk menampilkan konten dari setiap game NHL dengan cara yang nyaman. Sekarang Anda bisa melihat luasnya kemampuannya. Kode berisi lebih dari 40 baris, jadi saya akan membaginya menjadi beberapa bagian yang lebih kecil, dan pada akhirnya saya akan menampilkan semua konten HTML.

Inilah yang digunakan untuk menampilkan data game:

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

Templat didefinisikan menggunakan tag skrip. Ini berisi pengidentifikasi template dan jenis skrip khusus yang disebut teks / x-jsrender. Templat menentukan wadah div untuk setiap game yang berisi kelas game untuk menerapkan gaya dasar tertentu. Di dalam div ini adalah awal dari templat.

Div berikutnya menampilkan tim tamu dan tim tuan rumah. Ini diterapkan dengan menggabungkan nama kota dan nama tim dengan objek game dari data MySportsFeeds.

{{: game.awayTeam.City}} adalah cara saya mendefinisikan objek yang akan diganti dengan nilai fisik saat merender template. Sintaks ini didefinisikan oleh perpustakaan JsRender.

Saat game belum dimainkan, akan muncul garis di mana game dimulai dengan {{: game.time}}.

Sampai permainan selesai, skor saat ini ditampilkan: {{: awayScore}} - {{: homeScore}}. Dan, akhirnya, trik kecil yang akan menentukan periode apa sekarang, dan untuk memperjelas apakah ada jeda saat ini.

Jika variabelIntermission saat ini muncul dalam hasil, maka saya menggunakan fungsi I, yang didefinisikan oleh ordinal_suffix_of, yang mengubah nomor periode menjadi teks berikut: Istirahat 1 (2, 3, dll.).

Ketika tidak ada istirahat, saya mencari nilai currentPeriod. Ordinal_suffix_of juga digunakan di sini untuk menunjukkan bahwa permainan berada dalam periode ke-1 (ke-2, ke-3, dll.).

Selain itu, fungsi lain yang saya definisikan sebagai time_left digunakan untuk mengkonversi jumlah detik yang tersisa hingga akhir periode. Misalnya: 10:12.

Bagian terakhir dari kode menampilkan hasil akhir saat permainan selesai.

Berikut ini adalah contoh tampilannya ketika ada daftar campuran game yang sudah selesai, game yang belum selesai, dan game yang belum dimulai (saya bukan desainer yang sangat baik, sehingga hasilnya terlihat seperti seharusnya ketika pengembang membuat custom) antarmuka aplikasi do-it-yourself):

gambar

Berikutnya adalah cuplikan JavaScript yang membuat socket, fungsi helper ordinal_suffix_of dan time_left, dan variabel yang merujuk ke templat jQuery yang dihasilkan.

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

Fragmen terakhir adalah kode untuk menerima acara soket dan membuat template:

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

Saya memiliki pembatas dengan id data. Hasil dari templat render (tmpl.render) menulis HTML ke wadah ini. Yang benar-benar keren - perpustakaan JsRender dapat mengambil array data, dalam hal ini data.scoreboard.gameScore, yang diputar melalui setiap elemen dalam array dan membuat satu game per elemen.

Ini adalah versi final yang dijanjikan di atas, tempat HTML dan JavaScript disatukan:

 <!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> 

Sekarang adalah waktu untuk meluncurkan aplikasi Node dan membuka localhost : 3000 untuk melihat hasilnya!

Setiap X menit, server mengirim acara ke klien. Klien, pada gilirannya, akan menggambar ulang elemen game dengan data yang diperbarui. Karena itu, ketika Anda membiarkan situs terbuka, hasil permainan akan terus diperbarui.

Kesimpulan


Produk akhir menggunakan Socket.io untuk membuat server yang terhubung dengan klien. Server mengambil data dan mengirimkannya ke klien. Ketika seorang pelanggan menerima data, ia dapat memperbarui hasilnya secara bertahap. Ini mengurangi beban di server karena klien hanya bertindak ketika menerima acara dari server.

Server dapat mengirim pesan ke klien, dan klien, pada gilirannya, ke server. Ketika server menerima pesan, ia melakukan pemrosesan data.

Aplikasi obrolan bekerja dengan cara yang hampir sama. Server akan menerima pesan dari klien, dan kemudian mengirimkan semua data ke klien yang terhubung untuk menunjukkan bahwa seseorang telah mengirim pesan baru.

Saya harap Anda menikmati artikel ini, karena ketika saya mengembangkan aplikasi olahraga waktu-nyata ini, saya hanya mendapatkan segunung kesenangan yang ingin saya bagikan dengan pengetahuan saya. Bagaimanapun, hoki adalah salah satu olahraga favorit saya!

Source: https://habr.com/ru/post/id413957/


All Articles