Nodejs 服务器对http请求是串行的么?
Nodejs 服务器对http请求是串行的么?
初学nodejs,为了理解他的运行机制,写了下面的代码,想了解一下它对http请求的处理机制 var express = require(‘express’); var http = require(‘http’); var path = require(‘path’); var events = require(‘events’); var emitter = new events.EventEmitter(); var sleep = require(‘sleep’);
var app = express();
// all environments app.set(‘port’, process.env.PORT || 3000); app.set(‘views’, __dirname + ‘/views’); app.set(‘view engine’, ‘ejs’); app.use(express.favicon()); app.use(express.logger(‘dev’)); app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(express.cookieParser(‘your secret here’)); app.use(express.session()); app.use(app.router); app.use(express.static(path.join(__dirname, ‘public’)));
// development only if (‘development’ == app.get(‘env’)) { app.use(express.errorHandler()); }
//app.get(’/’, routes.index); //app.get(’/users’, user.list); function SynFun(){ this.count = 0; } SynFun.prototype.syncount = function(){ var self = this; emitter.emit(‘syncount’,self);
}
SynFun.prototype.asyncount = function(){ var self = this; emitter.emit(‘asyncount’,self); }
emitter.on(‘syncount’,function(obj){ console.log("======== ____syncount begin,time:" + new Date().getTime() ); obj.count = obj.count + 1; sleep.sleep(20); console.log("======== ____syncount end,time:" + new Date().getTime() ); });
emitter.on(‘asyncount’,function(obj){ console.log("======== ____asyncount begin,time:" + new Date().getTime() ); obj.count = obj.count + 1; console.log("======== ____asyncount end,time:" + new Date().getTime() ); });
var synFun = new SynFun();
app.get(’/syncount’,function(req,res){ console.log("========syncount requset begin,time:" + new Date().getTime() ); console.log("====== syncount begin count:" + synFun.count); synFun.syncount(); console.log("====== syncount end count:" + synFun.count); console.log("========syncount request end,time:" + new Date().getTime()); });
app.get(’/asyncount’,function(req,res){ console.log("========asyncount requset begin,time:" + new Date().getTime()); console.log("====== asyncount begin count:" + synFun.count); synFun.asyncount(); console.log("====== asyncount end count:" + synFun.count); console.log("========asyncount request end,time:" + new Date().getTime()); });
http.createServer(app).listen(app.get(‘port’), function(){ console.log('Express server listening on port ’ + app.get(‘port’)); });
上面的代码,我先请求运行syncount,之后休眠20妙,再运行asyncount,发现asyncount请求被阻塞了,似乎http的请求是串行执行的,如果这样哪性能岂不堪忧?但网上牛人的测试数据看似乎不存在这个问题,请有经验的朋友指教,谢谢!
在 Node.js 中,HTTP 请求通常是并行处理的,而不是串行处理。你遇到的问题可能与你的代码中的同步操作有关,特别是 sleep
库会阻塞事件循环,导致其他请求无法得到及时响应。
让我们通过一个简化的示例来说明这一点,并解释如何避免这种问题:
示例代码
const express = require('express');
const http = require('http');
const path = require('path');
const app = express();
// 设置静态文件路径
app.use(express.static(path.join(__dirname, 'public')));
// 定义两个路由处理函数
app.get('/syncount', (req, res) => {
console.log(`Syncount request received at ${new Date().toISOString()}`);
// 模拟同步操作
let count = 0;
count++;
console.log(`Syncount count: ${count}`);
// 模拟长时间阻塞操作
// 注意:使用 sleep 会导致阻塞,建议使用异步方法
// require('sleep').usleep(20 * 1000);
res.send(`Syncount count: ${count}`);
});
app.get('/asyncount', (req, res) => {
console.log(`Asyncount request received at ${new Date().toISOString()}`);
// 模拟异步操作
setTimeout(() => {
let count = 0;
count++;
console.log(`Asyncount count: ${count}`);
res.send(`Asyncount count: ${count}`);
}, 20); // 20毫秒
});
// 启动服务器
const PORT = process.env.PORT || 3000;
http.createServer(app).listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
解释
-
同步 vs 异步:
- 在
/syncount
路由中,我们模拟了一个同步操作,并使用了一个简单的计数器。 - 在
/asyncount
路由中,我们使用setTimeout
来模拟一个异步操作,这样不会阻塞事件循环。
- 在
-
事件循环:
- Node.js 是基于事件驱动和非阻塞 I/O 的架构。这意味着大多数操作(如网络请求、文件读写)都是异步的,不会阻塞事件循环。
- 如果你在某个操作中使用了
sleep
这样的库,会导致当前线程阻塞,从而影响其他请求的处理。
-
性能:
- 使用异步操作可以确保你的服务器能够高效地处理多个并发请求,而不会因为某个操作阻塞整个进程。
总结
在实际应用中,避免使用会阻塞事件循环的操作(如 sleep
),而是使用异步方法(如 setTimeout
、setInterval
、Promise 等)。这将确保你的 Node.js 服务器能够并行处理多个 HTTP 请求,提高整体性能和响应速度。
没人回复我自己顶顶,顺便说下我写这个测试用例的目的,是为了想知道在并发请求的情况下SynFun对象属性值是否会有同步的问题,毕竟SynFun在这个例子中是全局对象
Node.js 默认情况下是单线程的,因此它通过事件循环(Event Loop)和非阻塞 I/O 模型来处理并发请求。理论上,Node.js 可以同时处理多个请求,而不会因为一个请求的阻塞导致其他请求被阻塞。
但在你的例子中,你使用了 sleep
模块来模拟一个长时间运行的任务,这会导致当前 Node.js 的主线程被阻塞。因此,在这段时间内,其他请求将无法得到处理,从而看起来像是串行执行的。
如果你希望异步处理这些任务而不阻塞主线程,可以使用 Promise
或 async/await
来实现。以下是一个改进的示例:
const express = require('express');
const http = require('http');
const path = require('path');
const app = express();
// 设置视图引擎等配置
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.methodOverride());
app.use(express.cookieParser('your secret here'));
app.use(express.session());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
// 发送异步响应
app.get('/syncount', async (req, res) => {
console.log(`========syncount requset begin,time:${new Date().getTime()}`);
await sleep(20000); // 使用 sleep 模拟耗时操作
console.log(`========syncount request end,time:${new Date().getTime()}`);
res.send(`Syncount request finished`);
});
app.get('/asyncount', (req, res) => {
console.log(`========asyncount requset begin,time:${new Date().getTime()}`);
// 假设这里有一个异步任务
setTimeout(() => {
console.log(`========asyncount request end,time:${new Date().getTime()}`);
res.send(`Asyncount request finished`);
}, 20000);
});
// 启动服务器
http.createServer(app).listen(app.get('port'), () => {
console.log(`Express server listening on port ${app.get('port')}`);
});
// 睡眠函数
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
在这个示例中,/syncount
路由使用了 async/await
来等待耗时操作完成,而 /asyncount
路由则使用 setTimeout
来模拟耗时操作。这两种方法都不会阻塞主线程,因此即使有一个请求耗时较长,其他请求仍然可以并行处理。