Cách khởi chạy các tiến trình con trong Node.js
Khi user thực thi một chương trình Node.js đơn lẻ, chương trình đó sẽ chạy như một quy trình hệ điều hành (OS) duy nhất đại diện cho version của chương trình đang chạy. Trong quá trình đó, Node.js thực thi các chương trình trên một stream duy nhất. Như đã đề cập trước đó trong loạt bài này với hướng dẫn Cách viết mã không đồng bộ trong Node.js , bởi vì chỉ có một stream có thể chạy trên một quy trình, các hoạt động mất nhiều thời gian để thực thi trong JavaScript có thể chặn stream Node.js và làm chậm quá trình thực thi. của mã khác. Một chiến lược quan trọng để giải quyết vấn đề này là chạy một quy trình con , hoặc một quy trình được tạo bởi một quy trình khác, khi phải đối mặt với các nhiệm vụ kéo dài. Khi một quy trình mới được chạy , hệ điều hành có thể sử dụng các kỹ thuật đa xử lý đảm bảo rằng quy trình Node.js chính và quy trình con bổ sung chạy đồng thời hoặc cùng một lúc. Node.js bao gồm mô-đun child_process , có chức năng tạo các quy trình mới. Ngoài việc xử lý  các việc  lâu dài, module  này cũng có thể giao tiếp với hệ điều hành và chạy các lệnh shell . Người quản trị hệ thống có thể sử dụng Node.js để chạy các lệnh shell nhằm cấu trúc và duy trì hoạt động của họ như một mô-đun Node.js thay vì cácscript shell .
 Trong hướng dẫn này, bạn sẽ tạo các quy trình con trong khi thực thi một loạt các ứng dụng Node.js mẫu. Bạn sẽ tạo các quy trình với module  child_process bằng cách truy xuất kết quả của một quy trình con thông qua  cache  hoặc chuỗi với hàm thi exec() và sau đó từ một stream  dữ liệu với hàm spawn() . Bạn sẽ hoàn thành bằng cách sử dụng fork() để tạo một tiến trình con của một chương trình Node.js khác mà bạn có thể giao tiếp khi nó đang chạy. Để minh họa các khái niệm này, bạn sẽ viết một chương trình liệt kê nội dung của một folder , một chương trình để tìm file  và một  web server  với nhiều điểm cuối.
Yêu cầu
- Bạn phải cài đặt Node.js để chạy qua các ví dụ này. Hướng dẫn này sử dụng version 10.22.0. Để cài đặt tính năng này trên macOS hoặc Ubuntu 18.04, hãy làm theo các bước trong Cách cài đặt Node.js và Tạo Môi trường Phát triển Cục bộ trên macOS hoặc phần Cài đặt Sử dụng PPA của Cách Cài đặt Node.js trên Ubuntu 18.04 . 
- Bài viết này sử dụng một ví dụ tạo web server để giải thích cách hoạt động của hàm - fork(). Để làm quen với việc tạo web server , bạn có thể đọc hướng dẫn của ta về Cách tạo web server trong Node.js với Mô-đun HTTP .
 Bước 1 - Tạo Quy trình con với exec()
 Các nhà phát triển thường tạo các quy trình con để thực thi các lệnh trên hệ điều hành của họ khi họ cần thao tác  kết quả  của các chương trình Node.js của họ với một  shell , chẳng hạn như sử dụng đường ống hoặc chuyển hướng  shell . Hàm exec() trong Node.js tạo một quy trình  shell  mới và thực thi một lệnh trong  shell  đó. Đầu ra của lệnh được giữ trong một  cache  trong bộ nhớ, mà bạn có thể chấp nhận thông qua một hàm gọi lại được truyền vào exec() .
Hãy bắt đầu tạo các quy trình con đầu tiên của  ta  trong Node.js. Đầu tiên,  ta  cần  cài đặt  môi trường mã hóa  của bạn  để lưu trữ các script mà  ta  sẽ tạo trong suốt hướng dẫn này. Trong terminal , tạo một folder  có tên là child-processes :
- mkdir child-processes 
Nhập folder  đó vào terminal bằng lệnh cd :
- cd child-processes 
Tạo một file  mới có tên listFiles.js và mở file  trong editor . Trong hướng dẫn này,  ta  sẽ sử dụng nano , một editor   terminal :
- nano listFiles.js 
 Ta  sẽ viết một module  Node.js sử dụng hàm exec() để chạy ls . Lệnh ls liệt kê các file  và folder  trong một folder . Chương trình này lấy  kết quả  từ ls và hiển thị cho  user .
Trong editor , hãy thêm mã sau:
const { exec } = require('child_process');  exec('ls -lh', (error, stdout, stderr) => {   if (error) {     console.error(`error: ${error.message}`);     return;   }    if (stderr) {     console.error(`stderr: ${stderr}`);     return;   }    console.log(`stdout:\n${stdout}`); }); Đầu tiên,  ta  nhập lệnh child_process exec() từ module  child_process bằng cách sử dụng cấu trúc JavaScript . Sau khi được nhập,  ta  sử dụng hàm exec() . Đối số đầu tiên là lệnh mà  ta  muốn chạy. Trong trường hợp này, đó là ls -lh , liệt kê tất cả các file  và folder  trong folder  hiện tại ở định dạng dài, với tổng kích thước file  tính bằng đơn vị con người có thể đọc được ở đầu  kết quả .
 Đối số thứ hai là một hàm gọi lại với ba tham số: error , stdout và stderr . Nếu lệnh không chạy được, error sẽ ghi lại lý do tại sao lệnh không thành công. Điều này có thể xảy ra nếu  shell  không thể tìm thấy lệnh bạn đang cố gắng thực thi. Nếu lệnh được thực thi thành công, bất kỳ dữ liệu nào nó ghi vào luồng  kết quả  tiêu chuẩn sẽ được ghi lại trong stdout và mọi dữ liệu mà nó ghi vào luồng lỗi tiêu chuẩn sẽ được ghi lại trong stderr .
 Lưu ý: Điều quan trọng là phải ghi nhớ sự khác biệt giữa error và stderr . Nếu bản thân lệnh không chạy, error sẽ bắt lỗi. Nếu lệnh chạy nhưng trả về kết quả  kết quả  cho stream  lỗi, stderr sẽ nắm bắt nó. Các chương trình Node.js linh hoạt nhất sẽ xử lý tất cả các  kết quả  có thể có cho một quy trình con.
 Trong chức năng gọi lại của  ta , trước tiên  ta  kiểm tra xem  ta  có gặp lỗi không. Nếu  ta  đã làm,  ta  hiển thị của lỗi message (một tài sản của Error đối tượng) với console.error() và kết thúc chức năng với return . Sau đó,  ta  kiểm tra xem lệnh có in thông báo lỗi không và return nếu có. Nếu lệnh thực thi thành công,  ta  ghi  kết quả  của nó vào  control panel  bằng console.log() .
 Hãy chạy file  này để xem nó hoạt động. Đầu tiên, lưu và thoát nano bằng cách nhấn CTRL+X
 Quay lại terminal  của bạn, chạy ứng dụng của bạn bằng lệnh node :
- node listFiles.js 
Thiết bị terminal của bạn sẽ hiển thị kết quả sau:
Outputstdout: total 4.0K -rw-rw-r-- 1 sammy sammy 280 Jul 27 16:35 listFiles.js Phần này liệt kê nội dung của folder  child-processes ở định dạng dài, cùng với kích thước của nội dung ở trên cùng. Kết quả của bạn sẽ có  user  và  group   của bạn  thay cho sammy . Điều này cho thấy rằng chương trình listFiles.js đã chạy thành công lệnh shell ls -lh .
 Bây giờ  ta  hãy xem xét một cách khác để thực thi các quy trình đồng thời. Mô-đun child_process của Node.js cũng có thể chạy các file  thực thi với hàm execFile() . Sự khác biệt chính giữa các execFile() và thực thi exec() là đối số đầu tiên của thực execFile() bây giờ là một đường dẫn đến một file  thực thi thay vì một lệnh. Đầu ra của file  thực thi được lưu trữ trong một  cache  như exec() , mà  ta  truy cập thông qua hàm gọi lại với các tham số error , stdout và stderr .
 Lưu ý: Không thể chạy các tập lệnh trong Windows như file  .bat và .cmd với execFile() vì hàm không tạo  shell  khi chạy file . Trên Unix, Linux và macOS, các tập lệnh thực thi không phải lúc nào cũng cần shell để chạy. Tuy nhiên, máy Windows cần một shell để thực thi các tập lệnh. Để thực thi các file  kịch bản trên Windows, hãy sử dụng file  execute exec() , vì nó tạo ra một  shell  mới. Ngoài ra, bạn có thể sử dụng spawn() , mà bạn sẽ sử dụng sau trong Bước này.
 Tuy nhiên,  lưu ý  bạn có thể thực thi các file  .exe trong Windows thành công bằng cách sử dụng execFile() . Hạn chế này chỉ áp dụng cho các file  kịch bản yêu cầu  shell  để thực thi.
 Hãy bắt đầu bằng cách thêm một tập lệnh thực thi để execFile() .  Ta  sẽ viết một tập lệnh bash  download  biểu trưng Node.js từ trang web Node.js và Base64 mã hóa nó để chuyển đổi dữ liệu của nó thành một chuỗi ký tự ASCII .
 Tạo một file  script shell mới có tên processNodejsImage.sh :
- nano processNodejsImage.sh 
Bây giờ hãy viết một tập lệnh để download hình ảnh và base64 chuyển đổi nó:
#!/bin/bash curl -s https://nodejs.org/static/images/logos/nodejs-new-pantone-black.svg > nodejs-logo.svg base64 nodejs-logo.svg Tuyên bố đầu tiên là một tuyên bố shebang . Nó được sử dụng trong Unix, Linux và macOS khi  ta  muốn chỉ định một  shell  để thực thi tập lệnh  của bạn . Câu lệnh thứ hai là một lệnh curl . Tiện ích cURL , có lệnh là curl , là một công cụ dòng lệnh có thể truyền dữ liệu đến và đi từ một  server .  Ta  sử dụng cURL để  download  biểu trưng Node.js từ trang web, sau đó  ta  sử dụngchuyển hướng để lưu dữ liệu đã  download  vào file  mới nodejs-logo.svg . Câu lệnh cuối cùng  sử dụng trình  base64 để mã hóa file  nodejs-logo.svg mà  ta  đã  download  bằng cURL. Sau đó, tập lệnh xuất chuỗi được mã hóa đến console .
Lưu và thoát trước khi tiếp tục.
Để chương trình Node của ta chạy tập lệnh bash, ta phải làm cho nó có thể thực thi được. Để làm điều này, hãy chạy như sau:
- chmod u+x processNodejsImage.sh 
Điều này sẽ cung cấp cho user hiện tại của bạn quyền thực thi file .
 Với tập lệnh của  ta  tại chỗ,  ta  có thể viết một module  Node.js mới để thực thi nó. Tập lệnh này sẽ sử dụng execFile() để chạy tập lệnh trong một tiến trình con, bắt bất kỳ lỗi nào và hiển thị tất cả  kết quả  cho console .
 Trong terminal  của bạn, hãy tạo một file  JavaScript mới có tên getNodejsImage.js :
- nano getNodejsImage.js 
Nhập mã sau vào editor :
const { execFile } = require('child_process');  execFile(__dirname + '/processNodejsImage.sh', (error, stdout, stderr) => {   if (error) {     console.error(`error: ${error.message}`);     return;   }    if (stderr) {     console.error(`stderr: ${stderr}`);     return;   }    console.log(`stdout:\n${stdout}`); });  Ta  sử dụng hàm hủy JavaScript để nhập hàm execFile() từ module  child_process . Sau đó,  ta  sử dụng chức năng đó, chuyển đường dẫn file  làm tên đầu tiên. __dirname chứa đường dẫn folder  của module  mà nó được viết. Node.js cung cấp biến __dirname cho một module  khi module  chạy. Bằng cách sử dụng __dirname , kịch bản của  ta  sẽ luôn tìm ra processNodejsImage.sh  file  trên hệ điều hành khác nhau, không có vấn đề mà  ta  chạy getNodejsImage.js .  Lưu ý  đối  cài đặt  dự án hiện tại,  ta  getNodejsImage.js và processNodejsImage.sh phải nằm trong cùng một folder .
 Đối số thứ hai là một lệnh gọi lại với các tham error , stdout và stderr . Giống như với ví dụ trước của  ta  đã sử dụng exec() ,  ta  kiểm tra từng  kết quả  có thể có của file  script và đăng nhập chúng vào console .
Trong editor của bạn, hãy lưu file này và thoát khỏi editor .
 Trong terminal  của bạn, sử dụng node để thực thi module :
- node getNodejsImage.js 
Chạy tập lệnh này sẽ tạo ra kết quả như sau:
Outputstdout: PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNDQyLjQgMjcwLjkiPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjE4MC43IiB5MT0iODAuNyIge ... Lưu ý ta đã cắt bớt kết quả trong bài viết này vì kích thước lớn của nó.
 Trước khi mã hóa base64 hình ảnh, trước tiên processNodejsImage.sh tải nó xuống. Bạn cũng có thể  xác minh  bạn đã  download  hình ảnh bằng cách kiểm tra folder  hiện tại.
 Thực thi listFiles.js để tìm danh sách các file  được cập nhật trong folder  của  ta :
- node listFiles.js 
Tập lệnh sẽ hiển thị nội dung tương tự như sau trên terminal :
Outputstdout: total 20K -rw-rw-r-- 1 sammy sammy  316 Jul 27 17:56 getNodejsImage.js -rw-rw-r-- 1 sammy sammy  280 Jul 27 16:35 listFiles.js -rw-rw-r-- 1 sammy sammy 5.4K Jul 27 18:01 nodejs-logo.svg -rwxrw-r-- 1 sammy sammy  129 Jul 27 17:56 processNodejsImage.sh Bây giờ  ta  đã thực thi thành công processNodejsImage.sh dưới dạng một quy trình con trong Node.js bằng cách sử dụng hàm execFile() .
 Các execFile() exec() và execFile() có thể chạy các lệnh trên shell của hệ điều hành trong một tiến trình con Node.js. Node.js cũng cung cấp một phương thức khác có chức năng tương tự, spawn() . Sự khác biệt là thay vì nhận  kết quả  của tất cả các lệnh shell cùng một lúc,  ta  lấy chúng theo từng phần thông qua một stream . Trong phần tiếp theo,  ta  sẽ sử dụng lệnh spawn() để tạo một tiến trình con.
 Bước 2 - Tạo Quy trình con với spawn()
 Hàm spawn() chạy một lệnh trong một tiến trình. Hàm này trả về dữ liệu thông qua API stream  . Do đó, để có được  kết quả  của tiến trình con,  ta  cần lắng nghe các sự kiện  stream .
Các stream trong Node.js là các thể hiện của bộ phát sự kiện. Nếu bạn muốn tìm hiểu thêm về cách lắng nghe các sự kiện và nền tảng của việc tương tác với các stream , bạn có thể đọc hướng dẫn của ta về Sử dụng Bộ phát sự kiện trong Node.js.
 Nó thường là một ý tưởng tốt để chọn spawn() trên exec() hoặc execFile() khi lệnh bạn muốn chạy lon ra một lượng lớn dữ liệu. Với một  cache , như được sử dụng bởi exec() và execFile() , tất cả các dữ liệu xử lý được lưu trữ trong bộ nhớ của máy tính. Đối với một lượng lớn dữ liệu, điều này có thể làm giảm hiệu suất hệ thống. Với một stream , dữ liệu được xử lý và chuyển theo từng phần nhỏ. Do đó, bạn có thể xử lý một lượng lớn dữ liệu mà không cần sử dụng quá nhiều bộ nhớ cùng một lúc.
 Hãy xem cách  ta  có thể sử dụng spawn() để tạo một quy trình con.  Ta  sẽ viết một module  Node.js mới tạo ra một tiến trình con để chạy lệnh find .  Ta  sẽ sử dụng lệnh find để liệt kê tất cả các file  trong folder  hiện tại.
 Tạo một file  mới có tên findFiles.js :
- nano findFiles.js 
Trong editor  của bạn, hãy bắt đầu bằng cách gọi lệnh spawn() :
const { spawn } = require('child_process');  const child = spawn('find', ['.']); Đầu tiên  ta  nhập hàm spawn() từ module  child_process . Sau đó,  ta  gọi hàm spawn() để tạo một tiến trình con thực hiện lệnh find .  Ta  giữ tham chiếu đến tiến trình trong biến child , mà  ta  sẽ sử dụng để lắng nghe các sự kiện được truyền trực tuyến của nó.
 Đối số đầu tiên trong spawn() là lệnh chạy, trong trường hợp này là find . Đối số thứ hai là một mảng chứa các đối số cho lệnh được thực thi. Trong trường hợp này,  ta  đang yêu cầu Node.js thực hiện lệnh find với đối số . , do đó thực hiện lệnh tìm tất cả các file  trong folder  hiện tại. Lệnh tương đương trong terminal là find . .
 Với các execFile() exec() và execFile() ,  ta  đã viết các đối số cùng với lệnh trong một chuỗi. Tuy nhiên, với spawn() , tất cả các đối số của lệnh phải được nhập vào mảng. Đó là bởi vì spawn() , không giống như thi exec() và thực execFile() , không tạo một  shell  mới trước khi chạy một quy trình. Để có các lệnh với các đối số của chúng trong một chuỗi, bạn cũng cần Node.js để tạo một  shell  mới.
Hãy tiếp tục module của ta bằng cách thêm trình nghe cho kết quả của lệnh. Thêm các dòng được đánh dấu sau:
const { spawn } = require('child_process');  const child = spawn('find', ['.']);  child.stdout.on('data', data => {   console.log(`stdout:\n${data}`); });  child.stderr.on('data', data => {   console.error(`stderr: ${data}`); }); Các lệnh có thể trả về dữ liệu trong stream  stdout hoặc stream  stderr , vì vậy bạn đã thêm trình nghe cho cả hai. Bạn có thể thêm người nghe bằng cách gọi phương thức on() của từng đối tượng stream . Sự kiện data từ các stream  cung cấp cho  ta  kết quả của lệnh tới stream  đó.  Khi  nào  ta  nhận được dữ liệu trên một trong hai stream ,  ta  sẽ ghi dữ liệu đó vào console .
 Sau đó,  ta  lắng nghe hai sự kiện khác: sự kiện error nếu lệnh không thực thi hoặc bị gián đoạn và sự kiện close khi lệnh kết thúc thực hiện, do đó đóng stream .
Trong editor , hãy hoàn thành module Node.js bằng cách viết các dòng được đánh dấu sau:
const { spawn } = require('child_process');  const child = spawn('find', ['.']);  child.stdout.on('data', (data) => {   console.log(`stdout:\n${data}`); });  child.stderr.on('data', (data) => {   console.error(`stderr: ${data}`); });  child.on('error', (error) => {   console.error(`error: ${error.message}`); });  child.on('close', (code) => {   console.log(`child process exited with code ${code}`); }); Đối với sự kiện error và sự kiện close , bạn  cài đặt  trình lắng nghe trực tiếp trên biến child . Khi lắng nghe các sự kiện error , nếu một sự kiện xảy ra, Node.js sẽ cung cấp một đối tượng Error . Trong trường hợp này, bạn đăng nhập của lỗi message bất động sản.
 Khi nghe sự kiện close , Node.js cung cấp mã thoát của lệnh. Mã thoát biểu thị nếu lệnh chạy thành công hay không. Khi một lệnh chạy mà không có lỗi, nó trả về giá trị thấp nhất có thể cho mã thoát: 0 . Khi được thực thi với lỗi, nó sẽ trả về một mã khác 0.
 Mô-đun đã hoàn tất. Lưu và thoát nano bằng CTRL+X
 Bây giờ, hãy chạy mã bằng lệnh node :
- node findFiles.js 
Sau khi hoàn tất, bạn sẽ tìm thấy kết quả sau:
Outputstdout: . ./findFiles.js ./listFiles.js ./nodejs-logo.svg ./processNodejsImage.sh ./getNodejsImage.js  child process exited with code 0  Ta  tìm thấy danh sách tất cả các file  trong folder  hiện tại của  ta  và mã thoát của lệnh, mã này là 0 khi nó chạy thành công. Mặc dù folder  hiện tại của  ta  có một số lượng nhỏ các file , nếu  ta  chạy mã này trong folder  chính  của bạn , chương trình của  ta  sẽ liệt kê từng file  trong mọi folder  có thể truy cập cho  user  của  ta . Bởi vì nó có  kết quả  tiềm năng lớn như vậy, sử dụng hàm spawn() là lý tưởng nhất vì các stream  của nó không yêu cầu nhiều bộ nhớ như một  cache  lớn.
  Lúc này,   ta  đã sử dụng các hàm để tạo các quy trình con để thực thi các lệnh bên ngoài trong hệ điều hành của  ta . Node.js cũng cung cấp một cách để tạo một tiến trình con thực thi các chương trình Node.js khác. Hãy sử dụng hàm fork() để tạo một tiến trình con cho một module  Node.js trong phần tiếp theo.
 Bước 3 - Tạo Quy trình con với fork()
 Node.js cung cấp hàm fork() , một biến thể của spawn() , để tạo một quy trình con cũng là một quy trình Node.js. Lợi ích chính của việc sử dụng fork() để tạo một quy trình Node.js qua spawn() hoặc exec() là fork() cho phép giao tiếp giữa quy trình mẹ và quy trình con.
 Với fork() , ngoài việc lấy dữ liệu từ tiến trình con, một tiến trình mẹ có thể gửi tin nhắn đến tiến trình con đang chạy. Tương tự như vậy, process con có thể gửi tin nhắn đến process cha.
 Hãy xem một ví dụ trong đó việc sử dụng fork() để tạo một quy trình con Node.js mới có thể cải thiện hiệu suất của ứng dụng của  ta . Các chương trình Node.js chạy trên một quy trình duy nhất. Do đó,  các việc  chuyên sâu của CPU như lặp qua các vòng lặp lớn hoặc phân tích cú pháp các tệp JSON lớn sẽ ngăn mã JavaScript khác chạy. Đối với một số ứng dụng nhất định, đây không phải là một lựa chọn khả thi. Nếu một  web server  bị chặn, thì nó không thể xử lý bất kỳ yêu cầu mới nào đến cho đến khi mã chặn hoàn tất việc thực thi.
 Hãy xem điều này trong thực tế bằng cách tạo một  web server  với hai điểm cuối. Một điểm cuối sẽ thực hiện một phép tính chậm chặn quá trình Node.js. Điểm cuối kia sẽ trả về một đối tượng JSON nói lời hello .
 Đầu tiên, hãy tạo một file  mới có tên là httpServer.js , file  này sẽ có mã cho  server  HTTP của  ta :
- nano httpServer.js 
 Ta  sẽ bắt đầu bằng cách  cài đặt   server  HTTP. Điều này liên quan đến việc nhập module  http , tạo hàm lắng nghe yêu cầu, tạo đối tượng  server  và lắng nghe các yêu cầu trên đối tượng  server . Nếu bạn muốn đi sâu hơn vào việc tạo  server  HTTP trong Node.js hoặc muốn cập nhật, bạn có thể đọc hướng dẫn của  ta  về Cách tạo  web server  trong Node.js bằng Mô-đun HTTP .
Nhập mã sau vào editor của bạn để cài đặt server HTTP:
const http = require('http');  const host = 'localhost'; const port = 8000;  const requestListener = function (req, res) {};  const server = http.createServer(requestListener); server.listen(port, host, () => {   console.log(`Server is running on http://${host}:${port}`); }); Mã này  cài đặt  một  server  HTTP sẽ chạy tại http://localhost:8000 . Nó sử dụng các ký tự mẫu để tạo động URL đó.
 Tiếp theo,  ta  sẽ viết một hàm làm chậm có chủ đích đếm trong một vòng lặp 5 tỷ lần. Trước hàm requestListener() , hãy thêm mã sau:
... const port = 8000;  const slowFunction = () => {   let counter = 0;   while (counter < 5000000000) {     counter++;   }    return counter; }  const requestListener = function (req, res) {}; ... Điều này sử dụng cú pháp hàm mũi tên để tạo một vòng lặp while đếm đến 5000000000 .
 Để hoàn thành module  này,  ta  cần thêm mã vào hàm requestListener() . Hàm của  ta  sẽ gọi slowFunction() trên đường dẫn con và trả về một thông báo JSON nhỏ cho hàm kia. Thêm mã sau vào module :
... const requestListener = function (req, res) {   if (req.url === '/total') {     let slowResult = slowFunction();     let message = `{"totalCount":${slowResult}}`;      console.log('Returning /total results');     res.setHeader('Content-Type', 'application/json');     res.writeHead(200);     res.end(message);   } else if (req.url === '/hello') {     console.log('Returning /hello results');     res.setHeader('Content-Type', 'application/json');     res.writeHead(200);     res.end(`{"message":"hello"}`);   } }; ... Nếu  user  đến  server  tại đường dẫn con /total , thì  ta  chạy slowFunction() . Nếu  ta  gặp phải ở đường dẫn con /hello ,  ta  sẽ trả về thông báo JSON này: {"message":"hello"} .
 Lưu và thoát khỏi file  bằng cách nhấn CTRL+X
 Để kiểm tra, hãy chạy module   server  này với node :
- node httpServer.js 
Khi server của ta khởi động, console sẽ hiển thị như sau:
OutputServer is running on http://localhost:8000 Bây giờ, để kiểm tra hiệu suất của module  của  ta , hãy mở hai terminal  bổ sung. Trong terminal  đầu tiên, sử dụng lệnh curl để thực hiện yêu cầu đến điểm cuối /total , mà  ta  dự kiến sẽ chậm:
- curl http://localhost:8000/total 
Trong terminal  khác, sử dụng curl để thực hiện yêu cầu tới điểm cuối /hello như sau:
- curl http://localhost:8000/hello 
Yêu cầu đầu tiên sẽ trả về JSON sau:
Output{"totalCount":5000000000} Trong khi yêu cầu thứ hai sẽ trả về JSON này:
Output{"message":"hello"} Yêu cầu đến /hello chỉ hoàn thành sau khi yêu cầu đến /total . slowFunction() đã chặn không cho tất cả các mã khác thực thi trong khi nó vẫn ở trong vòng lặp của nó. Bạn có thể xác minh điều này bằng cách xem  kết quả   server  Node.js được đăng nhập trong terminal  ban đầu của bạn:
OutputReturning /total results Returning /hello results Để xử lý mã chặn trong khi vẫn chấp nhận các yêu cầu đến,  ta  có thể chuyển mã chặn sang một quy trình con với fork() .  Ta  sẽ chuyển mã chặn vào module  riêng của nó.  Server  Node.js sau đó sẽ tạo một quy trình con khi ai đó truy cập vào /total endpoint và lắng nghe kết quả từ quy trình con này.
 Khởi động lại  server  bằng cách tạo module  mới có tên getCount.js trước tiên sẽ chứa slowFunction() :
- nano getCount.js 
Bây giờ hãy nhập lại mã cho slowFunction()   :
const slowFunction = () => {   let counter = 0;   while (counter < 5000000000) {     counter++;   }    return counter; } Vì module  này sẽ là một quy trình con được tạo bằng fork() ,  ta  cũng có thể thêm mã để giao tiếp với quy trình mẹ khi slowFunction() đã hoàn tất quá trình xử lý. Thêm khối mã sau để gửi thông báo đến quy trình mẹ với JSON để trả lại cho  user :
const slowFunction = () => {   let counter = 0;   while (counter < 5000000000) {     counter++;   }    return counter; }  process.on('message', (message) => {   if (message == 'START') {     console.log('Child process received START message');     let slowResult = slowFunction();     let message = `{"totalCount":${slowResult}}`;     process.send(message);   } }); Hãy chia nhỏ khối mã này. Các thông báo giữa một tiến trình mẹ và con được tạo bởi fork() có thể truy cập được thông qua đối tượng process  global  Node.js.  Ta  thêm trình lắng nghe vào biến process để tìm kiếm các sự kiện message . Khi  ta  nhận được một sự kiện message ,  ta  sẽ kiểm tra xem đó có phải là sự kiện START . Mã  server  của  ta  sẽ gửi sự kiện START khi ai đó truy cập vào /total endpoint. Khi nhận được sự kiện đó,  ta  chạy slowFunction() và tạo một chuỗi JSON với kết quả của hàm.  Ta  sử dụng process.send() để gửi một thông báo đến tiến trình mẹ.
 Lưu và thoát getCount.js  bằng lệnh  CTRL+X trong nano.
 Bây giờ, hãy sửa đổi file  httpServer.js để thay vì gọi slowFunction() , nó tạo ra một tiến trình con thực thi getCount.js .
 Mở lại httpServer.js bằng nano :
- nano httpServer.js 
Đầu tiên, nhập hàm fork() từ module  child_process :
const http = require('http'); const { fork } = require('child_process'); ... Tiếp theo,  ta  sẽ xóa slowFunction() khỏi module  này và sửa đổi hàm requestListener() để tạo một tiến trình con. Thay đổi mã trong file  của bạn để nó trông giống như sau:
... const port = 8000;  const requestListener = function (req, res) {   if (req.url === '/total') {     const child = fork(__dirname + '/getCount');      child.on('message', (message) => {       console.log('Returning /total results');       res.setHeader('Content-Type', 'application/json');       res.writeHead(200);       res.end(message);     });      child.send('START');   } else if (req.url === '/hello') {     console.log('Returning /hello results');     res.setHeader('Content-Type', 'application/json');     res.writeHead(200);     res.end(`{"message":"hello"}`);   } }; ... Khi ai đó đi đến /total endpoint, bây giờ  ta  tạo một process con mới với fork() . Đối số của fork() là đường dẫn đến module  Node.js. Trong trường hợp này, đó là file  getCount.js trong folder  hiện tại của  ta , mà  ta  nhận được từ __dirname . Tham chiếu đến tiến trình con này được lưu trữ trong một biến child .
 Sau đó,  ta  thêm một trình lắng nghe vào đối tượng child . Trình lắng nghe này nắm bắt bất kỳ thông điệp nào mà tiến trình con cung cấp cho  ta . Trong trường hợp này, getCount.js sẽ trả về một chuỗi JSON với tổng số đếm bởi while vòng lặp. Khi  ta  nhận được thông báo đó,  ta  sẽ gửi JSON cho  user .
  Ta  sử dụng hàm send() của biến child để cung cấp cho nó một thông báo. Chương trình này sẽ gửi thông báo START , bắt đầu quá trình thực thi slowFunction() trong tiến trình con.
 Lưu và thoát nano  bằng lệnh  CTRL+X
 Để kiểm tra sự cải tiến bằng cách sử dụng fork() được thực hiện trên  server  HTTP, hãy bắt đầu bằng cách thực thi file  httpServer.js với node :
- node httpServer.js 
Giống như trước đây, nó sẽ xuất ra thông báo sau khi chạy :
OutputServer is running on http://localhost:8000 Để kiểm tra server , ta cần thêm hai terminal như lần đầu tiên. Bạn có thể sử dụng lại chúng nếu chúng vẫn còn mở.
 Trong terminal  đầu tiên, sử dụng lệnh curl để thực hiện yêu cầu đến điểm cuối /total , quá trình này sẽ mất một lúc để tính toán:
- curl http://localhost:8000/total 
Trong terminal  khác, sử dụng curl để đưa ra yêu cầu tới điểm cuối /hello , điểm này sẽ phản hồi trong thời gian ngắn:
- curl http://localhost:8000/hello 
Yêu cầu đầu tiên sẽ trả về JSON sau:
Output{"totalCount":5000000000} Trong khi yêu cầu thứ hai sẽ trả về JSON này:
Output{"message":"hello"} Không giống như lần đầu tiên  ta  thử điều này, yêu cầu thứ hai tới /hello chạy ngay lập tức. Bạn có thể xác nhận bằng cách xem lại log , log  sẽ giống như sau:
OutputChild process received START message Returning /hello results Returning /total results Các bản ghi này cho thấy rằng yêu cầu cho điểm cuối /hello chạy sau khi tiến trình con được tạo nhưng trước khi tiến trình con hoàn thành nhiệm vụ của nó.
 Vì  ta  đã di chuyển mã chặn trong quy trình con sử dụng fork() ,  server  vẫn có thể phản hồi các yêu cầu khác và thực thi mã JavaScript khác. Do khả năng truyền thông điệp của hàm fork() ,  ta  có thể kiểm soát khi nào một tiến trình con bắt đầu một hoạt động và  ta  có thể trả về dữ liệu từ một tiến trình con cho một tiến trình mẹ.
Kết luận
 Trong bài viết này, bạn đã sử dụng các hàm khác nhau để tạo một tiến trình con trong Node.js. Đầu tiên, bạn đã tạo các quy trình con với exec() để chạy các lệnh shell từ mã Node.js. Sau đó, bạn chạy một file  thực thi với hàm execFile() . Bạn nhìn vào spawn() chức năng, mà cũng có thể chạy lệnh nhưng dữ liệu lợi nhuận qua một dòng suối và không bắt đầu một vỏ như exec() và execFile() . Cuối cùng, bạn đã sử dụng hàm fork() để cho phép giao tiếp hai chiều giữa tiến trình cha và con.
 Để tìm hiểu thêm về module  child_process , bạn có thể đọc tài liệu Node.js. Nếu bạn muốn tiếp tục học Node.js, bạn có thể quay lại loạt bài Cách viết mã trong Node.js hoặc duyệt qua các dự án lập trình và  cài đặt  trên trang chủ đề Node của  ta .
Các tin liên quan
 

