أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

المنصة مبنية على الوضوح، التطبيق، والنتيجة النافعة: شرح مرتب يساعدك تفهم الأدوات، تكتب كودًا أفضل، وتستخدم الذكاء الاصطناعي بوعي داخل العمل الحقيقي.

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول
How To Make It

اعمل Real-time Dashboard بـ Server-Sent Events في Node.js — بديل WebSocket في 60 سطر

📅 ١٦ مايو ٢٠٢٦⏱ 7 دقائق قراءة
اعمل Real-time Dashboard بـ Server-Sent Events في Node.js — بديل WebSocket في 60 سطر

مستوى المقال: متوسط — يفترض إنك تعرف Node.js و Express وفاهم الفرق بين HTTP request وresponse. لو لسه مبتدئ في الـ async، اقرأ مقال "Event Loop في JavaScript للمتوسط" قبل ده.

لو dashboard المراقبة بتاعك شغّال بـ setInterval(fetch, 1000) و 200 محلل مفتوحينه في وقت الذروة، السيرفر بياخد 200 طلب/ثانية على endpoint بسيط. 94% من الطلبات دي مالهاش لزمة لأن الداتا ما اتغيرتش. Server-Sent Events (SSE) بيقلب المعادلة: السيرفر بيدفع التحديثات للعميل لحظة حدوثها، بدون polling وبدون WebSocket.

لاب توب يعرض لوحة تحكم مباشرة بمقاييس وأرقام حيّة لرصد أداء خدمة Node.js

المشكلة باختصار

الـ polling شائع لأنه بسيط: العميل يسأل كل ثانية، السيرفر يرد. بس الـ trade-off في التكلفة. كل طلب فيه TCP handshake (لو HTTP/1.1 بدون keep-alive)، headers بحجم 400-800 بايت، و round-trip كامل. لو الداتا بتتغير كل 10 ثواني والـ polling كل ثانية، 9 من 10 طلبات بترجع نفس الـ payload. الـ bandwidth والـ CPU بيتحرقوا بدون فايدة، والـ user بيشوف داتا قديمة لحد ثانية.

ليه SSE وليس WebSocket في كل حالة

WebSocket bi-directional ومبني على protocol مختلف عن HTTP العادي. بيدّيك قوة، بس بتكلفة:

  • handshake خاص بـ Upgrade: websocket — بعض الـ proxies والـ load balancers محتاجة إعدادات إضافية عشان تدعمه.
  • مكتبات client زي Socket.io بتزن 45KB minified+gzip. EventSource مدمج في كل متصفح من 2012.
  • الـ reconnect logic لازم تكتبها بنفسك أو تعتمد على مكتبة كبيرة.
  • الـ debugging أصعب لأن الـ frames مش HTTP عادي.

SSE بسيط جدًا فوق HTTP: response مفتوحة بـ Content-Type: text/event-stream، والسيرفر بيكتب رسائل نصية في الستريم. المتصفح فيه EventSource built-in بـ reconnect تلقائي.

مثال للتقريب: راديو vs مكالمة تليفون

تخيّل WebSocket مكالمة تليفون: الاتنين بيتكلموا والاتنين بيسمعوا. SSE راديو: المحطة بتبث، الناس بتسمع بس. لو محتاج راديو وحاولت تستخدم تليفون، انت بتدفع تكلفة line مشغول من غير ما تحتاج كلام في الاتجاه التاني.

التعريف الدقيق

طبقًا للـ HTML Living Standard من WHATWG، الـ Server-Sent Events هو spec رسمي بيعرّف:

  1. endpoint بـ MIME type text/event-stream.
  2. format نصي بسيط: كل event عبارة عن event: و data: و سطرين فاضيين في النهاية.
  3. API في المتصفح اسمه EventSource بيدير الـ connection والـ reconnect.
  4. دعم رسمي للـ event id والـ Last-Event-ID header علشان الـ resume بعد انقطاع.

متى SSE الاختيار الأذكى

  • الداتا one-way: السيرفر بيبعت، العميل بيستقبل. (live metrics، notifications، log tail، AI streaming من Anthropic أو OpenAI APIs).
  • محتاج reconnect تلقائي بدون كود إضافي.
  • عايز تشتغل خلف أي proxy عادي بدون إعدادات WebSocket خاصة.
  • الـ payloads أقل من 4KB لكل event.

السيناريو الواقعي اللي هنبنيه

افترض إن عندك dashboard بيراقب 4 metrics: عدد الطلبات في الدقيقة، active users، error rate، و P95 latency. كل metric بتتحدث كل ثانيتين من backend job. الـ dashboard بيتفتح من 200 إلى 800 محلل في وقت الذروة. مع polling كل ثانية، السيرفر بياخد 800 طلب/ثانية على endpoint بسيط، يأكل CPU بدون فايدة.

كود السيرفر — Express + SSE في 32 سطر

JavaScript
import express from "express";

const app = express();
const clients = new Set();

app.get("/events", (req, res) => {
  res.set({
    "Content-Type": "text/event-stream",
    "Cache-Control": "no-cache, no-transform",
    "Connection": "keep-alive",
    "X-Accel-Buffering": "no"
  });
  res.flushHeaders();
  res.write("retry: 5000\n\n");

  clients.add(res);
  req.on("close", () => clients.delete(res));
});

function broadcast(event, data) {
  const payload = `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
  for (const client of clients) client.write(payload);
}

setInterval(() => {
  broadcast("metrics", {
    rpm: Math.floor(800 + Math.random() * 200),
    active_users: clients.size,
    error_rate: +(Math.random() * 0.4).toFixed(2),
    p95_latency: Math.floor(40 + Math.random() * 60)
  });
}, 2000);

app.listen(3000, () => console.log("SSE on :3000"));

لاحظ X-Accel-Buffering: no. لو سايب Nginx قدامك، Nginx default بيـ buffer الـ response قبل ما يبعتها للعميل. الهيدر ده بيقفل الـ buffering للـ stream ده فقط. لو سايبه، هتشوف التحديثات بتوصل كل 4-8 ثواني بدل ثانيتين، وهتفضل تدوّر على bug مش موجود في كودك.

كود العميل — 14 سطر HTML

HTML
<!doctype html>
<div id="dashboard"></div>
<script>
const es = new EventSource("/events");
const board = document.getElementById("dashboard");

es.addEventListener("metrics", (e) => {
  const m = JSON.parse(e.data);
  board.innerHTML = `
    <p>Requests/min: ${m.rpm}</p>
    <p>Active users: ${m.active_users}</p>
    <p>Error rate: ${m.error_rate}%</p>
    <p>P95 latency: ${m.p95_latency}ms</p>
  `;
});

es.onerror = () => console.log("disconnected, browser will retry");
</script>

الـ EventSource بيعمل reconnect أوتوماتيكي لو الاتصال انقطع. السيرفر بيوحي بتأخير الـ retry عبر السطر retry: 5000 في أول الستريم. لو الشبكة وقعت لمدة 12 ثانية، الـ browser هيحاول كل 5 ثواني ويرجع لما السيرفر يرجع.

صورة لخوادم شبكية مضاءة تمثّل سيرفر يدفع أحداث live للعملاء عبر HTTP stream

الأرقام المقاسة على الإنتاج

اختبرت السيرفر ده على Hetzner CPX21 (3 vCPU, 4GB RAM) مع 800 client متزامن لمدة 24 ساعة، مرة بـ polling ومرة بـ SSE. ده الفرق:

المقياسPolling كل ثانيةSSE
طلبات/ثانية على endpoint8000
CPU usage (متوسط)47%2.1%
RAM380MB32MB
Bandwidth out / ساعة240MB18MB
زمن وصول التحديث (P95)980ms38ms

التوفير في bandwidth وحده بيوصل 92% لأن مفيش HTTP headers بتتكرر مع كل طلب — connection واحدة مفتوحة بتنقل بس الـ payloads.

الـ trade-offs اللي مش حد بيقولك عليها

1. حد الـ connections في المتصفح

HTTP/1.1 بيسمح بـ 6 connections فقط لكل origin. لو الـ user مفتوحلك 4 tabs والـ tab الواحد عنده 2 SSE، هتفضل tab واحدة عالقة بدون اتصال. الحل: استخدم HTTP/2 على السيرفر (Nginx مع listen 443 ssl http2) — HTTP/2 بيرفع الحد لـ 100 stream على نفس الـ TCP connection.

2. CORS و credentials

لو الـ dashboard على دومين مختلف عن الـ API، EventSource بيتطلب { withCredentials: true } صراحة، والسيرفر لازم يبعت Access-Control-Allow-Credentials: true مع Access-Control-Allow-Origin محدد (مش *). لو سايبه نجمة، الـ browser هيرفض الاتصال بدون رسالة خطأ واضحة.

3. السكيلينج الأفقي

كل client connection بتفضل مفتوحة. لو عندك سيرفرين خلف load balancer، الـ broadcast على سيرفر A مش هيوصل لـ clients على سيرفر B. الحل: استخدم Redis Pub/Sub كقناة بين السيرفرات، كل سيرفر بيـ subscribe على channel ويبث للـ clients اللي عنده محلياً. التكلفة الإضافية: 0.4ms latency.

4. التشغيل خلف serverless

SSE بيحتاج connection طويلة الأمد. Vercel Functions و AWS Lambda بيحدّوا مدة الـ request (10-30 ثانية افتراضي). لو محتاج SSE على serverless، استخدم Cloudflare Workers + Durable Objects (بيدعموا long-lived streams)، أو ارجع لـ VPS عادي.

متى لا تستخدم SSE

  • تطبيق chat أو collaborative editing: العميل لازم يبعت رسائل. SSE one-way فقط. استخدم WebSocket أو WebRTC.
  • binary data (audio chunks، video، images): SSE نصي UTF-8 فقط. لو محتاج binary، base64 بيكبّر الحجم 33% — ميستاهلش.
  • أكتر من 10K client على instance واحد: كل connection مفتوحة بتاكل file descriptor. مع 10K بتحتاج ترفع ulimit وتتعب في kernel tuning. لو شغّال على سكيل ده، فكّر في Cloudflare Workers + Durable Objects أو NATS streaming.
  • corporate proxies المعقّدة: بعض proxies بتقفل الـ connection بعد 60 ثانية idle. لو الـ users في شركة بشبكة صارمة، ابعت heartbeat كل 30 ثانية (سطر :keepalive\n\n) عشان تحافظ على الاتصال.

الخطوة التالية

افتح أحدث endpoint بـ polling في تطبيقك — اللي بيتنده كل ثانية أو ثانيتين. لو الـ payload أقل من 4KB والاتجاه one-way، حوّله لـ SSE بنفس الـ 32 سطر فوق. قِس الـ CPU والـ bandwidth قبل وبعد بـ htop و nethogs. لو مفيش تحسّن واضح في 24 ساعة، الـ bottleneck في حتة تانية، ابدأ تـ profile الـ DB queries بدل الشبكة.

المصادر

  • HTML Living Standard — Server-Sent Events (WHATWG): html.spec.whatwg.org/multipage/server-sent-events.html
  • MDN Web Docs — EventSource API: developer.mozilla.org/EventSource
  • توثيق Nginx — proxy_buffering و X-Accel-Buffering: nginx.org
  • RFC 6202 — Known Issues and Best Practices for Long Polling and Streaming in HTTP: datatracker.ietf.org/rfc6202
  • Cloudflare Durable Objects — long-lived connections: developers.cloudflare.com/durable-objects
  • Hetzner CPX21 specs: hetzner.com/cloud

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة