Cluster moduli
Klaster moduli nima?
Klaster moduli bir xil server portiga ega bo‘lgan bir nechta ishchi jarayonlarni yaratish usulini taqdim etadi.
Node.js sukut bo‘yicha bir torli bo‘lganligi sababli, Klaster moduli ilovangizga bir nechta CPU yadrolaridan foydalanishga yordam beradi va ko‘p yadroli tizimlarda unumdorlikni sezilarli darajada yaxshilaydi.
Har bir ishchi o‘z jarayonda o‘z hodisa sikli va xotira maydoni bilan ishlaydi, lekin ularning barchasi bir xil server portiga ega.
Asosiy jarayon ishchilarni yaratish va ular o‘rtasida kiruvchi aloqalarni taqsimlash uchun javobgardir.
Klaster modulini import qilish
Klaster moduli sukut bo‘yicha Node.js tarkibiga kiritilgan.
Siz uni skriptingizda talab qilish orqali foydalanishingiz mumkin:
const cluster = require('cluster');
const os = require('os');
// Check if this is the master process
if (cluster.isMaster) {
console.log(`Master process ${process.pid} is running`);
} else {
console.log(`Worker process ${process.pid} started`);
}
Klasterlash qanday ishlaydi
Klaster moduli bir nechta ishchi jarayonlarni keltirib chiqaradigan asosiy jarayonni yaratish orqali ishlaydi.
Asosiy jarayon dastur kodini bajarmaydi, balki ishchilarni boshqaradi.
Har bir ishchi jarayon dastur kodini mustaqil ravishda boshqaradigan yangi Node.js misolidir.
Eslatma: Kaput ostida Klaster moduli yangi ishchilarni yaratish uchun Child Process modulining fork() usulidan foydalanadi.
| Jarayon turi | Mas’uliyat |
|---|---|
| Ustoz |
|
| Ishchi |
|
Asosiy klasterni yaratish
Har bir protsessor uchun ishchi jarayonlari bilan klaster yaratishning oddiy misoli:
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
// This is the master process
console.log(`Master ${process.pid} is running`);
// Fork workers for each CPU core
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
// Listen for worker exits
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died`);
// You can fork a new worker to replace the dead one
console.log('Forking a new worker...');
cluster.fork();
});
} else {
// This is a worker process
// Create an HTTP server
http.createServer((req, res) => {
res.writeHead(200);
res.end(`Hello from Worker ${process.pid}\n`);
// Simulate CPU work
let i = 1e7;
while (i > 0) { i--; }
}).listen(8000);
console.log(`Worker ${process.pid} started`);
}
Ushbu misolda:
- Asosiy jarayon protsessor yadrolari sonini aniqlaydi
- U har bir protsessor uchun bitta ishchini ajratadi
- Har bir worker bir xil portda (8000) HTTP server yaratadi
- Klaster moduli kiruvchi ulanishlarni avtomatik ravishda yuklaydi
- Agar ishchi qulab tushsa, usta yangisini yaratadi
Ishchilar bilan aloqa
send() usuli va message hodisalari yordamida asosiy va ishchi jarayonlari o‘rtasida aloqa o‘rnatishingiz mumkin, xuddi IPC Child Process modulida qanday ishlaydi.
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
console.log(`Master ${process.pid} is running`);
// Track request count for each worker
const requestCounts = {};
// Fork workers
for (let i = 0; i < numCPUs; i++) {
const worker = cluster.fork();
requestCounts[worker.id] = 0;
// Listen for messages from this worker
worker.on('message', (msg) => {
if (msg.cmd === 'incrementRequestCount') {
requestCounts[worker.id]++;
console.log(`Worker ${worker.id} (pid ${worker.process.pid}) has handled ${requestCounts[worker.id]} requests`);
}
});
}
// Every 10 seconds, send the request count to each worker
setInterval(() => {
for (const id in cluster.workers) {
cluster.workers[id].send({
cmd: 'requestCount',
requestCount: requestCounts[id]
});
}
console.log('Total request counts:', requestCounts);
}, 10000);
// Handle worker exit
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died`);
// Fork a new worker to replace it
const newWorker = cluster.fork();
requestCounts[newWorker.id] = 0;
});
} else {
// Worker process
console.log(`Worker ${process.pid} started`);
let localRequestCount = 0;
// Handle messages from the master
process.on('message', (msg) => {
if (msg.cmd === 'requestCount') {
console.log(`Worker ${process.pid} has handled ${msg.requestCount} requests according to master`);
}
});
// Create an HTTP server
http.createServer((req, res) => {
// Notify the master that we handled a request
process.send({ cmd: 'incrementRequestCount' });
// Increment local count
localRequestCount++;
// Send response
res.writeHead(200);
res.end(`Hello from Worker ${process.pid}, I've handled ${localRequestCount} requests locally\n`);
}).listen(8000);
}
Nol ishlamay qolganda qayta ishga tushirish
Klasterlashning asosiy afzalliklaridan biri bu ishchilarni uzilishlarsiz qayta ishga tushirish imkoniyatidir. Bu ilovangizga yangilanishlarni joylashtirish uchun foydalidir.
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
console.log(`Master ${process.pid} is running`);
// Store workers
const workers = [];
// Fork initial workers
for (let i = 0; i < numCPUs; i++) {
workers.push(cluster.fork());
}
// Function to restart workers one by one
function restartWorkers() {
console.log('Starting zero-downtime restart...');
let i = 0;
function restartWorker() {
if (i >= workers.length) {
console.log('All workers restarted successfully!');
return;
}
const worker = workers[i++];
console.log(`Restarting worker ${worker.process.pid}...`);
// Create a new worker
const newWorker = cluster.fork();
newWorker.on('listening', () => {
// Once the new worker is listening, kill the old one
worker.disconnect();
// Replace the old worker in our array
workers[workers.indexOf(worker)] = newWorker;
// Continue with the next worker
setTimeout(restartWorker, 1000);
});
}
// Start the recursive process
restartWorker();
}
// Simulate a restart after 20 seconds
setTimeout(restartWorkers, 20000);
// Handle normal worker exit
cluster.on('exit', (worker, code, signal) => {
if (worker.exitedAfterDisconnect !== true) {
console.log(`Worker ${worker.process.pid} died unexpectedly, replacing it...`);
const newWorker = cluster.fork();
workers[workers.indexOf(worker)] = newWorker;
}
});
} else {
// Worker process
// Create an HTTP server
http.createServer((req, res) => {
res.writeHead(200);
res.end(`Worker ${process.pid} responding, uptime: ${process.uptime().toFixed(2)} seconds\n`);
}).listen(8000);
console.log(`Worker ${process.pid} started`);
}
Bu misol ko‘rsatadi:
- Ishchilarning dastlabki to‘plamini yaratish
- Har bir ishchini birma-bir almashtirish
- Eskisini o‘chirishdan oldin yangi ishchi tinglashiga ishonch hosil qiling
- Ishchilarning kutilmagan o‘limini ehtiyotkorlik bilan hal qilish
Yuklarni muvozanatlash
Klaster modulida ishchi jarayonlar o‘rtasida kiruvchi ulanishlarni taqsimlash uchun o‘rnatilgan yuk balansi mavjud.
Ikkita asosiy strategiya mavjud:
Round-Robin (standart)
Windowsdan tashqari barcha platformalarda sukut bo‘yicha Node.js ulanishlarni aylanali yondashuv yordamida tarqatadi, bunda usta ulanishlarni qabul qiladi va ularni ishchilar o‘rtasida aylana ketma-ketlikda taqsimlaydi.
Eslatma: Windows-da yuk taqsimoti Windows-ning portlarni qanday ishlashiga qarab boshqacha ishlaydi. Windows-da ishchilar ulanishlarni qabul qilish uchun raqobatlashadilar.
Asosiy ishchi
Bundan tashqari, cluster.schedulingPolicy ni belgilash orqali har bir ishchi jarayonga (worker) ulanishlarni to‘g‘ridan-to‘g‘ri qabul qilishga ruxsat berishingiz mumkin:
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
// Set the scheduling policy to SCHED_NONE (let workers accept connections themselves)
cluster.schedulingPolicy = cluster.SCHED_NONE;
if (cluster.isMaster) {
console.log(`Master ${process.pid} is running`);
// Fork workers
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died`);
cluster.fork();
});
} else {
// Worker process
http.createServer((req, res) => {
res.writeHead(200);
res.end(`Hello from Worker ${process.pid}\n`);
}).listen(8000);
console.log(`Worker ${process.pid} started`);
}
Umumiy holat (shared state)
Har bir ishchi o‘z jarayonida o‘z xotira maydoniga ega bo‘lganligi sababli, ular o‘zgaruvchilar orqali holatni bevosita almashishlari mumkin emas. Buning o‘rniga siz:
- IPC xabar almashinuvidan foydalaning (aloqa misolida ko‘rsatilganidek)
- Redis, MongoDB yoki fayl tizimi (fs) kabi tashqi xotiradan foydalaning
- Sessiyani boshqarish uchun yopishqoq yuk balansidan foydalaning
Yopishqoq sessiyalar misoli
Yopishqoq seanslar bir xil mijozning so‘rovlari doimo bir xil ishchi jarayonga o‘tishini ta’minlaydi:
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
console.log(`Master ${process.pid} is running`);
// Fork workers
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
// Store worker references by id
const workers = {};
for (const id in cluster.workers) {
workers[id] = cluster.workers[id];
}
// Create a server to route connections to workers
const server = http.createServer((req, res) => {
// Get client IP
const clientIP = req.connection.remoteAddress || req.socket.remoteAddress;
// Simple hash function to determine which worker to use
const workerIndex = clientIP.split('.').reduce((a, b) => a + parseInt(b), 0) % numCPUs;
const workerIds = Object.keys(workers);
const workerId = workerIds[workerIndex];
// Send the request to the selected worker
workers[workerId].send('sticky-session:connection', req.connection);
res.end(`Request routed to worker ${workerId}`);
}).listen(8000);
console.log('Master server listening on port 8000');
// Handle worker exit
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died`);
// Remove the dead worker
delete workers[worker.id];
// Create a replacement
const newWorker = cluster.fork();
workers[newWorker.id] = newWorker;
});
} else {
// Worker process - just demonstrates the concept
// In a real implementation, you'd need more socket handling
process.on('message', (msg, socket) => {
if (msg === 'sticky-session:connection' && socket) {
console.log(`Worker ${process.pid} received sticky connection`);
// In a real implementation, you'd handle the socket here
// socket.end(`Handled by worker ${process.pid}\n`);
}
});
// Workers would also run their own server
http.createServer((req, res) => {
res.writeHead(200);
res.end(`Direct request to Worker ${process.pid}\n`);
}).listen(8001);
console.log(`Worker ${process.pid} started`);
}
Bu yopishqoq seanslar kontseptsiyasini ko‘rsatadigan soddalashtirilgan misol. Ishlab chiqarishda siz odatda:
- Murakkab xashing algoritmidan foydalaning
- IP manzillar o‘rniga cookie fayllari yoki boshqa seans identifikatorlaridan foydalaning
- Soket ulanishlarini ehtiyotkorlik bilan ishlating
Ishchi jarayonning lifecycle’i (hayotiy sikli)
Klasterni to‘g‘ri boshqarish uchun ishchi jarayonning lifecycle’ini (hayotiy siklini) tushunish muhim:
| Tadbir | Tavsif |
|---|---|
fork |
Yangi ishchi vilkalar bo‘lganda chiqariladi |
online |
Ishchi ishlayotgan va xabarlarni qayta ishlashga tayyor bo‘lganda chiqariladi |
listening |
Ishchi ulanishlarni tinglashni boshlaganda chiqariladi |
disconnect |
Ishchi IPC kanali uzilganda chiqariladi |
exit |
Ishchi jarayoni chiqqanda chiqariladi |
const cluster = require('cluster');
const http = require('http');
if (cluster.isMaster) {
console.log(`Master ${process.pid} is running`);
// Fork a worker
const worker = cluster.fork();
// Listen for all worker lifecycle events
worker.on('fork', () => {
console.log(`Worker ${worker.process.pid} is being forked`);
});
worker.on('online', () => {
console.log(`Worker ${worker.process.pid} is online`);
});
worker.on('listening', (address) => {
console.log(`Worker ${worker.process.pid} is listening on port ${address.port}`);
});
worker.on('disconnect', () => {
console.log(`Worker ${worker.process.pid} has disconnected`);
});
worker.on('exit', (code, signal) => {
console.log(`Worker ${worker.process.pid} exited with code ${code} and signal ${signal}`);
if (signal) {
console.log(`Worker was killed by signal: ${signal}`);
} else if (code !== 0) {
console.log(`Worker exited with error code: ${code}`);
} else {
console.log('Worker exited successfully');
}
});
// After 10 seconds, gracefully disconnect the worker
setTimeout(() => {
console.log('Gracefully disconnecting worker...');
worker.disconnect();
}, 10000);
} else {
// Worker process
console.log(`Worker ${process.pid} started`);
// Create an HTTP server
http.createServer((req, res) => {
res.writeHead(200);
res.end(`Hello from Worker ${process.pid}\n`);
}).listen(8000);
// If worker is disconnected, close the server
process.on('disconnect', () => {
console.log(`Worker ${process.pid} disconnected, closing server...`);
// In a real application, you'd want to close all connections and clean up resources
process.exit(0);
});
}
Chiroyli o‘chirish
Ishchi jarayonlaringiz mavjud so‘rovlarni ular chiqishdan oldin ko‘rib chiqishni yakunlashiga imkon berish uchun oqlangan o‘chirish muhim ahamiyatga ega.
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
console.log(`Master ${process.pid} is running`);
// Fork workers
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
// Handle termination signals
process.on('SIGTERM', () => {
console.log('Master received SIGTERM, initiating graceful shutdown...');
// Notify all workers to finish their work and exit
Object.values(cluster.workers).forEach(worker => {
console.log(`Sending SIGTERM to worker ${worker.process.pid}`);
worker.send('shutdown');
});
// Set a timeout to force-kill workers if they don't exit gracefully
setTimeout(() => {
console.log('Some workers did not exit gracefully, forcing shutdown...');
Object.values(cluster.workers).forEach(worker => {
if (!worker.isDead()) {
console.log(`Killing worker ${worker.process.pid}`);
worker.process.kill('SIGKILL');
}
});
// Exit the master
console.log('All workers terminated, exiting master...');
process.exit(0);
}, 5000);
});
// Handle worker exits
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} exited (${signal || code})`);
// If all workers have exited, exit the master
if (Object.keys(cluster.workers).length === 0) {
console.log('All workers have exited, shutting down master...');
process.exit(0);
}
});
// Log to show the master is ready
console.log(`Master ${process.pid} is ready with ${Object.keys(cluster.workers).length} workers`);
console.log('Send SIGTERM to the master process to initiate graceful shutdown');
} else {
// Worker process
console.log(`Worker ${process.pid} started`);
// Track if we're shutting down
let isShuttingDown = false;
let activeConnections = 0;
// Create HTTP server
const server = http.createServer((req, res) => {
// Track active connection
activeConnections++;
// Simulate a slow response
setTimeout(() => {
res.writeHead(200);
res.end(`Hello from Worker ${process.pid}\n`);
// Connection complete
activeConnections--;
// If we're shutting down and no active connections, close the server
if (isShuttingDown && activeConnections === 0) {
console.log(`Worker ${process.pid} has no active connections, closing server...`);
server.close(() => {
console.log(`Worker ${process.pid} closed server, exiting...`);
process.exit(0);
});
}
}, 2000);
});
// Start server
server.listen(8000);
// Handle shutdown message from master
process.on('message', (msg) => {
if (msg === 'shutdown') {
console.log(`Worker ${process.pid} received shutdown message, stopping new connections...`);
// Set shutdown flag
isShuttingDown = true;
// Stop accepting new connections
server.close(() => {
console.log(`Worker ${process.pid} closed server`);
// If no active connections, exit immediately
if (activeConnections === 0) {
console.log(`Worker ${process.pid} has no active connections, exiting...`);
process.exit(0);
} else {
console.log(`Worker ${process.pid} waiting for ${activeConnections} connections to finish...`);
}
});
}
});
// Also handle direct termination signal
process.on('SIGTERM', () => {
console.log(`Worker ${process.pid} received SIGTERM directly`);
// Use the same shutdown logic
isShuttingDown = true;
server.close(() => process.exit(0));
});
}
Eng yaxshi amaliyotlar
- Ishchilar soni: Aksariyat hollarda protsessor yadrosiga bitta ishchi yarating
- Stateless dizayn: Klasterlar bilan samarali ishlash uchun ilovangizni stateless (ichki state saqlamaydigan) qilib loyihalang
- Aqlli o‘chirish: Ulanishlar uzilib qolmasligi uchun to‘g‘ri o‘chirishni amalga oshiring
- Ishchi monitoringi: Avariyaga uchragan ishchilarni zudlik bilan kuzatib boring va almashtiring
- Ma’lumotlar bazasi ulanishlari: Har bir ishchi o‘z ulanish puliga ega, shuning uchun ma’lumotlar bazasi ulanishlarini mos ravishda sozlang.
- Umumiy manbalar: Ishchilar o‘rtasida taqsimlangan resurslardan ehtiyot bo‘ling (masalan, fayl qulflari)
- Ishchilarni ozg‘in tutish: Ishchi jarayonlarida keraksiz xotiradan foydalanishdan saqlaning
Ogohlantirish: Bir nechta ishchilardan foydalanganda faylga asoslangan qulflash va boshqa umumiy resurslardan ehtiyot bo‘ling. Bitta jarayonli dasturda xavfsiz bo‘lgan operatsiyalar bir nechta ishchilar bilan poyga sharoitlariga olib kelishi mumkin.
Klaster modulining muqobillari
Klaster moduli kuchli bo‘lsa-da, bir nechta yadrolarda Node.js ilovalarini ishga tushirish uchun alternativalar mavjud:
| Yondashuv | Tavsif | Foydalanish holati |
|---|---|---|
| PM2 | Node.js ilovalari uchun o‘rnatilgan yuk balansi va klasterlash jarayoni boshqaruvchisi | Jarayonni ishonchli boshqarishni talab qiladigan ishlab chiqarish ilovalari |
| Yuklash balansi | Nginx kabi yuk balanslagichining orqasida bir nechta Node.js nusxalarini ishga tushirish | Bir nechta serverlar yoki konteynerlar bo‘ylab yukni taqsimlash |
| Ishchi iplari | CPU’ni ko‘p talab qiladigan vazifalar uchun yengilroq oqimlar (Node.js >= 10.5.0) | Bitta jarayonda protsessor talab qiladigan operatsiyalar |
| Konteynerlar | Bir nechta konteynerlashtirilgan nusxalarni ishga tushirish (masalan, Docker va Kubernetes yordamida) | Zamonaviy bulutli muhitda kengaytiriladigan, taqsimlangan ilovalar |
Murakkab yuklarni muvozanatlash strategiyalari
Klaster modulining sukut bo‘yicha aylanma rejimda yuk balansi ko‘p ilovalar uchun yaxshi ishlayotgan bo‘lsa-da, muayyan foydalanish holatlari uchun sizga murakkabroq strategiyalar kerak bo‘lishi mumkin.
1. Og‘irlangan raund-Robin
const cluster = require('cluster');
const http = require('http');
const os = require('os');
if (cluster.isMaster) {
console.log(`Master ${process.pid} is running`);
// Create workers with different weights
const workerWeights = [3, 2, 1]; // First worker gets 3x more load than the last
const workers = [];
// Create workers based on weights
workerWeights.forEach((weight, index) => {
for (let i = 0; i < weight; i++) {
const worker = cluster.fork({ WORKER_WEIGHT: weight });
worker.weight = weight;
workers.push(worker);
}
});
// Track the next worker to use
let workerIndex = 0;
// Create a load balancer server
http.createServer((req, res) => {
// Simple round-robin with weights
const worker = workers[workerIndex++ % workers.length];
worker.send('handle-request', req.socket);
}).listen(8000);
} else {
// Worker code
process.on('message', (message, socket) => {
if (message === 'handle-request' && socket) {
// Handle the request
socket.end(`Handled by worker ${process.pid}\n`);
}
});
}
2. Eng kam ulanishlar
const cluster = require('cluster');
const http = require('http');
if (cluster.isMaster) {
console.log(`Master ${process.pid} is running`);
// Create workers and track their connection counts
const workers = [];
const numCPUs = require('os').cpus().length;
for (let i = 0; i < numCPUs; i++) {
const worker = cluster.fork();
worker.connectionCount = 0;
workers.push(worker);
// Track worker connections
worker.on('message', (msg) => {
if (msg.type === 'connection') {
worker.connectionCount = msg.count;
}
});
}
// Create load balancer
http.createServer((req, res) => {
// Find worker with least connections
let minConnections = Infinity;
let selectedWorker = null;
for (const worker of workers) {
if (worker.connectionCount < minConnections) {
minConnections = worker.connectionCount;
selectedWorker = worker;
}
}
if (selectedWorker) {
selectedWorker.send('handle-request', req.socket);
}
}).listen(8000);
}
Ishlash monitoringi va ko‘rsatkichlar
Klasteringizning ishlashini kuzatish sog‘lom dasturni saqlash uchun juda muhimdir. Bu yerda asosiy ko‘rsatkichlar to‘plamini qanday amalga oshirish mumkin:
const cluster = require('cluster');
const os = require('os');
const promClient = require('prom-client');
if (cluster.isMaster) {
// Create metrics registry
const register = new promClient.Registry();
promClient.collectDefaultMetrics({ register });
// Custom metrics
const workerRequests = new promClient.Counter({
name: 'worker_requests_total',
help: 'Total requests handled by worker',
labelNames: ['worker_pid']
});
register.registerMetric(workerRequests);
// Fork workers
for (let i = 0; i < os.cpus().length; i++) {
const worker = cluster.fork();
worker.on('message', (msg) => {
if (msg.type === 'request_processed') {
workerRequests.inc({ worker_pid: worker.process.pid });
}
});
}
// Expose metrics endpoint
require('http').createServer(async (req, res) => {
if (req.url === '/metrics') {
res.setHeader('Content-Type', register.contentType);
res.end(await register.metrics());
}
}).listen(9090);
} else {
// Worker code
let requestCount = 0;
require('http').createServer((req, res) => {
requestCount++;
process.send({ type: 'request_processed' });
res.end(`Request ${requestCount} handled by worker ${process.pid}\n`);
}).listen(8000);
}
Monitoring uchun asosiy ko‘rsatkichlar
- So‘rov darajasi: Har bir ishchi uchun sekundiga so‘rovlar
- Xatolik darajasi: soniyada xato javoblari
- Javob vaqti: P50, P90, P99 javob vaqtlari
- CPUdan foydalanish: Har bir ishchi uchun protsessordan foydalanish
- Xotiradan foydalanish: Har bir ishchi uchun yig‘ma va RSS xotirasi
- Voqealar davrining kechikishi: Voqealar siklidagi kechikish
Konteyner integratsiyasi
Docker va Kubernetes kabi konteynerlashtirilgan muhitda ishlayotganda, quyidagi eng yaxshi amaliyotlarni ko‘rib chiqing:
1. Jarayonlarni boshqarish
// Dockerfile example for a Node.js cluster app
FROM node:16-slim
WORKDIR /app
COPY package*.json ./
RUN npm install --production
# Copy application code
COPY . .
# Use the node process as PID 1 for proper signal handling
CMD ["node", "cluster.js"]
# Health check
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost:8080/health || exit 1
2. Kubernetes’da joylashtirish
# k8s-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: node-cluster-app
spec:
replicas: 3 # Number of pods
selector:
matchLabels:
app: node-cluster
template:
metadata:
labels:
app: node-cluster
spec:
containers:
- name: node-app
image: your-image:latest
ports:
- containerPort: 8000
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "1Gi"
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
Umumiy xatolar va echimlar
1. Ishchilarda xotiraning oqishi
Muammo: Ishchi jarayonlarida xotiraning oqishi xotiraning asta-sekin o‘sishiga olib kelishi mumkin.
Yechim: Xotiradan foydalanish asosida ishchilarni qayta ishlashni amalga oshiring.
// In worker process
const MAX_MEMORY_MB = 500; // Max memory in MB before recycling
function checkMemory() {
const memoryUsage = process.memoryUsage();
const memoryMB = memoryUsage.heapUsed / 1024 / 1024;
if (memoryMB > MAX_MEMORY_MB) {
console.log(`Worker ${process.pid} memory ${memoryMB.toFixed(2)}MB exceeds limit, exiting...`);
process.exit(1); // Let cluster restart the worker
}
}
// Check memory every 30 seconds
setInterval(checkMemory, 30000);
2. Momaqaldiroq podasi muammosi
Muammo: Barcha ishchilar qayta ishga tushirilgandan so‘ng bir vaqtning o‘zida ulanishlarni qabul qilishmoqda.
Yechim: Asta-sekin ishga tushirishni amalga oshiring.
// In master process
if (cluster.isMaster) {
const numWorkers = require('os').cpus().length;
function forkWorker(delay) {
setTimeout(() => {
const worker = cluster.fork();
console.log(`Worker ${worker.process.pid} started after ${delay}ms delay`);
}, delay);
}
// Stagger worker starts by 1 second
for (let i = 0; i < numWorkers; i++) {
forkWorker(i * 1000);
}
}
3. Ishchilarning ochligi
Muammo: Ba’zi ishchilar boshqalarga qaraganda ko‘proq yuk olishadi.
Yechim: To‘g‘ri yukni muvozanatlash va monitoringini amalga oshirish.
// Track request distribution
const requestDistribution = new Map();
// In master process
if (cluster.isMaster) {
// ...
// Monitor request distribution
setInterval(() => {
console.log('Request distribution:');
requestDistribution.forEach((count, pid) => {
console.log(` Worker ${pid}: ${count} requests`);
});
}, 60000);
// Track requests per worker
cluster.on('message', (worker, message) => {
if (message.type === 'request_handled') {
const count = requestDistribution.get(worker.process.pid) || 0;
requestDistribution.set(worker.process.pid, count + 1);
}
});
}
Xulosa
Node.js Cluster moduli bir nechta protsessor yadrolari bo‘ylab ilovangizni masshtablashning samarali usulini taqdim etadi:
- Bir nechta ishchi jarayonlarini boshqaradigan asosiy jarayonni yaratadi
- Ishchilar bir xil server portini baham ko‘rishadi, bu esa yukni muvozanatlash imkonini beradi
- Ilovaning ishlashi va chidamliligini yaxshilaydi
- Nol ishlamay qolgan qayta ishga tushirish va qulay o‘chirishni yoqadi
- Usta va ishchilar o‘rtasidagi aloqa uchun IPC dan foydalanadi
Klasterlashni tushunish va to‘g‘ri amalga oshirish orqali siz barcha mavjud protsessor resurslaridan samarali foydalanadigan yuqori samarali, ishonchli Node.js ilovalarini yaratishingiz mumkin.
W3Schools Pathfinder
Yutuqlaringizni kuzating – bu bepul!
