面试题答案
一键面试常见负载均衡策略
- Round - Robin(轮询):
- 原理:按顺序依次将请求分配给每个子进程。例如有子进程
worker1
、worker2
、worker3
,第一个请求给worker1
,第二个给worker2
,第三个给worker3
,第四个又回到worker1
依次类推。 - 在Node.js Cluster模块默认使用的就是基于操作系统底层的轮询负载均衡方式(在Linux下基于SO_REUSEPORT实现),应用层无需额外代码实现。
- 原理:按顺序依次将请求分配给每个子进程。例如有子进程
- Least - Connections(最少连接数):
- 原理:优先将请求分配给当前连接数最少的子进程。这种策略能更好地利用系统资源,避免某个子进程负载过重。
- 在Node.js Cluster模块原生未直接提供此策略实现,需手动实现。大致思路是维护一个记录每个子进程当前连接数的对象,每次有新请求时,遍历该对象找到连接数最少的子进程进行请求分配。
自定义负载均衡策略设计思路及关键代码实现
- 设计思路:
- 基于CPU使用率的负载均衡策略。定期获取每个子进程的CPU使用率,将新请求分配给CPU使用率最低的子进程,这样能更合理地利用系统资源。
- 初始化时,为每个子进程启动一个定时器,定期获取CPU使用率。
- 当有新请求到来时,比较各子进程的CPU使用率,选择使用率最低的子进程来处理请求。
- 关键代码实现:
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使用率。