Jonli chat, oshxonadagi buyurtma ekrani, birja kursi yoki onlayn o'yin kabi ilovalar ma'lumotni sahifani yangilamasdan, shu zahoti ko'rsatishi kerak. Buning uchun ko'pincha WebSocket texnologiyasi tanlanadi. U brauzer va server o'rtasida bir marta ochiladigan va uzoq vaqt ochiq turadigan ikki tomonlama kanal yaratadi. Muammo shundaki, dasturchi kompyuterida a'lo ishlagan WebSocket ilova oddiy hostingga yuklanganda ko'pincha umuman ulanmaydi. Quyida sababini va to'g'ri yechimni ko'rib chiqamiz.
WebSocket oddiy HTTP so'rovdan nimasi bilan farq qiladi
Oddiy sayt "so'rov va javob" tamoyilida ishlaydi: brauzer sahifani so'raydi, server PHP skriptni bajaradi, javob qaytaradi va ulanish tugaydi. WebSocket ham HTTP so'rov bilan boshlanadi, lekin bu so'rovda Upgrade: websocket sarlavhasi bo'ladi. Server rozi bo'lsa, 101 Switching Protocols javobini qaytaradi va shundan keyin o'sha TCP ulanish yopilmaydi. Endi server ham, brauzer ham istalgan paytda bir-biriga xabar yubora oladi.
Demak, WebSocket uchun serverda doimiy ishlab turadigan, ulanishlarni ushlab turadigan jarayon kerak. Bu odatda Node.js, Python yoki Go'da yozilgan alohida dastur bo'lib, u o'z portini tinglaydi va minglab ochiq ulanishlarni xotirada saqlaydi. Aynan shu talab oddiy hosting bilan to'qnash keladi.
Nega oddiy hostingda WebSocket ishlamaydi
Oddiy (virtual) hostingda bitta server ko'plab mijozlar o'rtasida bo'lingan. U yerda PHP skriptlar so'rov kelganda ishga tushadi va bir necha soniyada tugashi kerak, bajarilish vaqti cheklangan. Mijozga o'z portini ochish va unga tashqaridan ulanish imkoni odatda berilmaydi, doimiy ishlab turadigan fon jarayonlari esa resurs cheklovlari sababli to'xtatilishi mumkin. Veb-server sozlamalari ham mijozning qo'lida emas, shuning uchun Upgrade sarlavhasini ichki dasturga uzatadigan proksi qoidasini qo'shib bo'lmaydi.
Shu sababli PHP'da yozilgan WebSocket kutubxonalari (masalan, Ratchet) ham oddiy hostingda barqaror ishlamaydi: ular ham doimiy jarayon va ochiq port talab qiladi. Agar loyiha oddiy hostingda qolishi shart bo'lsa, real vaqtga yaqin ishlashni boshqa yo'l bilan qilish kerak.
Oddiy hostingda qolish kerak bo'lsa: muqobil yo'llar
Ko'p hollarda "jonli" ko'rinish uchun WebSocket aslida shart emas. Masalan, maktab saytida dars jadvali o'zgarishlari yoki internet-do'kondagi buyurtma holati uchun brauzer har 5-10 soniyada kichik JSON so'rov yuborsa yetarli. Bu davriy so'rov (polling) usuli oddiy hostingda muammosiz ishlaydi, chunki har bir so'rov qisqa va odatiy PHP skript sifatida bajariladi.
long polling va Server-Sent Events kabi usullar ham bor, lekin ular har bir foydalanuvchi uchun PHP jarayonni uzoq vaqt band qiladi. Bir vaqtda o'nlab foydalanuvchi ulansa, hostingdagi jarayonlar limiti tezda tugaydi va sayt boshqa so'rovlarga javob bermay qo'yadi. Shuning uchun oddiy hostingda qisqa davriy so'rov eng xavfsiz tanlov, haqiqiy WebSocket esa alohida serverda ishlatiladi.
Misol: restoran oshxonasi uchun buyurtma ekrani
Toshkentdagi restoran ofitsiantlar planshetda buyurtma kiritishini va u oshxonadagi ekranda darhol paydo bo'lishini xohlaydi. Bir-ikki soniyalik kechikish ham oshpazlar uchun noqulay, shuning uchun bu yerda WebSocket o'rinli. Asosiy sayt va menyu oddiy hostingda qolishi mumkin, WebSocket serveri esa kichik VPS'da ishlaydi. Quyida Node.js va ws kutubxonasi asosidagi minimal server keltirilgan: u har bir kelgan buyurtmani barcha ulangan ekranlarga tarqatadi va uzilib qolgan ulanishlarni tozalaydi.
// server.js (npm install ws)
const { WebSocketServer } = require('ws');
const wss = new WebSocketServer({ host: '127.0.0.1', port: 8080 });
wss.on('connection', (ws) => {
ws.isAlive = true;
ws.on('pong', () => { ws.isAlive = true; });
ws.on('message', (data) => {
// yangi buyurtmani barcha ulangan ekranlarga tarqatish
for (const client of wss.clients) {
if (client.readyState === 1) client.send(data.toString());
}
});
});
// 30 soniyada bir "tirikmi" tekshiruvi
setInterval(() => {
for (const ws of wss.clients) {
if (!ws.isAlive) { ws.terminate(); continue; }
ws.isAlive = false;
ws.ping();
}
}, 30000);
E'tibor bering, server faqat 127.0.0.1 manzilini tinglaydi, ya'ni tashqaridan to'g'ridan-to'g'ri ulanib bo'lmaydi. Tashqi ulanishlarni Nginx qabul qiladi, shifrlaydi va ichkariga uzatadi.
Nginx orqali wss:// ulanishni sozlash
Agar sayt HTTPS orqali ochilsa, brauzer shifrlanmagan ws:// ulanishni aralash kontent sifatida bloklaydi, shuning uchun wss:// kerak. Eng qulay yo'l: Let's Encrypt sertifikati Nginx'da turadi, Nginx esa /ws/ manziliga kelgan so'rovlarni ichki Node.js serveriga proksi qiladi. Muhim nuqta Upgrade va Connection sarlavhalarini uzatish va HTTP/1.1 versiyasini yoqishdir, aks holda Nginx ulanishni oddiy so'rov deb hisoblaydi.
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 443 ssl;
server_name misol.uz;
# ssl_certificate ... (Let's Encrypt sertifikati)
location /ws/ {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_read_timeout 3600s;
}
}
proxy_read_timeout qiymati ham ahamiyatli. Standart holatda Nginx 60 soniya davomida ma'lumot kelmasa ulanishni yopadi. Yuqoridagi serverdagi 30 soniyalik ping bu muddatdan oldin trafik hosil qiladi, uzunroq timeout esa qo'shimcha zaxira beradi. Brauzer tomonida ulanish quyidagicha ochiladi:
const sock = new WebSocket('wss://misol.uz/ws/');
sock.onmessage = (e) => showOrder(JSON.parse(e.data));
sock.onclose = () => setTimeout(() => location.reload(), 3000);
Ulanishni tekshirish va serverni doimiy ishlatish
Sozlama to'g'riligini brauzerdan oldin curl bilan tekshirish mumkin. Javobning birinchi qatorida HTTP/1.1 101 Switching Protocols chiqsa, Nginx ham, Node.js ham to'g'ri ishlayapti. Agar 400 yoki 502 chiqsa, avval Node.js jarayoni ishlayotganini, keyin Nginx konfiguratsiyasidagi sarlavhalarni tekshiring.
curl -i -N \
-H "Connection: Upgrade" -H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" \
-H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
https://misol.uz/ws/
Node.js serverni terminalda qo'lda ochib qoldirmang: SSH sessiyasi yopilganda u ham to'xtashi mumkin. Uni systemd xizmati qilib qo'ying, shunda server qayta yuklanganda ham, dastur yiqilganda ham avtomatik ko'tariladi.
# /etc/systemd/system/ws-kitchen.service
[Service]
WorkingDirectory=/opt/ws-kitchen
ExecStart=/usr/bin/node server.js
Restart=always
[Install]
WantedBy=multi-user.target
sudo systemctl enable --now ws-kitchen
sudo nginx -t && sudo systemctl reload nginx
Bir nechta jarayon va masshtablash haqida
Bitta Node.js jarayon o'rtacha yuklamadagi ilova uchun yetarli. Agar keyinroq bir nechta jarayon yoki bir nechta server ishlatsangiz, bir jarayonga ulangan mijoz boshqa jarayondagi xabarni ko'rmaydi. Bunday holda jarayonlar xabarlarni umumiy navbat orqali almashishi kerak, ko'pincha bu vazifani Redis'ning pub/sub imkoniyati bajaradi.
Qisqasi, WebSocket uchun doimiy jarayon, ochiq port va veb-server sozlamalariga kirish kerak, bularning barchasi esa VPS'da bor. sayt.uz kabinetidan VPS buyurtma qilib, asosiy saytni hostingda qoldirgan holda faqat real vaqt qismini alohida serverga chiqarish mumkin.