Cluster moduli


ULASHISH

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 jarayonlarini yaratish va boshqarish
  • Xodimlarning sog‘lig‘ini kuzatish
  • Halokatga uchragan ishchilarni qayta ishga tushirish
  • Load balancing (distributing connections)
Ishchi
  • Haqiqiy dastur kodini ishga tushirish
  • Kiruvchi so‘rovlarni ko‘rib chiqish
  • Ma’lumotlarni qayta ishlash
  • Biznes mantiqini amalga oshirish


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:

  1. Asosiy jarayon protsessor yadrolari sonini aniqlaydi
  2. U har bir protsessor uchun bitta ishchini ajratadi
  3. Har bir worker bir xil portda (8000) HTTP server yaratadi
  4. Klaster moduli kiruvchi ulanishlarni avtomatik ravishda yuklaydi
  5. 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:

  1. Ishchilarning dastlabki to‘plamini yaratish
  2. Har bir ishchini birma-bir almashtirish
  3. Eskisini o‘chirishdan oldin yangi ishchi tinglashiga ishonch hosil qiling
  4. 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:

  1. IPC xabar almashinuvidan foydalaning (aloqa misolida ko‘rsatilganidek)
  2. Redis, MongoDB yoki fayl tizimi (fs) kabi tashqi xotiradan foydalaning
  3. 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:

  1. Murakkab xashing algoritmidan foydalaning
  2. IP manzillar o‘rniga cookie fayllari yoki boshqa seans identifikatorlaridan foydalaning
  3. 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!