Cerita-cerita horor sering bercerita tentang teknologi websocket, misalnya, bahwa itu tidak didukung oleh browser web, atau bahwa penyedia / admin menekan lalu lintas websocket - oleh karena itu tidak dapat digunakan dalam aplikasi. Di sisi lain, pengembang tidak selalu melihat jebakan yang dimiliki teknologi websocket, seperti teknologi lainnya. Mengenai batasan yang dituduhkan, saya akan katakan segera bahwa 96,8% browser web mendukung teknologi websocket saat ini. Anda dapat mengatakan bahwa sisa 3,2% terlalu banyak, ini adalah jutaan pengguna. Saya sepenuhnya setuju dengan Anda. Hanya semuanya yang diketahui perbandingannya. XmlHttpRequest yang sama, yang telah digunakan semua orang di Ajax selama bertahun-tahun, mendukung 97,17% browser web (tidak lebih, kan?), Dan mengambil secara umum, 93,08% browser web. Tidak seperti websocket, persentase seperti itu (dan sebelumnya itu bahkan lebih rendah) tidak menghentikan siapa pun untuk waktu yang lama ketika menggunakan teknologi Ajax. Jadi menggunakan fallback pada polling panjang saat ini tidak masuk akal. Jika hanya karena browser web yang tidak mendukung websocket adalah browser web yang sama yang tidak mendukung XmlHttpRequest, dan pada kenyataannya tidak akan terjadi penurunan.
Kisah horor kedua, pelarangan websocket dari penyedia atau admin jaringan perusahaan, juga tidak masuk akal, karena sekarang semua orang menggunakan protokol https, dan tidak mungkin untuk memahami bahwa koneksi websocket terbuka (tanpa melanggar https).
Adapun batasan nyata dan cara untuk mengatasinya, saya akan kirim dalam posting ini, pada contoh pengembangan area web admin aplikasi.
Jadi, objek WebSocket di browser web, sejujurnya, memiliki serangkaian metode yang sangat ringkas: kirim () dan tutup (), serta addEventListener (), hapusEventListener () dan dispatchEvent () metode yang diwarisi dari objek EventTarget. Oleh karena itu, pengembang harus menggunakan perpustakaan (biasanya) atau secara mandiri (hampir tidak mungkin) untuk menyelesaikan beberapa masalah.
Mari kita mulai dengan tugas yang paling bisa dimengerti. Koneksi ke server terputus secara berkala. Menghubungkan kembali cukup mudah. Tetapi jika Anda ingat bahwa pesan dari klien dan server terus berjalan saat ini, semuanya menjadi segera dan jauh lebih rumit. Secara umum, pesan dapat hilang jika mekanisme konfirmasi untuk pesan yang diterima tidak disediakan, atau dikirim kembali (bahkan beberapa kali) jika mekanisme konfirmasi diberikan, tetapi kegagalan terjadi tepat pada saat setelah penerimaan dan sebelum pesan dikonfirmasi.
Jika Anda memerlukan pengiriman pesan terjamin dan / atau pengiriman pesan tanpa perlu, maka ada protokol khusus untuk mengimplementasikan ini, misalnya, AMQP dan MQTT, yang bekerja dengan transportasi websocket. Tetapi hari ini kita tidak akan mempertimbangkannya.
Sebagian besar pustaka untuk bekerja dengan dukungan websocket transparan untuk pemrogram menghubungkan kembali ke server. Menggunakan perpustakaan seperti itu selalu lebih dapat diandalkan daripada mengembangkan implementasi Anda.
Selanjutnya, Anda perlu mengimplementasikan infrastruktur untuk mengirim dan menerima pesan asinkron. Untuk melakukan ini, gunakan event handler onmessage "telanjang" tanpa ikatan tambahan, tugas tanpa pamrih. Infrastruktur semacam itu dapat, misalnya, panggilan prosedur jarak jauh (RPC). Id id diperkenalkan ke dalam spesifikasi json-rpc, khusus untuk bekerja dengan transport websocket, yang memungkinkan Anda untuk memetakan panggilan prosedur jarak jauh oleh klien ke pesan respon dari server web. Saya lebih suka protokol ini daripada semua kemungkinan lain, tetapi sejauh ini saya belum menemukan implementasi protokol ini yang sukses untuk bagian server pada node.js.
Dan akhirnya, Anda perlu menerapkan penskalaan. Ingatlah bahwa koneksi antara klien dan server terjadi secara berkala. Jika kekuatan satu server tidak cukup bagi kami, kami dapat meningkatkan beberapa server lagi. Dalam hal ini, setelah koneksi terputus, koneksi ke server yang sama tidak dijamin. Biasanya, server redis atau sekelompok server redis digunakan untuk mengoordinasikan beberapa server websocket.
Dan, sayangnya, cepat atau lambat kita akan mengalami kinerja sistem, karena kemampuan node.js dalam jumlah koneksi websocket yang secara bersamaan terbuka (jangan bingung dengan kinerja) secara signifikan lebih rendah daripada dengan server khusus seperti antrian pesan dan broker. Dan perlunya pertukaran silang antara semua contoh server websocket melalui cluster server redis, setelah beberapa titik kritis, tidak akan memberikan peningkatan yang signifikan dalam jumlah koneksi terbuka. Cara untuk mengatasi masalah ini adalah dengan menggunakan server khusus, seperti AMQP dan MQTT, yang berfungsi, termasuk dengan transportasi websocket. Tetapi hari ini kita tidak akan mempertimbangkannya.
Seperti yang dapat Anda lihat dari daftar tugas yang terdaftar, bersepeda sambil bekerja dengan websocket sangat memakan waktu, dan bahkan tidak mungkin jika Anda perlu skala solusi ke beberapa server websocket.
Oleh karena itu, saya mengusulkan untuk mempertimbangkan beberapa perpustakaan populer yang mengimplementasikan pekerjaan dengan websocket.
Saya akan segera mengecualikan dari perpustakaan yang menerapkan mundur secara eksklusif pada moda transportasi yang sudah usang, karena hari ini fungsi ini tidak relevan, dan perpustakaan yang menerapkan fungsionalitas yang lebih luas, sebagai aturan, juga menerapkan mundur pada moda transportasi yang usang.
Saya akan mulai dengan perpustakaan paling populer - socket.io. Sekarang Anda dapat mendengar pendapatnya, kemungkinan besar adil, bahwa perpustakaan ini lambat dan mahal dalam hal sumber daya. Kemungkinan besar, dan itu bekerja lebih lambat dari websocket asli. Namun, saat ini perpustakaan ini merupakan perpustakaan yang paling berkembang. Dan, sekali lagi, ketika bekerja dengan websocket, faktor pembatas utama bukanlah kecepatan, tetapi jumlah koneksi yang terbuka secara simultan dengan klien unik. Dan pertanyaan ini sebaiknya diselesaikan dengan membuat koneksi dengan klien ke server khusus.
Jadi, soket.io mengimplementasikan pemulihan yang andal ketika memutuskan sambungan dari server dan melakukan penskalaan menggunakan server atau sekelompok server redis. socket.io, pada kenyataannya, mengimplementasikan protokol pengiriman pesannya sendiri, yang memungkinkan Anda untuk mengimplementasikan pengiriman pesan antara klien dan server tanpa terikat dengan bahasa pemrograman tertentu.
Fitur yang menarik dari socket.io adalah konfirmasi pemrosesan acara, di mana objek sewenang-wenang dapat dikembalikan dari server ke klien, yang memungkinkan untuk panggilan prosedur jarak jauh (meskipun tidak sesuai dengan standar json-rpc).
Juga, pendahuluan, saya memeriksa dua perpustakaan yang lebih menarik, yang akan saya diskusikan secara singkat di bawah ini.
Perpustakaan
faye faye.jcoglan.com . Ini mengimplementasikan protokol bayeux, yang dikembangkan dalam proyek CometD dan mengimplementasikan langganan / distribusi pesan ke saluran pesan. Proyek ini juga mendukung penskalaan menggunakan server atau sekelompok server redis. Upaya untuk menemukan cara menerapkan RPC tidak berhasil karena tidak cocok dengan skema protokol bayeux.
Dalam proyek socketcluster
socketcluster.io , penekanannya adalah pada penskalaan server websocket. Pada saat yang sama, cluster server websocket tidak dibuat atas dasar server redis, seperti pada dua pustaka yang disebutkan pertama, tetapi atas dasar node.js. Dalam hal ini, ketika mengerahkan kluster, perlu untuk meluncurkan infrastruktur perantara dan pekerja yang agak rumit.
Sekarang mari kita beralih ke implementasi RPC di socket.io. Seperti yang saya katakan di atas, perpustakaan ini telah menerapkan kemampuan untuk bertukar objek antara klien dan server:
import io from 'socket.io-client'; const socket = io({ path: '/ws', transports: ['websocket'] }); const remoteCall = data => new Promise((resolve, reject) => { socket.emit('remote-call', data, (response) => { if (response.error) { reject(response); } else { resolve(response); } }); });
const server = require('http').createServer(); const io = require('socket.io')(server, { path: '/ws' }); io.on('connection', (socket) => { socket.on('remote-call', async (data, callback) => { handleRemoteCall(socket, data, callback); }); }); server.listen(5000, () => { console.log('dashboard backend listening on *:5000'); }); const handleRemoteCall = (socket, data, callback) => { const response =... callback(response) }
Ini adalah skema umum. Sekarang kita akan mempertimbangkan masing-masing bagian dalam kaitannya dengan aplikasi tertentu. Untuk membangun panel admin, saya menggunakan perpustakaan react-admin
github.com/marmelab/react-admin . Pertukaran data dengan server di perpustakaan ini diimplementasikan menggunakan penyedia data, yang memiliki skema yang sangat nyaman, hampir semacam standar. Misalnya, untuk mendapatkan daftar, metode ini disebut:
dataProvider( 'GET_LIST', ' ', { pagination: { page: {int}, perPage: {int} }, sort: { field: {string}, order: {string} }, filter: { Object } }
Metode ini dalam respons asinkron mengembalikan objek:
{ data: [ ], total: }
Saat ini terdapat sejumlah implementasi penyedia data reaksi-admin yang mengesankan untuk berbagai server dan kerangka kerja (mis. Firebase, spring boot, graphql, dll.). Dalam kasus RPC, implementasi ternyata yang paling ringkas, karena objek ditransfer dalam bentuk aslinya ke panggilan fungsi emit:
import io from 'socket.io-client'; const socket = io({ path: '/ws', transports: ['websocket'] }); export default (action, collection, payload = {}) => new Promise((resolve, reject) => { socket.emit('remote-call', {action, collection, payload}, (response) => { if (response.error) { reject(response); } else { resolve(response); } }); });
Sayangnya, sedikit lebih banyak pekerjaan yang harus dilakukan di sisi server. Untuk mengatur pemetaan fungsi yang menangani panggilan jarak jauh, router yang mirip dengan express.js dikembangkan. Hanya alih-alih tanda tangan middleware (req, res, next) implementasi bergantung pada tanda tangan (socket, payload, callback). Hasilnya, kita semua mendapatkan kode yang biasa:
const Router = require('./router'); const router = Router(); router.use('GET_LIST', (socket, payload, callback) => { const limit = Number(payload.pagination.perPage); const offset = (Number(payload.pagination.page) - 1) * limit return callback({data: users.slice(offset, offset + limit ), total: users.length}); }); router.use('GET_ONE', (socket, payload, callback) => { return callback({ data: users[payload.id]}); }); router.use('UPDATE', (socket, payload, callback) => { users[payload.id] = payload.data return callback({ data: users[payload.id] }); }); module.exports = router; const users = []; for (let i = 0; i < 10000; i++) { users.push({ id: i, name: `name of ${i}`}); }
Rincian implementasi router dapat ditemukan
di repositori proyek.Yang tersisa adalah menetapkan penyedia untuk komponen Admin:
import React from 'react'; import { Admin, Resource, EditGuesser } from 'react-admin'; import UserList from './UserList'; import dataProvider from './wsProvider'; const App = () => <Admin dataProvider={dataProvider}> <Resource name="users" list={UserList} edit={EditGuesser} /> </Admin>; export default App;
Tautan yang bermanfaat
1.www.infoq.com/articles/Web-Sockets-Proxy-Serversapapacy@gmail.com
14 Juli 2019