非常好的问题!乍一看可能觉得惊讶,但Node.js可以高效地处理数百万的并发请求——即使它们都调用同一个单例方法——这要归功于其事件驱动及非阻塞的架构。
我们一步一步来分解吧。
Node.js 和事件循环之旅Node.js 使用的是一个单线程事件循环,但这并不意味着它只能一次处理一个请求的模式。
相反,它依赖于异步 I/O 操作和事件触发回调来处理多个同时请求,从而不会阻塞主线程。
请求进来时,会发生这些事:
- Node.js 开始处理相应的请求。
- 如果请求涉及异步操作(如数据库查询或文件读取),这些任务会被委托给(交给系统内核或工作线程)。
- 在等待期间,Node.js 会转而去处理下一个请求。
- 当异步操作完成后,回调会被加入队列并在适当时候执行。
这种架构允许Node.js使用极少资源来处理大量并发请求。
2. 共享的单例函数的访问比如说,我们现在有一个像这样的简单单例(Singleton)对象。
类 Config {
构造函数() {
this.配置 = {
dbHost: 'localhost',
dbPort: 3306
};
}
获取(key) {
return this.配置[key];
}
}
const config = new Config();
module.exports = config;
-
这个
config
对象在应用程序启动时仅创建一次。 -
每个导入此模块的请求都会得到同一个实例。
get()
方法只是从静态内存对象中读取 —— 不修改数据、不涉及 I/O、不造成阻塞。
config.get()
,会怎样?
他们全都成功了,而且一点冲突都没有。
那是因为:
- 没有共享状态被更改。
- 没有阻塞或等待的情况。
- 每个调用都很轻便且各自独立。
我们可以通过一个简单的 Express 服务器来说明。
const express = require('express');
const app = express();
const config = require('./config');
app.get('/', (req, res) => {
const dbHost = config.get('dbHost');
res.send(`数据库主机为 ${dbHost}`);
});
app.listen(3000, () => {
console.log('服务器正在端口3000上运行。');
});
如果1百万个请求发送到该服务器上,每次请求计为一次:
更好的表达可以是:
如果1百万个请求发送到该服务器,每次请求计为一次。
- 每一项都会调用
config.get('dbHost')
。 - 这只是一个在 JavaScript 对象中的 属性查找。
- Node.js 在事件循环中处理这些请求,高效地排队和响应,无需等待前一个请求完成。
因为它是这样的:
- 无状态
- 只读
- 非阻塞的
所以,Node.js 可以并发处理请求,而且几乎没有任何延迟。
4. 当单例模式成为性能瓶颈这种效率只在Singleton不保存状态且为非阻塞式的情况下才成立。
可能会发生以下问题:
阻塞操作示例 class Logger {
constructor() {
this.logFile = fs.createWriteStream('./app.log');
}
log(message) {
this.logFile.write(`${new Date().toISOString()} ${message}\n`);
}
}
如果每个请求都写一条日志:
- 所有请求使用同一个
Logger
实例和同一个文件流。 - 写入磁盘是 I/O 密集型且可能造成阻塞 的。
- 在高负载情况下,日志写入会变慢或阻塞事件循环。
class EmailSender {
constructor() {
this.recipients = [];
}
setRecipients(list) {
this.recipients = list;
}
send(message) {
this.recipients.forEach(email => {
console.log(`正在发送 "${message}" 至 ${email}`);
});
}
}
如果多个请求同时修改 recipients
列表中的内容,它们就会相互干扰——这是一种经典的竞态状况。
是的 — 一百万个请求可以安全地调用单例中的同一个方法,只要在以下条件下:
✅ 此方法为只读
✅ 不涉及共享的可变状态
✅ 没有阻塞操作,例如文件或网络 I/O 操作
✅ 此函数以常数时间和内存运行
Node.js通过事件循环而不是通过创建线程来处理高并发,以高效且非阻塞的方式排队和分发工作。
要点Node.js 中的单例是安全且可扩展的 如果它是无状态的并且执行非阻塞操作 。但是一旦它引入了可变数据或阻塞逻辑,它可能会悄悄地变成一个性能瓶颈——更糟糕的是,成为 bug 的源头。
[MCP]: 模型上下文协议
[LLM]: 大语言模型
[RAG]: 检索增强生成
[SSE]: 服务器发送事件
共同學(xué)習(xí),寫下你的評論
評論加載中...
作者其他優(yōu)質(zhì)文章