الفكرة الأساسية
Node بيشغّل كود الـ JavaScript بتاعك على thread واحد. يعني لو السيرفر فيه 8 cores، الـ process الواحدة هتشغّل كودك على core واحد بس، والسبعة التانيين قاعدين.
السؤال اللي المذكرة كلها بتجاوب عليه: إزاي أستغل باقي الـ cores؟وفي PM2، الإجابة دي اسمها exec_mode، وليها قيمتين بس:
كل instance هي process مستقلة تمامًا، ومفيش بينها وبين التانية أي مشاركة.
workers كتير بيشتغلوا من خلال process رئيسية، وكلهم بيشاركوا نفس الـ port.
القيمتين دول مش اختراع من PM2. الاتنين مبنيين على حاجات موجودة في Node نفسه: child_process.fork() والـ cluster module.
طب إزاي Node بيخدم ألف request وهو thread واحد؟
لأن أغلب شغل السيرفر هو انتظار: query على الداتابيز، أو request لـ API تاني، أو قراية ملف. وأثناء ما Node مستني، بيروح يخدم request تاني. وده اسمه concurrency.
لكن لو الكود نفسه بيحسب حاجة تقيلة، زي loop كبيرة أو تشفير أو معالجة صورة، فالـ thread بيبقى مشغول، وكل الـ requests التانية بتستنى وراه. وهنا بتحتاج processes أكتر، أو worker_threads لو الحساب جوه request واحدة. وده هنرجعله في فصل المقارنة.
السيرفر اللي هنجرّب عليه
const http = require("node:http");const PORT = process.env.PORT || 3000;http .createServer((req, res) => res.end(`pid ${process.pid} on port ${PORT}\n`)) .listen(PORT, () => console.log(`pid ${process.pid} listening on ${PORT}`));كل response فيه الـ pid. فلما تبعت كذا request وتلاقي الرقم بيتغيّر، تعرف إن الطلبات راحت لـ processes مختلفة.
الـ default في PM2
pm2 start server.js # fork، process واحدةpm2 start server.js -i 4 # cluster تلقائي، 4 workerspm2 start server.js -i max # cluster، worker لكل corepm2 start server.js -i 4 -x # 4 processes بس fork- الـ default هو fork.
- أول ما تكتب
-iفي الـ CLI على Node app، PM2 بيحوّل لـ cluster لوحده حتى لو مقولتلوش. - لو عايز كذا instance في fork، استخدم
-x(اسمها--execute-command). مفيش flag اسمه--exec-modeفي الـ CLI. - أي حاجة مش Node، زي Python أو bash أو binary، بتشتغل fork دايمًا.