Nodejs对非堵塞的一点疑惑。

Nodejs对非堵塞的一点疑惑。

各位前辈,我才接触nodejs不久。 发现nodejs的非阻塞 只是没有阻塞当前请求,但是会阻塞下次请求。 比如下面的代码: function sleep(milliSeconds) { var startTime = new Date().getTime(); while (new Date().getTime() < startTime + milliSeconds); } var events=require(‘events’); var emitter=new events.EventEmitter(); emitter.on(‘myevent’,function(){ sleep(10000); console.log(‘myevent’); }) var http=require(‘http’); http.createServer(function(req,res){ if(’/’==req.url){ emitter.emit(‘myevent’); } res.end(‘xxx’); }).listen(80);

当我们访问 http://localhost/xxx 是不会阻塞的 。 如果访问根目录 http://localhost/ 也不会阻塞, 因为 sleep的 10秒 在定义的事件里面。 但是 在这10秒内, 再发起任何其他请求 都会被阻塞。

用什么方法来避免这种阻塞?


4 回复

你提到的疑惑实际上是对 Node.js 的非阻塞特性存在一些误解。Node.js 的非阻塞(或异步)特性主要是通过事件循环、回调函数、Promise 和 async/await 等机制实现的。在你的例子中,sleep 函数导致了线程阻塞,这与 Node.js 的非阻塞设计背道而驰。

示例代码

const events = require('events');
const emitter = new events.EventEmitter();

// 使用 setTimeout 实现代理睡眠
function sleep(milliSeconds, callback) {
    setTimeout(callback, milliSeconds);
}

emitter.on('myevent', function() {
    console.log('myevent');
});

const http = require('http');

http.createServer((req, res) => {
    if (req.url === '/') {
        sleep(10000, () => {
            emitter.emit('myevent');
        });
    }
    res.end('xxx');
}).listen(80, () => {
    console.log('Server running at http://localhost:80/');
});

解释

  1. 事件循环和回调:Node.js 的核心是非阻塞 I/O 操作。当你调用 setTimeoutsetInterval 时,这些操作会立即返回,并将回调函数添加到事件队列中。当定时器到期时,事件循环会将回调函数放入执行栈中执行。

  2. 避免阻塞:在你的代码中,使用 sleep 函数会导致主线程阻塞。为了避免这种情况,你可以使用 setTimeout 来模拟睡眠操作。setTimeout 不会阻塞主线程,而是将回调函数推迟执行。

  3. HTTP 请求处理:在处理 HTTP 请求时,如果需要进行耗时操作(例如数据库查询、文件读写等),应该使用异步 API(如 fs.readFiledb.query 等)。这样可以确保服务器在等待 I/O 完成时可以继续处理其他请求。

  4. 更好的实践:在现代 Node.js 应用中,通常会使用 Promise 或 async/await 来简化异步代码。例如:

const fs = require('fs').promises;

http.createServer(async (req, res) => {
    if (req.url === '/') {
        await fs.readFile('/path/to/file.txt');
        emitter.emit('myevent');
    }
    res.end('xxx');
}).listen(80, () => {
    console.log('Server running at http://localhost:80/');
});

这种方式不仅使代码更简洁,而且更容易理解和维护。


你都sleep了当然会阻塞咯,一般说非阻塞是说等待IO响应的时候是非阻塞的。

延时操作用setTimeout(cb, ms)或setInterval(cb, ms) 非阻塞的意思只是保证你读写IO不会阻塞(卡着)

你的问题主要在于sleep函数会导致Node.js线程在执行时被阻塞,从而影响其他请求的处理。为了实现非阻塞的效果,你需要使用异步操作来代替同步的sleep函数。

示例代码

你可以使用setTimeout来模拟异步操作,而不是使用同步的sleep函数:

const events = require('events');
const emitter = new events.EventEmitter();

emitter.on('myevent', function() {
    console.log('myevent');
});

const http = require('http');

http.createServer((req, res) => {
    if (req.url === '/') {
        emitter.emit('myevent');
    }
    res.end('xxx');
}).listen(80);

console.log('Server running at http://localhost/');

在这个例子中,我们使用了事件发射器(EventEmitter)来模拟一个耗时的操作,而不需要使用sleep函数。这样,当用户访问根目录时,事件会被触发,但不会阻塞其他请求的处理。

解释

  1. 事件发射器(EventEmitter):这是一种用来处理多个相同类型事件的机制。在这种情况下,当事件被触发时,事件处理器会被调用,而不会阻塞其他操作。

  2. 异步操作:通过使用setTimeout等异步API,我们可以确保长时间运行的任务不会阻塞Node.js事件循环。例如,在上面的代码中,我们没有使用sleep函数,而是直接在事件处理器中打印一条消息。

  3. 事件循环:Node.js使用事件循环来处理异步操作。事件循环不断检查是否有需要处理的事件,并调用相应的回调函数。因此,即使有一个长任务正在执行,事件循环仍然可以处理新的请求。

通过这种方式,你可以确保Node.js服务器在处理长时间运行的任务时,不会阻塞其他请求的处理。

回到顶部