WebSocket في JavaScript: تطبيقات الوقت الفعلي المتقدمة على Node.js

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

العمل المتقدم مع WebSocket في JavaScript: من النموذج الأولي إلى الإنتاج

\n

WebSocket هو بروتوكول يوفر اتصالاً ثنائي الاتجاه بالكامل بين العميل والخادم عبر اتصال TCP واحد دائم. في المقالات السابقة، تناولنا الأساسيات: إنشاء الاتصال، إرسال الرسائل، والدردشات البسيطة. ومع ذلك، لإنشاء تطبيقات الوقت الفعلي الموثوقة والقابلة للتوسع حقًا (الألعاب عبر الإنترنت، منصات التداول، أنظمة التحرير التعاوني)، من الضروري إتقان التقنيات المتقدمة. في هذه المادة، سنناقش الهندسة المعمارية لخادم WebSocket من فئة الإنتاج على Node.js، بما في ذلك التوسع، إدارة الحالة، التفويض، ومعالجة الأخطاء.

\n\n

1. الهندسة المعمارية المتقدمة للخادم وإدارة الغرف

\n

أبسط خادم WebSocket (يعتمد على مكتبة ws) يعالج جميع العملاء في كائن عام واحد. في المشاريع الحقيقية، نحتاج إلى عزل منطقي - غرف (rooms) وقنوات (channels).

\n

لننظر إلى مثال على الهندسة المعمارية باستخدام فئة RoomManager:

\n
const WebSocket = require('ws');\n\nclass RoomManager {\n    constructor() {\n        this.rooms = new Map(); // roomName -> Set<WebSocket>\n    }\n\n    join(ws, roomName) {\n        if (!this.rooms.has(roomName)) {\n            this.rooms.set(roomName, new Set());\n        }\n        this.rooms.get(roomName).add(ws);\n        ws.room = roomName; // حفظ الغرفة على المقبس\n    }\n\n    leave(ws) {\n        if (ws.room && this.rooms.has(ws.room)) {\n            this.rooms.get(ws.room).delete(ws);\n            if (this.rooms.get(ws.room).size === 0) {\n                this.rooms.delete(ws.room);\n            }\n        }\n    }\n\n    broadcast(roomName, message, excludeWs = null) {\n        if (!this.rooms.has(roomName)) return;\n        this.rooms.get(roomName).forEach(client => {\n            if (client !== excludeWs && client.readyState === WebSocket.OPEN) {\n                client.send(JSON.stringify(message));\n            }\n        });\n    }\n}\n\n// الاستخدام\nconst wss = new WebSocket.Server({ port: 8080 });\nconst roomManager = new RoomManager();\n\nwss.on('connection', (ws) => {\n    ws.on('message', (data) => {\n        const msg = JSON.parse(data);\n        switch (msg.type) {\n            case 'join':\n                roomManager.join(ws, msg.room);\n                break;\n            case 'message':\n                roomManager.broadcast(ws.room, { user: msg.user, text: msg.text }, ws);\n                break;\n        }\n    });\n\n    ws.on('close', () => roomManager.leave(ws));\n});
\n

يسمح هذا الكود بإنشاء دردشات معزولة أو جلسات ألعاب. لاحظ معالجة إغلاق الاتصال - هذا أمر بالغ الأهمية لمنع تسرب الذاكرة.

\n\n

2. التوسع: التجميع و Redis Pub/Sub

\n

لا يمكن لعملية Node.js واحدة استخدام أكثر من نواة CPU واحدة بكفاءة. للتوسع، نستخدم الوحدة المدمجة cluster. ومع ذلك، تنشأ مشكلة: اتصالات WebSocket مرتبطة بعامل معين. كيف نرسل رسالة إلى مستخدم متصل بعامل آخر؟

\n

الحل هو وسيط رسائل خارجي، مثل Redis Pub/Sub.

\n
const cluster = require('cluster');\nconst http = require('http');\nconst { Server } = require('ws');\nconst redis = require('redis');\n\nconst REDIS_CHANNEL = 'ws:messages';\n\nif (cluster.isMaster) {\n    // تشغيل العمال حسب عدد النوى\n    const numCPUs = require('os').cpus().length;\n    for (let i = 0; i < numCPUs; i++) {\n        cluster.fork();\n    }\n\n    cluster.on('exit', (worker) => {\n        console.log(`Worker ${worker.process.pid} died`);\n        cluster.fork(); // إعادة التشغيل\n    });\n} else {\n    // العامل\n    const server = http.createServer();\n    const wss = new Server({ server });\n\n    // الاتصال بـ Redis\n    const pub = redis.createClient();\n    const sub                

المدونات

توصيات الكتب