面试题答案
一键面试缓冲区管理
- 合理设置缓冲区大小:
- 在 Node.js 的
net.Socket
中,可以通过socket.setEncoding()
来设置数据的接收编码,并且合理设置socket.setNoDelay(true)
禁用 Nagle 算法,以减少延迟。对于缓冲区大小,net.Server
和net.Socket
都有默认的缓冲区设置。如果每个连接传输大量数据,可以适当增大net.Socket
的写入缓冲区大小。 - 示例代码:
const net = require('net'); const server = net.createServer((socket) => { socket.setNoDelay(true); socket.setEncoding('utf8'); // 假设增大写入缓冲区到 64KB socket.writableHighWaterMark = 65536; }); server.listen(8080, () => { console.log('Server listening on port 8080'); });
- 在 Node.js 的
- 高效的缓冲区复用:
- 使用
Buffer.allocUnsafe()
预先分配内存来复用缓冲区,减少频繁的内存分配开销。在数据接收处理过程中,将接收到的数据填充到复用的缓冲区中。 - 示例代码:
const net = require('net'); const bufferSize = 1024 * 1024; // 1MB 缓冲区 const buffer = Buffer.allocUnsafe(bufferSize); let bufferOffset = 0; const server = net.createServer((socket) => { socket.on('data', (chunk) => { chunk.copy(buffer, bufferOffset); bufferOffset += chunk.length; // 处理 buffer 中的数据 //... }); }); server.listen(8080, () => { console.log('Server listening on port 8080'); });
- 使用
事件处理机制
- 使用事件驱动模型:
- Node.js 本身基于事件驱动,在 TCP 服务端中,利用
net.Server
的'connection'
事件来处理新连接,net.Socket
的'data'
事件处理接收到的数据,'end'
事件处理连接结束等。通过这种方式,能够高效地处理大量并发连接而不会阻塞主线程。 - 示例代码:
const net = require('net'); const server = net.createServer((socket) => { socket.on('data', (data) => { console.log('Received data:', data.toString()); }); socket.on('end', () => { console.log('Connection ended'); }); }); server.on('connection', (socket) => { console.log('New connection'); }); server.listen(8080, () => { console.log('Server listening on port 8080'); });
- Node.js 本身基于事件驱动,在 TCP 服务端中,利用
- 利用
cluster
模块实现多进程:- 对于多核 CPU 的服务器,可以使用
cluster
模块将 TCP 服务端负载分发到多个工作进程中,每个进程处理一部分连接,充分利用多核 CPU 的性能。 - 示例代码:
const cluster = require('cluster'); const net = require('net'); const numCPUs = require('os').cpus().length; if (cluster.isMaster) { console.log(`Master ${process.pid} is running`); for (let i = 0; i < numCPUs; i++) { cluster.fork(); } cluster.on('exit', (worker, code, signal) => { console.log(`worker ${worker.process.pid} died`); }); } else { const server = net.createServer((socket) => { socket.write('Hello from worker'+ process.pid + '\n'); socket.end(); }); server.listen(8080, () => { console.log('Worker'+ process.pid +'listening on port 8080'); }); }
- 对于多核 CPU 的服务器,可以使用
流控制
- 背压处理:
- 当客户端发送数据过快,服务端来不及处理时,会出现背压问题。在 Node.js 中,
net.Socket
是Duplex
流,可以通过监听'drain'
事件来处理背压。当写入缓冲区已满,socket.write()
返回false
,此时需要暂停写入,直到'drain'
事件触发。 - 示例代码:
const net = require('net'); const server = net.createServer((socket) => { let paused = false; function writeData() { if (paused) return; const data = 'a'.repeat(1024 * 1024); // 大量数据 const writeResult = socket.write(data); if (!writeResult) { paused = true; } } socket.on('drain', () => { paused = false; writeData(); }); writeData(); }); server.listen(8080, () => { console.log('Server listening on port 8080'); });
- 当客户端发送数据过快,服务端来不及处理时,会出现背压问题。在 Node.js 中,
- 流量限制:
- 可以使用第三方模块如
limiter
来限制每个连接的流量,避免某个连接占用过多带宽影响其他连接。 - 安装
limiter
:npm install limiter
- 示例代码:
const net = require('net'); const { RateLimiterMemory } = require('limiter'); const limiter = new RateLimiterMemory({ tokensPerInterval: 1024 * 1024, // 1MB 每秒 interval: '1s' }); const server = net.createServer((socket) => { socket.on('data', async (data) => { try { await limiter.removeTokens(data.length); // 处理数据 console.log('Received data:', data.toString()); } catch (err) { socket.write('Rate limit exceeded\n'); socket.end(); } }); }); server.listen(8080, () => { console.log('Server listening on port 8080'); });
- 可以使用第三方模块如