MAISON CODE .
/ Backend · Realtime · WebSockets · Analytics · UX

لوحات المعلومات في الوقت الفعلي: العناية الواجبة بالهندسة

لماذا أصبح زر "التحديث" قديمًا؟ دليل فني للأحداث المرسلة من الخادم (SSE)، وWebSockets، ومشكلة C10K.

AB
Alex B.
لوحات المعلومات في الوقت الفعلي: العناية الواجبة بالهندسة

إنها الجمعة السوداء. الاستخدام موجود في “غرفة الحرب”. يسأل الرئيس التنفيذي: “ما مقدار الإيرادات التي حققناها في آخر 10 دقائق؟” قمت بالنقر فوق تحديث. يستغرق استعلام قاعدة البيانات 15 ثانية. يتم تحميل الصفحة. “50.000 يورو.” بحلول الوقت الذي تقوله، الرقم خاطئ.

في التجارة الحديثة، البيانات الثابتة ميتة. تحركات المخزون. الأسعار تتغير. طفرات المرور. لوحات المعلومات في الوقت الفعلي ليست مجرد لعبة مرئية “من الممتع اقتناؤها”؛ فهي أدوات تشغيلية بالغة الأهمية. لكن هندستها بشكل صحيح - بالنسبة لآلاف المستخدمين المتزامنين، دون إيقاف قاعدة بياناتك - تمثل تحديًا للأنظمة الموزعة.

في Maison Code Paris، نبني واجهات “Mission Control” التي تبدو حية. إليك كيفية القيام بذلك دون ذوبان الخوادم.

لماذا تتحدث Maison Code عن هذا

في Maison Code Paris، نعمل كضمير معمari لعملائنا. غالبًا ما نرث حزمًا “حديثة” تم بناؤها دون فهم أساسي للحجم.

نناقش هذا الموضوع لأنه يمثل نقطة تحول حاسمة في النضج الهندسي. التنفيذ الصحيح يميز MVP الهش عن منصة مؤسسية مرنة يمكنها التعامل مع حركة مرور الجمعة السوداء.

معركة البروتوكول: الاقتراع مقابل SSE مقابل WebSockets

القرار الأول نقل البيانات بكفاءة.

1. الاقتراع (النهج الساذج)

setInterval(() => fetch('/api/sales'), 5000);

  • الإيجابيات: بسيطة. يعمل على كل خادم (PHP، Node، Python).
  • سلبيات:
    • زمن الوصول: متوسط التأخير 2.5 ثانية.
    • النفايات: 90% من الطلبات تُرجع “لا تغيير”. أنت تدق قاعدة البيانات الخاصة بك من أجل لا شيء.
    • البطارية: تحافظ على نشاط الراديو المحمول.

2. WebSockets (الأنبوب ثنائي الاتجاه)

اتصال TCP مستمر.

  • الإيجابيات: سريع للغاية. ثنائي الاتجاه (يمكن للعميل إرسال “أنا أكتب”).
  • سلبيات:
    • جدران الحماية: غالبًا ما يحظر وكلاء الشركات حركة المرور بخلاف HTTP.
    • الحالة: القياس أمر صعب. أنت بحاجة إلى جلسات ثابتة أو واجهة خلفية redis.
    • المبالغة: عادة ما تكون لوحة المعلومات للقراءة فقط.

3. الأحداث المرسلة من الخادم (SSE) (المعيار الذهبي)

اتصال HTTP قياسي، لكن الخادم يبقيه مفتوحًا ويبث النص. “نوع المحتوى: نص/دفق حدث”.

  • الإيجابيات:
    • متوافق مع HTTP: يعمل من خلال موازنات التحميل/الوكلاء القياسية.
    • إعادة الاتصال تلقائيًا: يعالج المتصفح عمليات إعادة المحاولة تلقائيًا.
    • خفيفة الوزن: الرسائل النصية البسيطة فقط.
  • السلبيات: اتجاه واحد فقط (الخادم -> العميل).

بالنسبة للوحات المعلومات، SSE هو الفائز الفني.

العمارة: حافلة الأحداث

لا يمكنك التدفق البسيط من قاعدة البيانات إلى العميل. إذا كان لديك 5000 عميل، وقمت بتشغيل SELECT sum(total) FROM order في كل مرة يتغير فيها الصف، فسوف يموت PostgreSQL.

أنت بحاجة إلى هندسة ناقل الأحداث.

  1. الاستقبال: اطلب إنشاء خطاف ويب -> واجهة برمجة التطبيقات.
  2. النشر: واجهة برمجة التطبيقات (API) -> Redis Pub/Sub (القناة: المبيعات).
  3. Fan-Out: يشترك خادم SSE في Redis.
  4. البث: عندما يرسل Redis رسالة، يقوم خادم SSE بالتكرار عبر العملاء المتصلين والكتابة إلى التدفقات الخاصة بهم.

الكود: تيار Remix / Node.js

في إطار عمل حديث مثل Remix أو Next.js App Router، يكون البث أمرًا أصليًا.

// app/routes/api.stream.ts في الريمكس
استيراد {eventStream} من "remix-utils/sse/server"؛
استيراد {redis} من "~/lib/redis"؛

تصدير محمل دالة غير متزامنة ({ request }: LoaderFunctionArgs) {
  إرجاع حدث دفق (request.signal، إعداد الوظيفة (إرسال) {
    قناة const = "تحديثات لوحة المعلومات";

    مستمع ثابت = (الرسالة: سلسلة) => {
      إرسال({ الحدث: "تحديث"، البيانات: رسالة });
    };

    المشترك الثابت = redis.duplicate();
    مشترك.اشتراك(قناة);
    المشترك.ون ("رسالة"، (تشان، رسالة) => {
      if (chan ===channel) waiter(msg);
    });

    تنظيف وظيفة العودة () {
      المشترك. إلغاء الاشتراك (القناة)؛
      المشترك. إنهاء ()؛
    };
  });
}

تحديات الاتصال

1. مشكلة C10K (القياس)

يمكن لـ Node.js القياسي التعامل مع حوالي 10 آلاف من الاتصالات المتزامنة. تعاني الخوادم مثل Apache/PHP من المئات. إذا كنت بحاجة إلى 100 ألف مستخدم متصل (على سبيل المثال، عد تنازلي ضخم للبيع السريع)، فلا يمكنك استخدام خادم واحد قياسي. الحل:

  • القياس الأفقي: 10 خوادم خلف موازن التحميل.
  • Redis Backplane: يشترك كل خادم في Redis. لذا، إذا قام الخادم “أ” بالنشر، فإن الخادم “ب” يستقبله ويدفعه إلى المستخدم “ب”.
  • الخدمات المُدارة: للحصول على نطاق واسع، قم بإلغاء التحميل إلى Pusher أو Supabase Realtime. يديرون طبقة المقبس.

2. القطيع الرعد (إعادة الاتصال)

قمت بنشر إصدار جديد من لوحة المعلومات. تتم إعادة تشغيل الخادم. قطع اتصال 5000 عميل. يحاول 5000 عميل على الفور إعادة الاتصال في تمام الساعة 10:00:01. ترتفع وحدة المعالجة المركزية لديك إلى 100%. تعطل الخادم. إعادة المحاولة للعملاء. حلقة الموت. الحل: الارتعاش. يجب على العميل الانتظار “عشوائي (0، 5000)” مللي ثانية قبل إعادة الاتصال. يحتوي تطبيق SSE الأصلي للمتصفح على تراجع أساسي، لكن منطق WebSocket المخصص غالبًا ما يتجاهل ذلك.

3. حدود واصف ملفات نظام التشغيل

يحتوي خادم Linux على حد افتراضي يبلغ 1024 ملفًا مفتوحًا (اتصالات). إذا كنت تريد 10 آلاف مستخدم، فيجب عليك تعديل /etc/security/limits.conf. * ملف ناعم 100000 *رقم الهارد 100000 إذا نسيت هذا، فسوف يتعطل خادم Node.js الخاص بك بسبب أخطاء EMFILE عند المستخدم رقم 1025.

المصالحة على مستوى الدولة: الفجوة

المستخدم في نفق القطار. 10:00:00 - المستخدم متصل بالإنترنت. الإجمالي: 100 يورو. 10:00:05 - النفق. المستخدم غير متصل. 10:00:06 - يدفع الخادم الحدث “طلب جديد +50 يورو”. المجموع الآن 150 يورو. 10:00:10 - نهاية النفق. إعادة اتصال المستخدم.

يعتقد المستخدم أن الإجمالي هو 100 يورو. لقد فاتهم الحدث. الدولة تنجرف.

الإصلاح: معرف الحدث الأخير.

  1. كل حدث له معرف (رقم تسلسلي أو طابع زمني).
  2. يقوم المتصفح بتخزين آخر معرف شاهده.
  3. عند إعادة الاتصال، يرسل المتصفح “معرف آخر حدث: 105”.
  4. يتحقق الخادم من المخزن المؤقت الخاص به. “آه، العميل في الخلف. إليكم الأحداث 106، 107، 108.”

أو استراتيجية أبسط: StaleCheck. في كل عملية إعادة اتصال، تقوم الواجهة الأمامية بتشغيل HTTP قياسي fetch('/api/current-total') لإعادة مزامنة الخط الأساسي.

تجربة المستخدم: تصور النبض

تؤدي بيانات الوقت الفعلي إلى إنشاء “ضوضاء مرئية”. إذا كنت تبيع 10 عناصر في الثانية، وقمت بتحديث الرقم 10 مرات في الثانية، فلن يتمكن المستخدم من قراءته. انها مجرد طمس.

تحديثات الخانق: الحد من إعادة طلاء واجهة المستخدم لمرة واحدة لكل 500 مللي ثانية. تجميع التغييرات في المخزن المؤقت. المخزن المؤقت = +10، +5، +20. تدفق -> تحديث واجهة المستخدم +35.

الرسوم المتحركة: استخدم “framer-motion” أو “react-spring” لتحريك الرقم. 100 -> 135. يرى المستخدم الأرقام تتدحرج (مثل مضخة الغاز). وهذا ينقل “السرعة” و”النشاط” بشكل أفضل بكثير من القفزة الثابتة.

وظيفة رقم متحرك({ القيمة }) {
  ثابت { رقم } = useSpring({
    من: {الرقم: 0}،
    الرقم: القيمة،
    تأخير: 200،
    التكوين: { الكتلة: 1، التوتر: 20، الاحتكاك: 10 }،
  });

  إرجاع <animated.span>{number.to((n) => n.toFixed(0))}</animated.span>;
}

10. الأمان: التحكم في الوصول المستند إلى الدور (RBAC) على التدفقات

تتحقق مصادقة HTTP القياسية من صحة الطلب. ولكن بمجرد فتح المقبس، كيف يمكنك منع “مدير المتجر” من الاستماع إلى أحداث مبيعات “المسؤول العام”؟ ** نطاق القناة **.

  1. يتصل المستخدم بـ JWT.
  2. يقوم الخادم بفك تشفير JWT -> الدور: المدير، معرف_المتجر: 12.
  3. يطلب العميل الاشتراك في خدمة “المبيعات العالمية”.
  4. منطق الخادم: إذا (الدور!== 'المشرف') يرمي ممنوعًا.
  5. منطق الخادم: السماح فقط بـ redis.subscribe('sales-store-12').

11. دقة البيانات: التجزئة مقابل التجميع

هل ينبغي عليك إرسال حدث لكل طلب بقيمة 5 يورو لكل؟ إذا كان لديك 100 طلب/ثانية، فستبدو لوحة المعلومات الخاصة بك مثل ضوء قوي. استراتيجية التجميع:

  1. الدفق الخام: الاستخدام الداخلي/تصحيح الأخطاء.
  2. ** الدفق المقيد (واجهة مستخدم المستخدم) **: التجميع كل ثانية واحدة.
    • { الأحداث: 50، المجموع_المضاف: 5000 }.
    • أرسل رسالة واحدة.
  3. تدفق اللقطات: كل دقيقة واحدة. مزامنة كاملة. قم بموازنة “الإحساس بالزمن الحقيقي” مع “الحدود المعرفية البشرية”.

13. قرار “البناء مقابل الشراء” (غرافانا)

في بعض الأحيان، لا تحتاج إلى إنشاء لوحة تحكم React مخصصة. إذا كان الجمهور “مهندسين” أو “عمليات داخلية”، فما عليك سوى استخدام Grafana. قم بتوصيله بنسخة قراءة PostgreSQL الخاصة بك. اضبط معدل التحديث على 5 ثوانٍ. منتهي. التكلفة 0 يورو. نحن نبني لوحات معلومات React مخصصة فقط عندما يكون الجمهور “عملاء خارجيين” أو “مسؤولين تنفيذيين على المستوى التنفيذي الذين يحتاجون إلى تجربة مستخدم محددة”.

14. عتبات التنبيه (الإشارات المرئية)

الرقم الذي يتغير من 100 إلى 120 هو بيانات. الرقم الذي يتغير من 100 إلى 120 (خلفية حمراء) هو معلومات. نحن نبني “منطق العتبة” في مكونات الواجهة الأمامية. `إذا (القيمة <الهدف) اللون = ‘أحمر”. وهذا يساعد المستخدمين على معالجة الدفق الحقيقي بشكل معرفي. “لست بحاجة لقراءة الأرقام؛ أنا فقط بحاجة للبحث عن اللون الأحمر.”

15. الاستنتاج

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


هل بياناتك قديمة؟

نحن نبني واجهات تداول عالية التردد للبيع بالتجزئة.

[قم بتعيين مهندسين معماريين لدينا](/جهة اتصال). قم بتوظيف مهندسينا.