Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/460.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 节点如何处理并发请求?_Javascript_Node.js - Fatal编程技术网

Javascript 节点如何处理并发请求?

Javascript 节点如何处理并发请求?,javascript,node.js,Javascript,Node.js,我最近一直在阅读nodejs,试图了解它如何处理多个并发请求,我知道nodejs是一个基于单线程事件循环的体系结构,在给定的时间点上,只有一条语句将执行,即在主线程上,阻塞代码/IO调用由工作线程处理(默认值为4) 现在我的问题是当使用nodejs构建的web服务器收到多个请求时会发生什么,我知道,有很多堆栈溢出线程也有类似的问题,但没有找到具体的答案 所以我在这里举一个例子,假设我们在一个类似/index的路由中有以下代码 假设readImage()读取该图像大约需要1分钟。 如果两个请求T1

我最近一直在阅读nodejs,试图了解它如何处理多个并发请求,我知道nodejs是一个基于单线程事件循环的体系结构,在给定的时间点上,只有一条语句将执行,即在主线程上,阻塞代码/IO调用由工作线程处理(默认值为4)

现在我的问题是当使用nodejs构建的web服务器收到多个请求时会发生什么,我知道,有很多堆栈溢出线程也有类似的问题,但没有找到具体的答案

所以我在这里举一个例子,假设我们在一个类似/index的路由中有以下代码

假设readImage()读取该图像大约需要1分钟。 如果两个请求T1和T2同时出现,nodejs将如何处理这些请求

它是否会接受第一个请求T1,在排队等待请求T2时对其进行处理(如果我的理解有误,请纠正我),如果遇到任何异步/阻塞内容,如readImage,它会将其发送到工作线程(稍后当异步工作完成时,它会通知主线程,主线程开始执行回调),执行下一行代码。 当它完成T1时,然后选择T2请求?它是否正确?或者它可以处理中间的T2代码(这意味着在调用readImage时,它可以开始处理T2)


如果有人能帮我找到这个问题的答案,我将不胜感激。

您只需使用fork()在不同的文件中移动readImage()函数,即可创建子进程

父文件,
parent.js

const { fork } = require('child_process');
const forked = fork('child.js');
forked.on('message', (msg) => {
   console.log('Message from child', msg);
});

forked.send({ hello: 'world' });
process.on('message', (msg) => {
  console.log('Message from parent:', msg);
});

let counter = 0;

setInterval(() => {
  process.send({ counter: counter++ });
}, 1000);
子文件,
child.js

const { fork } = require('child_process');
const forked = fork('child.js');
forked.on('message', (msg) => {
   console.log('Message from child', msg);
});

forked.send({ hello: 'world' });
process.on('message', (msg) => {
  console.log('Message from parent:', msg);
});

let counter = 0;

setInterval(() => {
  process.send({ counter: counter++ });
}, 1000);
以上文章可能对您有用

在上面的父文件中,我们分叉
child.js
(它将使用node命令执行该文件),然后侦听
消息
事件。每当子文件使用
process.send
时,就会发出
消息
事件,我们每秒钟都会这样做

要将消息从父对象传递给子对象,我们可以在分叉对象本身上执行
send
函数,然后在子脚本中,我们可以在全局
进程
对象上侦听
消息
事件


当执行上面的
parent.js
文件时,它将首先发送
{hello:'world}
对象由分叉子进程打印,然后分叉子进程将每秒发送一个递增的计数器值由父进程打印。

有许多文章对此进行了解释,如

它的长短不一之处在于,
nodejs
并不是一个真正的单线程应用程序,它只是一个假象

  • NodeJS事件循环在单个线程中运行
  • 当它收到一个请求时,它将该请求交给一个新线程
例如,在您的代码中,您正在运行的应用程序的PID为1。当您得到请求T1时,它将创建PID 2来处理该请求(需要1分钟)。在运行时,您会收到请求T2,该请求生成PID 3也需要1分钟。PID 2和3都将在任务完成后结束,但PID 1将继续侦听并在事件传入时传递事件


总之,
NodeJS
是“单线程”是正确的,但它只是一个事件循环侦听器,它将它们传递给异步执行的线程池,这意味着它不会阻止其他请求。

对于每个传入的请求,节点都将逐个处理。这意味着必须有顺序,就像队列一样,先入先出。当节点开始处理请求时,所有同步代码都将执行,异步代码也将执行节点可以开始处理下一个请求。异步部分完成后,它将返回主线程并继续运行

所以,当同步代码花费的时间太长时,您会阻塞主线程,节点将无法处理其他请求,这很容易测试

app.use('/index', function(req, res, next) {
    // synchronous part
    console.log("hello index routes was invoked");
    var sum = 0;
    // useless heavy task to keep running and block the main thread
    for (var i = 0; i < 100000000000000000; i++) {
        sum += i;
    }
    // asynchronous part, pass to work thread
    readImage("path", function(err, content) {
        // when work thread finishes, add this to the end of the event loop and wait to be processed by main thread
        status = "Success";
        if(err) {
            console.log("err :", err);
            status = "Error"
        }
        else {
            console.log("Image read");
        }
        return res.send({ status: status });
    });
    // continue synchronous part at the same time.
    var a = 4, b = 5;
    console.log("sum =", a + b);
});
app.use('/index',函数(req,res,next){
//同步部分
log(“调用了hello索引路由”);
var总和=0;
//无用的繁重任务,以保持运行并阻止主线程
对于(变量i=0;i<10000000000000000;i++){
总和+=i;
}
//异步部分,传递到工作线程
readImage(“路径”,函数(错误,内容){
//当工作线程完成时,将其添加到事件循环的末尾,并等待主线程处理
status=“Success”;
如果(错误){
日志(“err:,err”);
status=“Error”
}
否则{
控制台日志(“图像读取”);
}
返回res.send({status:status});
});
//同时继续同步部分。
变量a=4,b=5;
console.log(“sum=”,a+b);
});

在完成所有同步部分之前,Node不会开始处理下一个请求。因此,人们说不要阻止主线程。

您的困惑可能是因为没有充分关注事件循环。很明显,您知道这是如何工作的,但可能不是全部情况

第1部分,事件循环基础 当您调用
use
方法时,会在幕后创建另一个线程来侦听连接

但是,当请求进入时,因为我们处于与V8引擎不同的线程中(并且不能直接调用route函数),对该函数的序列化调用会附加到共享事件循环上,以便稍后调用。(在这种上下文中,事件循环是一个糟糕的名称,因为它的操作更像队列或堆栈)

在js文件的末尾,V8将检查事件循环中是否有任何正在运行的指令或消息。
hello callback!
hello inner!
hello callback!
hello inner!
1 [callback] (executing)
2 [callback] (next in line)
3 [inner]    (just added by callback)
1 [inner]    (next in line)
2 [inner]    (added by most recent callback)
// in this example definition of readImage, its entirely
// synchronous, never using an alternate thread or the
// event loop
function readImage (path, callback) {
    let image = fs.readFileSync(path);
    callback(null, image);
    // a definition like this will force the callback to
    // fully return before readImage returns. This means
    // means readImage will block any subsequent calls.
}

// in this alternate example definition its entirely
// asynchronous, and take advantage of fs' async
// callback.
function readImage (path, callback) {
    fs.readFile(path, (err, data) => {
        callback(err, data);
    });
    // a definition like this will force the readImage
    // to immediately return, and allow exectution
    // to continue.
}