NodeJs có phải là một luồng không?

Mar 20 2023
Làm cách nào để làm việc với các luồng khác trong Nodejs?
Node.js chạy mã JavaScript trong một luồng, nghĩa là mã của bạn chỉ có thể thực hiện một tác vụ tại một thời điểm.
Quy trình I/O của Nodejs

Node.js chạy mã JavaScript trong một luồng, nghĩa là mã của bạn chỉ có thể thực hiện một tác vụ tại một thời điểm. Nhưng bản thân Node.js là đa luồng và cung cấp các luồng ẩn thông qua thư viện `libuv`, xử lý các hoạt động I/O như đọc tệp từ đĩa hoặc yêu cầu mạng. Thông qua việc sử dụng các luồng ẩn, Node.js cung cấp các phương thức không đồng bộ cho phép mã của bạn thực hiện các yêu cầu I/O mà không chặn luồng chính.

Node.js đã giới thiệu mô-đun worker-threads cho chúng tôi. Mô-đun đó cho phép tạo các luồng và thực thi song song các tác vụ Javascript. Nếu một luồng hoàn thành một tác vụ, nó sẽ gửi một thông báo đến luồng chính có chứa kết quả để chúng tôi có thể sử dụng nó trong luồng chính. Worker-threads không chặn main thread, vì vậy chúng ta có thể phân chia và phân phối nhiệm vụ của mình thành nhiều phân tích cú pháp để tối ưu hóa nó.

Hãy làm một ví dụ về nó và xem xét nó kỹ hơn.

Chúng tôi đang tạo một dự án Nodejs và cài đặt Express framework

mkdir multiple-thread
cd multiple-thread
npm init -y
npm install express

Trước hết, có các quy trình và chủ đề. Chúng ta nên biết sự khác biệt giữa chúng.

Quy trình là gì?

Một tiến trình đang chạy một chương trình trong HĐH. Nó có thể thực hiện một nhiệm vụ tại một thời điểm.

Hãy xem xét kỹ hơn quá trình này. Tạo một tệp JS và ghi nhật ký quá trình.

//ex-process.js

const processName = process.argv.slice(2)[0]

let count = 0;

while(true){
 count++;
 if(count === 3333 || count=== 6666){
   console.log(`${processName}: ${count}`)
 }
}

node ex-process.js FirstEx &

// & symbol means the Node program to run in the background, so we can use 
our teminal for other works

Output
[1] 82942
FirstEx: 3333
FirstEx: 6666

ps |grep node

Output
82942 ttys001    2:38.85 node ex-process.js FirstEx
83105 ttys001    0:00.00 grep node

Chủ đề là một cách tương đối nhẹ để triển khai nhiều đường dẫn thực thi bên trong một ứng dụng. Ở cấp độ hệ thống, các chương trình chạy song song với nhau, với hệ thống phân bổ thời gian thực hiện cho từng chương trình dựa trên nhu cầu của nó và nhu cầu của các chương trình khác.

Cùng code Express Server với non-blocking route vàblocking routes

Hãy mã hóa một tuyến đường chặn bình thường cho người dùng. trong app.js;

const express = require('express');

const app = express();

app.get('/hello', (req,res)=>{
    res.status(200).send(`Hello`);
})

app.get('/blocking-route', (req,res)=>{
    console.log('Blocking route called')
    let counter = 0;
    for (let i = 0; i < 15_000_000_000; i++) {
      counter++;
    }
    res.status(200).send(`result is ${counter}`);
})



app.listen(7333, ()=> console.log('Server started on port 7333'));

Nó không phản hồi cho đến khi blocking-routekết thúc và đã gửi lại phản hồi.

Tại sao nó không trả lời chúng tôi ngay lập tức?

Trong ví dụ này, vòng lặp for giới hạn CPU chặn luồng chính. Trong khi luồng chính bị chặn, Node.js không thể xử lý bất kỳ truy vấn nào cho đến khi quá trình liên kết với CPU kết thúc. vì vậy không có tuyến đường nào khác /hellohoạt động như /blocking-routetuyến đường tải.

Do đó, nếu chúng ta có một API có hàng nghìn GETyêu cầu đồng thời tới tuyến không chặn như /hello, thì chỉ một yêu cầu tới tuyến đó /blocking-routesẽ chặn tất cả các tuyến trong API. Điều đó có nghĩa là nó làm cho tất cả các tuyến ứng dụng không phản hồi.

DAAA Javascript của CNTT, chúng tôi không thể sử dụng chủ đề khác

Dudeeee Không chỉ có JS. Đó là Nodejs. Chúng ta có thể giảm tải một tác vụ liên quan đến CPU bằng worker-thread.

Trước hết, chúng ta nên kiểm tra lõi của mình bằng nproclệnh

nproc

output
8

Hãy tạo một tệp JS khác như thế nào worker.js, tên tùy thuộc vào bạn. Trong tệp này, chúng tôi thực hiện tác vụ sử dụng nhiều CPU

//worker.js
const { parentPort } = require("worker_threads");

let counter = 0;
console.log("Worker started");
for (let i = 0; i < 15_000_000_000; i++) {
  counter++;
}

parentPort.postMessage(counter);

//app.js
const express = require("express");
const { Worker } = require("worker_threads");

const app = express();

app.get("/blocking-route", async (req, res) => {
  console.log("Blocking route called");
  let counter = 0;
  for (let i = 0; i < 15_000_000_000; i++) {
    counter++;
  }
  res.status(200).send(`result is ${counter}`);
});

app.get("/hello", (req, res) => {
  res.status(200).send(`Hello`);
});

app.get("/non-blocking-route", async (req, res) => {
  console.log("Non-blocking route called");
  const worker = new Worker("./worker.js");

  worker.on("message", (data) => {
    res.status(200).send(`result is ${data}`);
  });

  worker.on("error", (err) => {
    res.status(404).send(` error ${err}`);
  });

  console.log("I am not blocked");
});

app.listen(7333, () => console.log("Server started on port 7333"));

Hãy khởi động máy chủ với node app.js. Mở trình duyệt của bạn và truy cập http://localhost:7333/non-blocking-routetrước khi nó trả lời phản hồi. truy cập http://localhost:7333/hello. Bạn có nhận ra nó không? Chúng tôi nhận được phản hồi ngay lập tức. Không có chặn. Chúng tôi không đợi các tuyến đường khác tải xong. Bởi vì tác vụ liên kết với CPU đã được giảm tải và tác vụ đó đang hoạt động trên một luồng khác và luồng chính sẽ xử lý tất cả các yêu cầu. Chúng tôi có thể sử dụng nhiều công nhân hơn để cải thiện hiệu suất của chúng tôi. Ví dụ: tôi có 8 lõi và tôi có thể chia Quy trình của mình thành 8.

Phần kết luận

Công nhân (luồng) có thể trợ giúp với các hoạt động JavaScript sử dụng nhiều CPU. Không thích child_processhoặc cluster, worker_threadscó thể chia sẻ bộ nhớ.

Trong bài viết này, tôi đã cố gắng làm cho nó dễ dàng hơn và giải thích Worker_threads hoạt động như thế nào?

Cảm ơn vì đã đọc

>REF