面试题答案
一键面试性能调优
- 利用事件循环
- 合理处理回调:确保回调函数执行时间尽可能短,避免在回调中执行长时间阻塞的操作。例如,避免在 I/O 回调中进行大量的 CPU 密集型计算。如果有这类计算,可考虑使用 Worker Threads 将其放到单独线程执行。
- 优化任务队列:Node.js 的事件循环会依次处理微任务队列和宏任务队列。合理安排任务,尽量将一些不重要的任务延迟执行,避免在主线程事件循环中堆积过多任务,影响高并发处理能力。例如,使用
setImmediate
来延迟执行一些非紧急的任务,它会在当前轮次事件循环的宏任务队列末尾执行。
- 非阻塞 I/O
- 高效使用 Stream:在 TCP 服务器中处理数据读写时,使用
Stream
来处理数据流。Stream
基于事件驱动,以非阻塞方式工作,能够高效处理大量数据。例如,使用net.Socket
的可读可写流特性,在数据可读时及时处理,而不是一次性读取大量数据导致内存占用过高。可以通过pipe
方法将可读流直接连接到可写流,实现数据的高效传输,如readableStream.pipe(writableStream)
。 - 连接池管理:对于需要与外部资源(如数据库)交互的情况,建立连接池。在高并发场景下,避免每次请求都创建新的连接,因为创建连接本身是一个 I/O 操作,会消耗资源和时间。使用连接池可以复用已有连接,提高效率。例如,在 Node.js 中操作 MySQL 数据库时,可以使用
mysql2
库的连接池功能。
- 高效使用 Stream:在 TCP 服务器中处理数据读写时,使用
- 操作系统资源
- 调整文件描述符限制:在高并发场景下,TCP 服务器可能会同时处理大量连接,每个连接都需要一个文件描述符。默认情况下,操作系统对文件描述符数量有限制。可以通过
ulimit -n
命令查看当前文件描述符限制,并通过修改系统配置文件(如/etc/security/limits.conf
)来提高限制。在 Node.js 中,可以在启动脚本中通过process.setMaxListeners
方法适当增加EventEmitter
的监听器数量限制,因为每个 TCP 连接可能会触发多个事件。 - 内存管理优化:合理释放不再使用的内存,避免内存泄漏。在处理大量连接和数据时,及时释放不再使用的缓冲区和对象。例如,当一个 TCP 连接关闭后,确保与之相关的所有资源(如缓冲区、事件监听器等)都被正确释放。可以使用
WeakMap
来存储一些弱引用对象,当对象没有其他强引用时,垃圾回收机制可以自动回收其内存。
- 调整文件描述符限制:在高并发场景下,TCP 服务器可能会同时处理大量连接,每个连接都需要一个文件描述符。默认情况下,操作系统对文件描述符数量有限制。可以通过
安全措施
- 防止 DDoS 攻击
- 流量限制:
- 基于 IP 限制:使用中间件记录每个 IP 的请求频率,例如使用
express-rate-limit
这样的库(虽然通常用于 HTTP 应用,但原理类似)。对于 TCP 服务器,可以自行实现类似逻辑。记录每个 IP 在一定时间内发起的连接数,如果超过设定阈值(如每分钟 100 次连接),则暂时拒绝该 IP 的连接请求一段时间(如 1 分钟)。 - 基于全局流量限制:监控服务器整体的连接速率,如果超过服务器能够承受的最大连接速率(根据服务器硬件和网络带宽等因素确定),则采取限流措施。可以丢弃新的连接请求或者采用排队机制,在连接数下降后逐步处理排队的请求。
- 基于 IP 限制:使用中间件记录每个 IP 的请求频率,例如使用
- SYN 缓存与 Cookie 挑战:
- SYN 缓存:在 TCP 三次握手过程中,服务器接收到 SYN 包后,将连接信息存储在 SYN 缓存中,而不是立即分配大量资源建立完整连接。只有当接收到 ACK 包完成三次握手后,才正式建立连接并分配资源。这样可以防止恶意攻击者通过发送大量 SYN 包耗尽服务器资源。
- Cookie 挑战:对于新连接请求,服务器发送一个带有特殊 Cookie 的 SYN + ACK 包,客户端必须在后续的 ACK 包中返回该 Cookie。只有返回正确 Cookie 的连接请求才被接受。这样可以有效过滤掉一些自动化的 DDoS 攻击工具,因为它们通常不会处理这种复杂的挑战。
- 流量限制:
- 防止端口扫描
- 隐藏端口:不要将服务器端口暴露在公网上,如果必须暴露,尽量使用非标准端口(大于 1024 的端口)。同时,在服务器配置中,避免在服务发现协议(如 DNS 等)中公开服务器所使用的端口信息。
- 端口访问控制:使用防火墙(如
iptables
在 Linux 系统中)限制对服务器端口的访问。只允许来自可信 IP 地址段的连接请求,对于其他 IP 的连接请求直接拒绝。例如,只允许公司内部 IP 段(如 192.168.1.0/24)访问服务器的特定 TCP 端口。 - 检测异常连接尝试:通过监控系统记录每个端口的连接尝试次数和频率。如果某个端口在短时间内收到大量连接尝试且失败率很高,可能是端口扫描行为。可以在 Node.js 服务器中通过日志记录和分析模块实现对连接尝试的监控,一旦发现异常,及时采取措施,如暂时封禁发起扫描的 IP 地址。