MST

星途 面试题库

面试题:Node.js Cluster模块负载均衡策略

在Node.js的Cluster模块中,有哪些常见的负载均衡策略用于将请求分配到各个子进程?如果让你自定义一种负载均衡策略,你会如何设计,简述思路及关键代码实现?
41.3万 热度难度
前端开发Node.js

知识考点

AI 面试

面试题答案

一键面试

常见负载均衡策略

  1. Round - Robin(轮询)
    • 原理:按顺序依次将请求分配给每个子进程。例如有子进程 worker1worker2worker3,第一个请求给 worker1,第二个给 worker2,第三个给 worker3,第四个又回到 worker1 依次类推。
    • 在Node.js Cluster模块默认使用的就是基于操作系统底层的轮询负载均衡方式(在Linux下基于SO_REUSEPORT实现),应用层无需额外代码实现。
  2. Least - Connections(最少连接数)
    • 原理:优先将请求分配给当前连接数最少的子进程。这种策略能更好地利用系统资源,避免某个子进程负载过重。
    • 在Node.js Cluster模块原生未直接提供此策略实现,需手动实现。大致思路是维护一个记录每个子进程当前连接数的对象,每次有新请求时,遍历该对象找到连接数最少的子进程进行请求分配。

自定义负载均衡策略设计思路及关键代码实现

  1. 设计思路
    • 基于CPU使用率的负载均衡策略。定期获取每个子进程的CPU使用率,将新请求分配给CPU使用率最低的子进程,这样能更合理地利用系统资源。
    • 初始化时,为每个子进程启动一个定时器,定期获取CPU使用率。
    • 当有新请求到来时,比较各子进程的CPU使用率,选择使用率最低的子进程来处理请求。
  2. 关键代码实现
const cluster = require('cluster');
const os = require('os');
const http = require('http');

if (cluster.isMaster) {
    const numCPUs = os.cpus().length;
    const cpuUsage = {};
    // 初始化子进程
    for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
    }
    // 监听子进程的消息
    cluster.on('message', (worker, msg) => {
        if (msg.cpuUsage) {
            cpuUsage[worker.id] = msg.cpuUsage;
        }
    });
    // 为每个子进程启动定时器获取CPU使用率
    Object.keys(cluster.workers).forEach((id) => {
        setInterval(() => {
            const worker = cluster.workers[id];
            if (worker) {
                worker.send({ getCPUUsage: true });
            }
        }, 1000);
    });
    // 创建HTTP服务器
    const server = http.createServer((req, res) => {
        // 找到CPU使用率最低的子进程
        let minUsageWorker;
        let minUsage = Infinity;
        Object.keys(cpuUsage).forEach((id) => {
            if (cpuUsage[id] < minUsage) {
                minUsage = cpuUsage[id];
                minUsageWorker = cluster.workers[id];
            }
        });
        if (minUsageWorker) {
            minUsageWorker.send({ req, res });
        } else {
            res.writeHead(500);
            res.end('No available worker');
        }
    });
    server.listen(3000);
} else {
    // 子进程代码
    let startUsage;
    let startTick;
    process.on('message', (msg) => {
        if (msg.getCPUUsage) {
            const cpus = os.cpus();
            let totalIdle = 0;
            let totalTick = 0;
            cpus.forEach((cpu) => {
                totalTick += cpu.times.user + cpu.times.nice + cpu.times.sys + cpu.times.irq;
                totalIdle += cpu.times.idle;
            });
            if (!startUsage) {
                startUsage = totalIdle;
                startTick = totalTick;
                return;
            }
            const endUsage = totalIdle;
            const endTick = totalTick;
            const cpuUsage = ((endTick - startTick) - (endUsage - startUsage)) / (endTick - startTick) * 100;
            process.send({ cpuUsage });
            startUsage = endUsage;
            startTick = endTick;
        } else if (msg.req && msg.res) {
            const req = msg.req;
            const res = msg.res;
            // 处理请求
            res.writeHead(200);
            res.end('Response from worker'+ cluster.worker.id);
        }
    });
}

以上代码实现了一个基于CPU使用率的自定义负载均衡策略,在主进程中根据子进程汇报的CPU使用率分配请求,子进程定期汇报自己的CPU使用率。