تكرار السؤال: …
الإجابة المختصرة
نموذج الخيط لكل طلب يخصص خيطاً كاملاً لكل اتصال مستخدم ويحجزه طوال فترة الانتظار، بينما تعتمد حلقة الأحداث على خيط واحد يستمع لأحداث المقابس عبر آليات النواة (epoll/kqueue) دون حجب.
الشرح
في السيرفرات الكلاسيكية (مثل Apache Tomcat القديم)، حجز خيط لكل طلب يعني أنه عند وصول 10,000 اتصال متزامن يحتاج السيرفر لـ 10,000 خيط، مما يستهلك عدة غيغابايت من الذاكرة للمكدسات (Stacks) وينهار تحت وطأة الـ Context Switching (معضلة C10K). في المقابل، يعمل Node.js و Nginx و Netty بحلقة أحداث واحدة: يُسجل مقبس العميل مع النواة، وعند وصول بايتات من الشبكة، تطلق النواة إشعاراً ليلتقطه الـ Event Loop وينفذ الـ Callback بسرعة ويعود للاستماع فوراً.
مثال بالكود
// Event loop (Node.js): one thread, non-blocking I/O — the thread is free while the DB works
import http from "node:http";
http.createServer(async (req, res) => {
const rows = await db.query("SELECT * FROM products");
res.end(JSON.stringify(rows));
}).listen(3000);
// Thread-per-request (classic Java): every connection occupies a whole thread while it waits
ServerSocket server = new ServerSocket(3000);
while (true) {
Socket client = server.accept();
new Thread(() -> handle(client)).start(); // blocked on the DB → threads pile up under load
}أخطاء شائعة
- تشغيل عملية حسابية ثقيلة (CPU-intensive مثل تشفير معقد أو تعدين) على خيط الـ Event Loop، مما يجمد معالجة كافة طلبات المستخدمين الآخرين في نفس اللحظة.