面试题答案
一键面试1. 异常监控设计
- 在Node.js应用层面:
- 使用
try - catch
块包裹异步操作,如async/await
函数中的代码,捕获未处理的Promise拒绝和同步代码中的异常。例如:
async function someAsyncOperation() { try { const result = await someAsyncFunction(); return result; } catch (error) { // 调用异常处理函数 handleError(error); } }
- 为
process
对象的uncaughtException
和unhandledRejection
事件添加监听器,捕获全局未处理的异常和拒绝。
process.on('uncaughtException', (error) => { handleError(error); }); process.on('unhandledRejection', (reason, promise) => { handleError(reason, { promise }); });
- 使用
- 添加唯一标识符:为每个请求生成唯一的请求ID(如使用
uuid
库),并在整个请求处理过程中传递该ID。这样在异常发生时,通过该ID可以追踪异常来源于哪个具体请求。例如:const uuid = require('uuid'); app.use((req, res, next) => { req.requestId = uuid.v4(); next(); });
2. 日志记录设计
- 日志格式:采用结构化日志格式,如JSON,方便后续聚合和分析。每条日志应包含时间戳、请求ID、节点标识、日志级别(如
info
、warn
、error
)、日志消息等字段。例如:{ "timestamp": "2023 - 10 - 01T12:00:00Z", "requestId": "123e4567 - e89b - 12d3 - a456 - 426614174000", "nodeId": "node - 1", "level": "error", "message": "Database connection error" }
- 本地日志存储:在每个节点上使用日志库(如
winston
)将日志记录到本地文件。可以按日期或文件大小进行切割,以管理日志文件大小。例如,使用winston
配置如下:const winston = require('winston'); const { createLogger, format, transports } = winston; const { combine, timestamp, json } = format; const logger = createLogger({ format: combine(timestamp(), json()), transports: [ new transports.File({ filename: 'error.log', level: 'error' }), new transports.File({ filename: 'combined.log' }) ] });
3. 分布式日志聚合与分析
- 日志传输:
- 使用消息队列(如Kafka或RabbitMQ)将本地日志发送到中央日志聚合服务器。每个节点将日志消息发布到消息队列的特定主题。例如,在Node.js中使用
kafka - node
库发送日志:
const kafka = require('kafka - node'); const Producer = kafka.Producer; const client = new kafka.Client(); const producer = new Producer(client); function sendLogToKafka(logMessage) { const payloads = [ { topic: 'logs - topic', messages: JSON.stringify(logMessage) } ]; producer.send(payloads, (err, data) => { if (err) { console.error('Error sending log to Kafka:', err); } }); }
- 使用消息队列(如Kafka或RabbitMQ)将本地日志发送到中央日志聚合服务器。每个节点将日志消息发布到消息队列的特定主题。例如,在Node.js中使用
- 聚合与分析:
- 在中央日志聚合服务器上,使用日志管理工具(如Elasticsearch + Kibana)来接收、存储和分析日志。Elasticsearch可以高效地存储和索引日志数据,Kibana提供可视化界面进行查询和分析。将从消息队列消费的日志数据存储到Elasticsearch中,通过Kibana可以按请求ID、节点、时间范围等条件进行查询和分析异常情况。
4. 处理网络延迟和节点故障
- 网络延迟:
- 在消息队列发送日志时,设置合理的重试机制。如果发送失败,根据一定的策略(如指数退避)进行重试。例如,在上述Kafka发送代码中添加重试逻辑:
const MAX_RETRIES = 3; function sendLogToKafka(logMessage, retries = 0) { const payloads = [ { topic: 'logs - topic', messages: JSON.stringify(logMessage) } ]; producer.send(payloads, (err, data) => { if (err && retries < MAX_RETRIES) { const delay = 2 ** retries * 1000; setTimeout(() => { sendLogToKafka(logMessage, retries + 1); }, delay); } else if (err) { console.error('Failed to send log to Kafka after retries:', err); } }); }
- 节点故障:
- 采用冗余设计,在多个节点上备份关键的监控和日志记录组件。例如,对于消息队列,可以使用多副本机制确保即使某个节点故障,消息也不会丢失。同时,在节点故障恢复后,重新同步未发送的日志数据到消息队列或中央聚合服务器。可以在本地日志文件中标记已发送的日志,以便故障恢复后知道从何处继续发送。