Node.js Cluster Module

Node.js CPU

ক্লাস্টার মডিউল কি?

ক্লাস্টার মডিউল একই সার্ভার পোর্ট ভাগ করে এমন একাধিক কর্মী প্রক্রিয়া তৈরি করার একটি উপায় প্রদান করে।

Node.js single-threaded , Cluster - CPU .

প্রতিটি কর্মী তার নিজস্ব প্রক্রিয়ায় তার নিজস্ব ইভেন্ট লুপ এবং মেমরি স্পেসে চলে, কিন্তু তারা সবাই একই সার্ভার পোর্ট ভাগ করে।

মাস্টার প্রক্রিয়া শ্রমিক তৈরি এবং তাদের মধ্যে আগত সংযোগ বিতরণের জন্য দায়ী।

ক্লাস্টার মডিউল আমদানি করা হচ্ছে

ক্লাস্টার মডিউলটি ডিফল্টরূপে Node.js-এ অন্তর্ভুক্ত থাকে।

আপনি প্রয়োজন করে আপনার স্ক্রিপ্টে এটি ব্যবহার করতে পারেন:

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`);
}

কিভাবে ক্লাস্টারিং কাজ করে

ক্লাস্টার মডিউল একটি মাস্টার প্রক্রিয়া তৈরি করে কাজ করে যা একাধিক কর্মী প্রক্রিয়া তৈরি করে।

মাস্টার প্রক্রিয়া অ্যাপ্লিকেশন কোড চালায় না, কিন্তু কর্মীদের পরিচালনা করে।

প্রতিটি কর্মী প্রক্রিয়া একটি নতুন Node.js উদাহরণ যা আপনার অ্যাপ্লিকেশন কোড স্বাধীনভাবে চালায়।

🔧দ্রষ্টব্য:

মূলত, ক্লাস্টার মডিউল নতুন কর্মী তৈরি করতে চাইল্ড প্রসেস মডিউলের ফর্ক() পদ্ধতি ব্যবহার করে।

প্রক্রিয়ার ধরন দায়িত্বশীল
ওস্তাদ
  • শ্রম প্রক্রিয়া তৈরি এবং পরিচালনা
  • শ্রম স্বাস্থ্য পর্যবেক্ষণ
  • বিপর্যস্ত শ্রমিকদের পুনরায় চালু করা হচ্ছে
  • লোড ব্যালেন্সিং (সংযোগ বিতরণ)
শ্রম
  • প্রকৃত অ্যাপ্লিকেশন কোড চলমান
  • ইনকামিং অনুরোধ হ্যান্ডলিং
  • তথ্য প্রক্রিয়াকরণ
  • ব্যবসা লজিক চলমান

একটি মৌলিক ক্লাস্টার তৈরি করা হচ্ছে

এখানে প্রতিটি CPU এর জন্য কর্মী প্রক্রিয়া সহ একটি ক্লাস্টার তৈরি করার একটি সহজ উদাহরণ রয়েছে:

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`);
}

এই উদাহরণে:

মাস্টার প্রক্রিয়া CPU কোরের সংখ্যা সনাক্ত করে
এটি সিপিইউ প্রতি একজন কর্মীকে কাঁটা দেয়
প্রতিটি কর্মী একই পোর্টে (8000) একটি HTTP সার্ভার তৈরি করে।
ক্লাস্টার মডিউল স্বয়ংক্রিয়ভাবে ইনকামিং সংযোগের ভারসাম্য লোড করে
একজন কর্মী ক্রাশ হলে, মাস্টার একটি নতুন তৈরি করে

শ্রম সম্পর্ক

চাইল্ড প্রসেস মডিউলে আইপিসি যেভাবে কাজ করে সেরকম, সেন্ড() পদ্ধতি এবং মেসেজ ইভেন্ট ব্যবহার করে মাস্টার এবং কর্মী প্রক্রিয়ার মধ্যে যোগাযোগ করা যেতে পারে।

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);
}

জিরো-ড্রপ রিস্টার্ট

ক্লাস্টারিংয়ের প্রধান সুবিধাগুলির মধ্যে একটি হল ডাউনটাইম ছাড়াই কর্মীদের পুনরায় চালু করার ক্ষমতা। এটি আপনার অ্যাপ্লিকেশনের আপডেট প্রকাশের জন্য দরকারী।

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`);
}

এই উদাহরণটি ব্যাখ্যা করে:

প্রাথমিক কর্মীদের একটি পুল নির্মাণ
একে একে প্রতি কর্মী প্রতিস্থাপন
পুরানোটি কাটার আগে নিশ্চিত করা যে একজন নতুন কর্মী শুনছে
অপ্রত্যাশিত শ্রমিক মৃত্যু সদয়ভাবে পরিচালনা করা

লোড ব্যালেন্সিং

কর্মী প্রক্রিয়ার মধ্যে ইনকামিং সংযোগ বিতরণের জন্য ক্লাস্টার মডিউলটিতে একটি অন্তর্নির্মিত লোড ব্যালেন্সার রয়েছে।

দুটি প্রাথমিক কৌশল আছে:

রাউন্ড-রবিন (ডিফল্ট)

উইন্ডোজ ব্যতীত সমস্ত প্ল্যাটফর্মে, Node.js একটি রাউন্ড-রবিন পদ্ধতি ব্যবহার করে সংযোগ বিতরণ করে, যেখানে মাস্টার সংযোগগুলি গ্রহণ করে এবং একটি বৃত্তাকার ক্রমে কর্মীদের মধ্যে বিতরণ করে।

💻দ্রষ্টব্য:

উইন্ডোজে, পোর্টগুলি কীভাবে পরিচালনা করা হয় তার কারণে লোড বিতরণ ভিন্নভাবে কাজ করে। উইন্ডোজে, কর্মীরা সংযোগ গ্রহণ করার জন্য প্রতিযোগিতা করে।

প্রাথমিক কর্মী

cluster.schedulingPolicy :

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`);
}

শেয়ার করা স্ট্যাটাস

যেহেতু প্রতিটি কর্মী তার নিজস্ব প্রক্রিয়ায় তার নিজস্ব মেমরি স্পেসে চলে, তারা ভেরিয়েবলের মাধ্যমে সরাসরি রাজ্য ভাগ করতে পারে না। পরিবর্তে, আপনি:

একটি IPC বার্তা ব্যবহার করুন (যোগাযোগের উদাহরণে দেখানো হয়েছে)
রেডিস, মঙ্গোডিবি বা একটি ফাইল সিস্টেমের মতো বাহ্যিক স্টোরেজ ব্যবহার করুন
সেশন পরিচালনার জন্য স্টিকি লোড ব্যালেন্সিং ব্যবহার করুন

স্টিকি সেশনের উদাহরণ

স্টিকি সেশনগুলি নিশ্চিত করে যে একই ক্লায়েন্টের অনুরোধগুলি সর্বদা একই কর্মী প্রক্রিয়ায় যায়:

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`);
}

এটি একটি সরলীকৃত উদাহরণ যা স্টিকি সেশনের ধারণা প্রদর্শন করে। উত্পাদনে, আপনি সাধারণত:

একটি উন্নত হ্যাশিং অ্যালগরিদম ব্যবহার করুন
IP ঠিকানার পরিবর্তে কুকিজ বা অন্যান্য সেশন শনাক্তকারী ব্যবহার করুন
অত্যন্ত যত্ন সহকারে সকেট সংযোগগুলি পরিচালনা করুন

শ্রম জীবন চক্র

আপনার ক্লাস্টারকে সঠিকভাবে পরিচালনা করার জন্য কর্মী জীবনচক্র বোঝা গুরুত্বপূর্ণ:

ঘটনা ব্যাখ্যা
fork একটি নতুন কর্মী কাঁটাচামচ করা হলে ছেড়ে দেওয়া হয়
online যখন কর্মী চলছে এবং বার্তা প্রক্রিয়া করার জন্য প্রস্তুত তখন নির্গত হয়
listening কর্মী সংযোগের জন্য জিজ্ঞাসা শুরু করলে ছেড়ে দেওয়া হয়
disconnect একজন কর্মীর IPC চ্যানেল সংযোগ বিচ্ছিন্ন হলে ইস্যু করা হয়
exit একজন কর্মী প্রক্রিয়া প্রস্থান করলে মুক্তি পায়
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);
  });
}

চমৎকার আলিঙ্গন

প্রস্থান করার আগে আপনার কর্মী প্রক্রিয়াগুলিকে বিদ্যমান অনুরোধগুলি সম্পূর্ণ করার অনুমতি দেওয়ার জন্য একটি আকর্ষণীয় শাটডাউন গুরুত্বপূর্ণ।

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));
  });
}

সর্বোত্তম অনুশীলন

শ্রমিকের সংখ্যা:বেশিরভাগ ক্ষেত্রে, CPU কোর প্রতি একজন কর্মী তৈরি করুন
স্ট্যাটিক ডিজাইন:ক্লাস্টারগুলির সাথে দক্ষতার সাথে কাজ করার জন্য আপনার অ্যাপ্লিকেশনটিকে স্ট্যাটিক করতে ডিজাইন করুন৷
সুন্দর আলিঙ্গন:সংযোগ বাদ এড়াতে সঠিক শাটডাউন হ্যান্ডলিং প্রয়োগ করুন
শ্রম পর্যবেক্ষণ:বিধ্বস্ত শ্রমিকদের দ্রুত ট্র্যাক করুন এবং প্রতিস্থাপন করুন
ডাটাবেস সংযোগ:প্রতিটি কর্মীর নিজস্ব সংযোগ পুল আছে, তাই সেই অনুযায়ী ডাটাবেস সংযোগ কনফিগার করুন
ভাগ করা সম্পদ:কর্মীদের মধ্যে ভাগ করা সম্পদ (যেমন ফাইল লক) সম্পর্কে সতর্ক থাকুন
কর্মীদের হালকা রাখুন:কর্মী প্রক্রিয়ায় অপ্রয়োজনীয় মেমরি ব্যবহার এড়িয়ে চলুন

⚠️সতর্কতা:

একাধিক কর্মী ব্যবহার করার সময় ফাইল-ভিত্তিক লকিং এবং অন্যান্য ভাগ করা সংস্থানগুলির সাথে সতর্ক থাকুন৷ একটি একক-প্রক্রিয়া অ্যাপ্লিকেশনে নিরাপদ ক্রিয়াকলাপ একাধিক কর্মীদের সাথে প্রতিযোগিতার পরিস্থিতি সৃষ্টি করতে পারে।

ক্লাস্টার মডিউলের বিকল্প

যদিও ক্লাস্টার মডিউল শক্তিশালী, একাধিক কোরে Node.js অ্যাপ্লিকেশন চালানোর বিকল্প রয়েছে:

পন্থা ব্যাখ্যা কেস ব্যবহার করুন
PM2 বিল্ট-ইন লোড ব্যালেন্সিং এবং ক্লাস্টারিং সহ Node.js অ্যাপ্লিকেশনগুলির জন্য একটি প্রক্রিয়া পরিচালক৷ ম্যানুফ্যাকচারিং অ্যাপ্লিকেশন যার জন্য শক্তিশালী প্রক্রিয়া পরিচালনার প্রয়োজন
লোড ব্যালেন্সিং Nginx এর মত লোড ব্যালেন্সারের পিছনে একাধিক Node.js দৃষ্টান্ত চালানো একাধিক সার্ভার বা পাত্রে লোড বিতরণ করা
শ্রম পাঠ্য CPU- নিবিড় কাজের জন্য হালকা স্ক্রিপ্টিং (Node.js >= 10.5.0) একটি একক প্রক্রিয়ার মধ্যে CPU- নিবিড় অপারেশন
পাত্রে একাধিক কন্টেইনারাইজড উদাহরণ চালানো হচ্ছে (ডকার এবং কুবারনেটসের সাথে) আধুনিক ক্লাউড পরিবেশে স্কেলেবল, বিতরণ করা অ্যাপ্লিকেশন

উন্নত লোড ব্যালেন্সিং কৌশল

যদিও ক্লাস্টার মডিউলের ডিফল্ট রাউন্ড-রবিন লোড ব্যালেন্সিং অনেক অ্যাপ্লিকেশনের জন্য ভাল কাজ করে, নির্দিষ্ট ব্যবহারের ক্ষেত্রে উন্নত কৌশলগুলির প্রয়োজন হতে পারে।

1. ওজন রাউন্ড-রবিন

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. কম সংযোগ

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);
}

কর্মক্ষমতা নিরীক্ষণ এবং মেট্রিক্স

একটি সুস্থ অ্যাপ্লিকেশন বজায় রাখার জন্য আপনার ক্লাস্টারের কর্মক্ষমতা নিরীক্ষণ করা গুরুত্বপূর্ণ। এখানে মৌলিক মেট্রিক্স সংগ্রহ কিভাবে বাস্তবায়ন করা যায়:

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);
}

নিরীক্ষণের জন্য মূল মেট্রিক্স

চাহিদার হার:প্রতি কর্মী প্রতি সেকেন্ডে অনুরোধ
ত্রুটি হার:প্রতি সেকেন্ডে ত্রুটি প্রতিক্রিয়া
প্রতিক্রিয়া সময়:P50, P90, P99 প্রতিক্রিয়ার সময়
CPU ব্যবহার:কর্মী প্রতি CPU ব্যবহার
মেমরি ব্যবহার:কর্মী প্রতি গাদা এবং আরএসএস মেমরি
ইভেন্ট লুপ ব্যাকএন্ড:ইভেন্ট লুপে বিলম্ব

কন্টেইনার ইন্টিগ্রেশন

ডকার এবং কুবারনেটসের মতো কন্টেইনারাইজড পরিবেশে চালানোর সময়, এই সেরা অনুশীলনগুলি বিবেচনা করুন:

1. প্রক্রিয়া ব্যবস্থাপনা

// 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 Deployment

# 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

সাধারণ সমস্যা এবং সমাধান

1. কর্মীদের মেমরি লিক

সমস্যা:কর্মী প্রক্রিয়ায় মেমরি ফাঁস ধীরে ধীরে স্মৃতি বৃদ্ধির কারণ হতে পারে।

সমাধান:মেমরি ব্যবহারের উপর ভিত্তি করে কর্মী পুনর্ব্যবহারযোগ্য সক্ষম করুন৷

// 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. থান্ডার হার্ড সমস্যা

সমস্যা:পুনরায় চালু করার পরে সমস্ত কর্মী একযোগে সংযোগ গ্রহণ করে।

সমাধান:স্তম্ভিত স্টার্টআপ সক্ষম করুন৷

// 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. শ্রমের অনাহার

সমস্যা:কিছু শ্রমিক অন্যদের তুলনায় একটি ভারী লোড পেতে.

সমাধান:সঠিক লোড ব্যালেন্সিং এবং মনিটরিং বাস্তবায়ন করুন।

// 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);
    }
  });
}

সারাংশ

Node.js Cluster CPU :

একটি মাস্টার প্রক্রিয়া তৈরি করে যা একাধিক কর্মী প্রক্রিয়া পরিচালনা করে
শ্রমিকরা একই সার্ভার পোর্ট ভাগ করে, লোড ব্যালেন্সিংয়ের অনুমতি দেয়
অ্যাপ্লিকেশন কর্মক্ষমতা এবং নমনীয়তা উন্নত
জিরো-ড্রপ রিস্টার্ট এবং আকর্ষণীয় শাটডাউন সক্ষম করে
মাস্টার এবং কর্মীদের মধ্যে যোগাযোগের জন্য IPC ব্যবহার করে

ক্লাস্টারিংকে সঠিকভাবে বোঝা এবং প্রয়োগ করে, আপনি উচ্চ-কর্মক্ষমতা, নির্ভরযোগ্য Node.js অ্যাপ্লিকেশন তৈরি করতে পারেন যা সমস্ত উপলব্ধ CPU সংস্থানগুলির দক্ষ ব্যবহার করে।

অনুশীলন করুন

সঠিক মডিউল নাম নির্বাচন করুন.

The ______ module allows you to create child processes that run simultaneously and share the same server port.

child_process
✗ ভুল! "child_process" মডিউল শিশু প্রক্রিয়া তৈরি করে, কিন্তু তারা একই পোর্ট ভাগ করে না
worker_threads
✗ ভুল! "worker_threads" মডিউল থ্রেড তৈরি করে, চাইল্ড প্রসেস নয়, এবং তারা পোর্ট শেয়ার করে না
cluster
✓ ঠিক আছে! "ক্লাস্টার" মডিউল আপনাকে চাইল্ড প্রসেস তৈরি করতে দেয় যা একই সার্ভার পোর্ট শেয়ার করে
os
✗ ভুল! "OS" মডিউল অপারেটিং সিস্টেম-সম্পর্কিত তথ্য প্রদান করে, কিন্তু শিশু প্রক্রিয়া তৈরি করে না