Node js多线程[duplicate]

gajydyqb  于 2023-08-04  发布在  Node.js
关注(0)|答案(2)|浏览(142)

此问题在此处已有答案

Expressjs main loop blocked during intense operation(2个答案)
3天前关闭。

const http = require("http");

const server = http.createServer((req, res) => {
  if (req.url === "/") {
    res.end("home page");
  } else if (req.url === "/about") {
    setTimeout(() => {
      for(let i=0; i<1000; i++){
        for (let j = 0; j < 10; j++) {
          console.log(`${i} ${j}`);
        }
      }
    }, 3000);
    res.end("about page");
  } else res.end("page not found");
});

server.listen(8090, () => {
  console.log("server loaded 8090");
});

字符串
我首先运行节点服务器,它将监听端口8090,然后我打开两个标签localhost:8090/这是主页和localhost:8090/这是关于页面,如果我加载关于页面服务器运行一个循环,它可能需要10秒才能完成,问题是,当循环运行时,我试图加载主页,但它不加载,因为循环正在运行,即使我尝试在两个不同的设备,然后也发生同样的问题
循环结束后,我重新加载主页,它会重新加载秒的分数
我试着在两个不同的设备上加载这两个页面,但是没有任何效果

yrefmtwq

yrefmtwq1#

您遇到的问题是由于Node.js的单线程特性。Node.js在单线程上运行,虽然它可以有效地处理异步操作,但它可能会被冗长的同步任务所阻碍,例如“/about”路由处理程序中的循环。
当您加载“/about”页面时,它会触发循环,由于循环是同步的且耗时,因此它会阻碍事件循环,从而阻止处理任何其他请求,包括对主页的请求。因此,两个标签或设备都被卡住,等待来自服务器的响应。
为了解决这个问题并允许同时处理多个请求,您可以考虑使用Node.js中的“worker_threads”模块在单独的线程中运行CPU密集型任务。这样,主线程(事件循环)就不会被阻塞,它可以继续处理其他传入的请求。
以下是如何修改代码以使用“worker_threads”:

const http = require("http");
const { Worker, isMainThread, parentPort } = require("worker_threads");

if (isMainThread) {
  const server = http.createServer((req, res) => {
    if (req.url === "/") {
      res.end("home page");
    } else if (req.url === "/about") {
      const worker = new Worker(__filename);
      worker.on("message", (message) => {
        console.log(message);
        res.end("about page");
      });
      worker.postMessage("start");
    } else {
      res.end("page not found");
    }
  });

  server.listen(8090, () => {
    console.log("server loaded 8090");
  });
} else {
  parentPort.on("message", (message) => {
    if (message === "start") {
      for (let i = 0; i < 1000; i++) {
        for (let j = 0; j < 10; j++) {
          console.log(`${i} ${j}`);
        }
      }
      parentPort.postMessage("loop done");
    }
  });
}

字符串
通过这种调整,当请求“/about”路由时,创建新的工作线程来处理循环,并且主线程可以继续处理其他请求,包括主页请求。
但是,请注意,使用工作线程执行CPU密集型任务适用于许多场景,但它可能不是每个用例的最佳解决方案。如果你的循环有可能消耗大量的系统资源,或者如果你预计会有大量的并发请求,你可能需要考虑使用一种更具可扩展性的方法,比如一个单独的微服务或使用一个任务队列系统。

jqjz2hbq

jqjz2hbq2#

您所面临的问题是Node.js默认为单线程的结果。当“/about”路由内的循环运行时,它独占了事件循环,导致服务器对其他传入的请求(包括对“主页”的请求)无响应。
要解决此问题并支持并发处理请求,可以利用Node.js“cluster”模块,该模块有助于创建多线程环境。通过使用群集模块,您可以生成多个工作进程,每个工作进程都能够独立处理传入请求。
通过使用“cluster”模块,您可以在这些工作进程之间有效地分配工作负载,从而确保“/about”路由中的循环不再阻塞事件循环,并允许服务器同时处理其他请求。这种方法显著提高了Node.js应用程序的响应能力和可伸缩性。
要实现此解决方案,您需要创建一个主进程,负责派生多个辅助进程,每个进程处理传入请求的一个子集。如果工作进程没有响应或崩溃,主进程可以创建一个新的工作进程来保持平稳的操作。
有了“cluster”模块,您的Node.js应用程序将转换为多线程设置,并且每个工作进程都在自己的隔离内存空间中运行。这种安排可确保一个工作线程的操作不会干扰另一个工作线程的操作,从而增强服务器的整体稳定性和性能。
在运行修改后的代码时,您将看到多个辅助进程在不同的端口上加载服务器。由于每个工作线程都独立处理传入请求,“关于页面”循环将不再阻止其他请求,从而允许您在循环处于活动状态时自由访问“主页”或任何其他路由。
总之,使用Node.js“cluster”模块是创建多线程环境的有效方法,它支持并发处理请求,并显著提高了Node.js服务器的整体响应能力和可伸缩性。
试试这个1:-

const http = require("http");
const cluster = require("cluster");
const numCPUs = require("os").cpus().length;

if (cluster.isMaster) {
  // Fork workers for each CPU core
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  // Handle when a worker dies, create a new one
  cluster.on("exit", (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died`);
    cluster.fork();
  });
} else {
  const server = http.createServer((req, res) => {
    if (req.url === "/") {
      res.end("home page");
    } else if (req.url === "/about") {
      setTimeout(() => {
        for (let i = 0; i < 1000; i++) {
          for (let j = 0; j < 10; j++) {
            console.log(`${i} ${j}`);
          }
        }
      }, 3000);
      res.end("about page");
    } else {
      res.end("page not found");
    }
  });

  server.listen(8090, () => {
    console.log(`Worker ${cluster.worker.id} - Server loaded on port 8090`);
  });
}

字符串
输出:-
Worker 1 -服务器已加载到端口8090
Worker 2 -服务器已加载到端口8090
通过此修改,您的Node.js应用程序将创建多个工作进程,每个工作进程独立地处理自己的请求。带有循环的“关于页面”路由不会阻止其他请求,并且在循环运行时,您应该能够加载“主页”或任何其他路由。
请记住,使用“群集”模块将创建应用程序的单独示例,并且每个工作线程都将拥有自己的内存空间。如果您需要在工作者之间共享数据,则需要使用其他技术,如进程间通信(IPC)。
试试这个2:-
输入:-

const http = require("http");
const cluster = require("cluster");
const numCPUs = require("os").cpus().length;

if (cluster.isMaster) {
  // Fork workers for each CPU core
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  // Handle when a worker exits, create a new one
  cluster.on("exit", (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died`);
    cluster.fork();
  });
} else {
  const server = http.createServer((req, res) => {
    if (req.url === "/") {
      res.end("home page");
    } else if (req.url === "/about") {
      // Simulate a time-consuming task using setTimeout
      setTimeout(() => {
        for (let i = 0; i < 1000; i++) {
          for (let j = 0; j < 10; j++) {
            console.log(`${i} ${j}`);
          }
        }
        res.end("about page");
      }, 3000);
    } else {
      res.end("page not found");
    }
  });

  server.listen(8090, () => {
    console.log(`Worker ${cluster.worker.id} - Server loaded on port 8090`);
  });
}


输出:-
Worker 1 -服务器已加载到端口8090
Worker 2 -服务器已加载到端口8090
在第2版中,我们首先使用“cluster.isMaster”检查当前进程是否为主进程。如果它是主进程,我们使用cluster.fork()派生多个工作进程,其数量等于可用的CPU核心数。每个工作线程都将在其自己的单独线程中运行,从而实现传入请求的并发处理。
“cluster.on(“exit”)”事件用于处理工作进程退出或终止的时间。如果一个工作进程终止,主进程将创建一个新的工作进程来替换它,从而确保我们始终有所需数量的工作进程在运行。
在else块中,其中“cluster.isMaster”为“false”,我们创建HTTP服务器并处理传入的请求。当访问“/about”路由时,我们使用setTimeout来模拟一个耗时的任务,这与您的原始代码类似。

相关问题