面试题答案
一键面试优化方案
- 使用日志队列:
- 引入一个队列(如
async - queue
),当请求到达记录日志的中间件时,将日志信息放入队列,而不是直接进行文件I/O操作。这样可以避免大量请求同时竞争文件I/O资源。 - 示例代码:
const asyncQueue = require('async - queue'); const logQueue = asyncQueue((logEntry, callback) => { // 这里进行实际的文件写入操作 fs.appendFile('log.txt', logEntry + '\n', (err) => { if (err) { console.error('Error writing to log file:', err); } callback(); }); }, 1); // 并发数设为1,确保顺序写入 app.use((req, res, next) => { const logEntry = `${new Date().toISOString()} ${req.method} ${req.url}`; logQueue.push(logEntry); next(); });
- 引入一个队列(如
- 异步写入:
- 使用Node.js的异步文件I/O操作(如
fs.appendFile
的异步版本),这样在进行文件写入时不会阻塞事件循环,从而不影响其他请求的处理。
- 使用Node.js的异步文件I/O操作(如
- 日志缓存:
- 可以设置一个缓存(如内存缓存),当缓存达到一定大小(如100条日志)或者达到一定时间间隔(如1秒),再批量将缓存中的日志写入文件。这减少了文件I/O的频率。
- 示例代码:
let logCache = []; const cacheInterval = 1000; // 1秒 const cacheSize = 100; setInterval(() => { if (logCache.length > 0) { const logString = logCache.join('\n'); fs.appendFile('log.txt', logString + '\n', (err) => { if (err) { console.error('Error writing cached logs to file:', err); } }); logCache = []; } }, cacheInterval); app.use((req, res, next) => { const logEntry = `${new Date().toISOString()} ${req.method} ${req.url}`; logCache.push(logEntry); if (logCache.length >= cacheSize) { const logString = logCache.join('\n'); fs.appendFile('log.txt', logString + '\n', (err) => { if (err) { console.error('Error writing cached logs to file:', err); } }); logCache = []; } next(); });
资源竞争和线程安全问题考虑
- 队列和并发控制:
- 如上述使用队列时,将队列的并发数设置为1(
asyncQueue((logEntry, callback) => { /*... */ }, 1)
),确保日志是顺序写入文件的,避免多个写入操作同时进行导致数据错乱。
- 如上述使用队列时,将队列的并发数设置为1(
- 锁机制(在需要时):
- 如果在缓存写入等操作中有共享资源(如共享的缓存数组),可以使用简单的锁机制。例如,设置一个标志位表示是否正在进行写入操作,在写入前检查并设置标志位,写入完成后清除标志位。
let isWriting = false; const writeCacheToFile = () => { if (isWriting) { return; } isWriting = true; const logString = logCache.join('\n'); fs.appendFile('log.txt', logString + '\n', (err) => { if (err) { console.error('Error writing cached logs to file:', err); } isWriting = false; logCache = []; }); };
- 使用线程安全的库:
- 选择线程安全的日志库(如
winston
等),这些库在设计上已经考虑了多线程或高并发场景下的安全问题,能有效避免资源竞争。例如,winston
支持多种日志传输(包括文件传输),并且在内部处理了并发写入等问题。
const winston = require('winston'); const logger = winston.createLogger({ level: 'info', format: winston.format.json(), transports: [ new winston.transport.Console(), new winston.transport.File({ filename: 'log.txt' }) ] }); app.use((req, res, next) => { logger.info(`${new Date().toISOString()} ${req.method} ${req.url}`); next(); });
- 选择线程安全的日志库(如