الفكرة
في cluster فيه process رئيسية اسمها primary. الـ primary مش بيخدم requests، شغلته إنه يعمل fork لـ workers ويمسك الـ port. والـ workers كلهم بيشاركوا نفس الـ port، والـ primary بيوزّع الـ connections عليهم بالدور (round-robin، وده الـ default على Linux وmacOS).
port واحد، ومفيش Nginx مطلوب عشان التوزيع.بإيدك: Node cluster
cluster.js
const cluster = require("node:cluster");const http = require("node:http");const os = require("node:os");if (cluster.isPrimary) { const count = os.availableParallelism(); console.log(`primary ${process.pid} → ${count} workers`); for (let i = 0; i < count; i++) cluster.fork(); cluster.on("exit", (worker, code) => { // رجّع الـ worker لو وقع، مش لو احنا اللي قفلناه if (!worker.exitedAfterDisconnect) { console.log(`worker ${worker.process.pid} died (${code}), forking again`); cluster.fork(); } });} else { http .createServer((req, res) => res.end(`pid ${process.pid}\n`)) .listen(3000);}cluster.fork()من جوه بيستخدمchild_process.fork()، بس معاه قناة IPC مع الـ primary. وده اللي بيخلي الـ workers يشاركوا الـ server handle بتاع الـ port.- الـ worker لما ينادي
listen(3000)، ده مش بيفتح الـ port بنفسه. الطلب بيروح للـ primary، والـ primary هو اللي ماسك الـ port. isPrimaryهو الاسم الجديد. أماisMasterفـ deprecated بس لسه شغال.
نفس الكلام بـ PM2: PM2 هو الـ primary
في cluster mode، PM2 نفسه هو الـ primary. كودك بيتشغّل جوه workers بس، فمحتاجش أي سطر من cluster:
ecosystem.config.js
module.exports = { apps: [ { name: "api", script: "./server.js", exec_mode: "cluster", instances: "max", // worker لكل core env: { PORT: 3000 }, }, ],};- Client ← PM2: :3000
- PM2 ← worker 1
- PM2 ← worker 2
- PM2 ← worker N
قيم instances
"max"أو0: worker لكل core.-1: كل الـ cores إلا واحد، عشان تسيب core للـ OS أو للداتابيز لو على نفس السيرفر.- رقم زي
4: عدد ثابت، وما ينفعش يزيد عن عدد الـ cores (هنشرح ليه في فصلbin/www).
🧪 Lab: اتأكد إن التوزيع شغال
terminal
pm2 start ecosystem.config.jspm2 list # mode = cluster، وكذا سطر لنفس الاسمfor i in $(seq 8); do curl -s --no-keepalive localhost:3000; doneلازم تلاقي الـ pid بيتغيّر بين الردود. ولو كل الردود جت من pid واحد، فغالبًا الـ client بيعيد استخدام نفس الـ connection (keep-alive)، لأن التوزيع بيتم على مستوى الـ connection مش الـ request.
خلّصت الفصل لما
🧠 اختبر نفسك
١) في PM2 cluster mode، كودك بيشتغل primary ولا worker؟
worker. PM2 هو الـ primary.
٢) ليه workers كتير يقدروا يعملوا listen على نفس الـ port؟
لأن الـ primary هو اللي ماسك الـ port فعلًا، والـ workers بياخدوا منه الـ connections عن طريق IPC.
٣) كل الردود جت من نفس الـ pid. ليه؟
غالبًا keep-alive. التوزيع بيحصل لكل connection جديدة مش لكل request.