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的请求是串行执行的,如果这样哪性能岂不堪忧?但网上牛人的测试数据看似乎不存在这个问题,请有经验的朋友指教,谢谢!


3 回复

在 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}`);
});

解释

  1. 同步 vs 异步

    • /syncount 路由中,我们模拟了一个同步操作,并使用了一个简单的计数器。
    • /asyncount 路由中,我们使用 setTimeout 来模拟一个异步操作,这样不会阻塞事件循环。
  2. 事件循环

    • Node.js 是基于事件驱动和非阻塞 I/O 的架构。这意味着大多数操作(如网络请求、文件读写)都是异步的,不会阻塞事件循环。
    • 如果你在某个操作中使用了 sleep 这样的库,会导致当前线程阻塞,从而影响其他请求的处理。
  3. 性能

    • 使用异步操作可以确保你的服务器能够高效地处理多个并发请求,而不会因为某个操作阻塞整个进程。

总结

在实际应用中,避免使用会阻塞事件循环的操作(如 sleep),而是使用异步方法(如 setTimeoutsetInterval、Promise 等)。这将确保你的 Node.js 服务器能够并行处理多个 HTTP 请求,提高整体性能和响应速度。


没人回复我自己顶顶,顺便说下我写这个测试用例的目的,是为了想知道在并发请求的情况下SynFun对象属性值是否会有同步的问题,毕竟SynFun在这个例子中是全局对象

Node.js 默认情况下是单线程的,因此它通过事件循环(Event Loop)和非阻塞 I/O 模型来处理并发请求。理论上,Node.js 可以同时处理多个请求,而不会因为一个请求的阻塞导致其他请求被阻塞。

但在你的例子中,你使用了 sleep 模块来模拟一个长时间运行的任务,这会导致当前 Node.js 的主线程被阻塞。因此,在这段时间内,其他请求将无法得到处理,从而看起来像是串行执行的。

如果你希望异步处理这些任务而不阻塞主线程,可以使用 Promiseasync/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 来模拟耗时操作。这两种方法都不会阻塞主线程,因此即使有一个请求耗时较长,其他请求仍然可以并行处理。

回到顶部