Rumah >hujung hadapan web >tutorial js >Analisis mendalam tentang cara membuat proses anak dalam Node.js

Analisis mendalam tentang cara membuat proses anak dalam Node.js

青灯夜游
青灯夜游ke hadapan
2021-10-12 10:05:483016semak imbas

Artikel ini akan membawa anda melalui sub-proses dalam Node.js dan memperkenalkan empat kaedah untuk mencipta sub-proses dalam Node.js saya harap ia akan membantu semua orang.

Analisis mendalam tentang cara membuat proses anak dalam Node.js

Seperti yang kita semua tahu, Node.js ialah bahasa pengaturcaraan berbenang tunggal, tak segerak, tidak menyekat, jadi bagaimana untuk menggunakan sepenuhnya kelebihan berbilang teras CPU? Ini memerlukan modul child_process untuk mencipta proses anak Dalam Node.js, terdapat empat cara untuk mencipta proses anak:

  • exec

  • execFile

  • spawn

  • fork

[Pembelajaran yang disyorkan: "tutorial nodejs "]

Empat kaedah di atas akan mengembalikan contoh ChildProcess (diwarisi daripada EventEmitter), yang mempunyai tiga aliran stdio Standard :

  • child.stdin

  • child.stdout

  • child.stderr

Peristiwa yang boleh didaftarkan dan dipantau semasa kitaran hayat proses anak ialah:

exit: dicetuskan apabila proses anak tamat, dan parameternya ialah kod ralat kod dan isyarat gangguan isyarat.

close: Dicetuskan apabila proses anak tamat dan strim stdio ditutup Parameternya adalah sama dengan acara exit.

disconnect: Dicetuskan apabila proses induk memanggil child.disconnect() atau proses anak memanggil process.disconnect().

error: Dicetuskan apabila proses anak tidak boleh dibuat, tidak boleh dibunuh atau gagal menghantar mesej kepada proses anak.

message: Dicetuskan apabila proses anak menghantar mesej melalui process.send().

spawn: Dicetuskan apabila proses anak berjaya dibuat (acara ini hanya ditambahkan dalam Node.js v15.1).

Kaedah exec dan execFile juga menyediakan fungsi panggil balik tambahan, yang akan dicetuskan apabila proses anak ditamatkan. Seterusnya, analisis terperinci:

exec

kaedah exec digunakan untuk melaksanakan perintah bash dan parameternya ialah rentetan arahan. Sebagai contoh, untuk mengira bilangan fail dalam direktori semasa, fungsi exec ditulis sebagai:

const { exec } = require("child_process")
exec("find . -type f | wc -l", (err, stdout, stderr) => {
  if (err) return console.error(`exec error: ${err}`)
  console.log(`Number of files ${stdout}`)
})

exec akan mencipta sub-proses baharu, kemudian cache hasil berjalannya dan panggil fungsi panggil balik selepas operasi selesai.

Anda mungkin terfikir bahawa arahan exec berbahaya Jika anda menggunakan rentetan yang disediakan oleh pengguna sebagai parameter fungsi exec, anda akan menghadapi risiko suntikan baris arahan, contohnya:

.
find . -type f | wc -l; rm -rf /;

Selain itu, memandangkan exec akan cache semua hasil output dalam ingatan, apabila data agak besar, spawn akan menjadi pilihan yang lebih baik.

execFile

Perbezaan antara execFile dan exec ialah ia tidak mencipta shell, tetapi secara langsung melaksanakan arahan, jadi ia lebih cekap, contohnya:

const { execFile } = require("child_process")
const child = execFile("node", ["--version"], (error, stdout, stderr) => {
  if (error) throw error
  console.log(stdout)
})

Oleh kerana Tiada cangkerang dicipta, dan parameter program dihantar sebagai tatasusunan, jadi ia mempunyai keselamatan yang tinggi.

spawn

Fungsi spawn adalah serupa dengan execFile Cangkang tidak didayakan secara lalai, tetapi perbezaannya ialah execFile mencache keluaran baris arahan dan kemudian menghantar hasilnya ke dalam panggilan balik. fungsi, manakala spawn menggunakan output aliran, dengan aliran, ia adalah sangat mudah untuk menyambung input dan output Contohnya, perintah wc biasa:

const child = spawn("wc")
process.stdin.pipe(child.stdin)
child.stdout.on("data", data => {
  console.log(`child stdout:\n${data}`)
})

Pada masa ini, input akan diperolehi. daripada stdin baris arahan Apabila pengguna mencetuskan Apabila anda menekan Enter ctrl D, anda mula melaksanakan arahan dan mengeluarkan hasil daripada stdout.

wc ialah singkatan Word Count, yang digunakan untuk mengira bilangan perkataan Sintaksnya ialah:

wc [OPTION]... [FILE]...

Jika anda memasukkan arahan wc pada terminal dan. tekan Enter, statistik pada masa ini ialah Masukkan aksara dalam terminal daripada papan kekunci, tekan kekunci Enter sekali lagi, dan kemudian tekan Ctrl D untuk mengeluarkan keputusan statistik.

Anda juga boleh menggabungkan perintah kompleks melalui paip, seperti mengira bilangan fail dalam direktori semasa Dalam baris arahan Linux, ia akan ditulis seperti ini:

find . -type f | wc -l

Dalam Node.js Kaedah penulisan adalah sama seperti baris arahan:

const find = spawn("find", [".", "-type", "f"])
const wc = spawn("wc", ["-l"])
find.stdout.pipe(wc.stdin)
wc.stdout.on("data", (data) => {
  console.log(`Number of files ${data}`)
})

spawn mempunyai konfigurasi tersuai yang kaya, contohnya:

const child = spawn("find . -type f | wc -l", {
  stdio: "inherit", // 继承父进程的输入输出流
  shell: true, // 开启命令行模式
  cwd: "/Users/keliq/code", // 指定执行目录
  env: { ANSWER: 42 }, // 指定环境变量(默认是 process.env)
  detached: true, // 作为独立进程存在
})

garpu

Fungsi garpu ialah varian fungsi spawn, saluran komunikasi akan dibuat secara automatik antara proses anak yang dibuat menggunakan garpu dan proses induk, dan kaedah hantar akan dipasang pada proses objek global proses anak. Contohnya, kod parent.js proses induk:

const { fork } = require("child_process")
const forked = fork("./child.js")

forked.on("message", msg => {
  console.log("Message from child", msg);
})

forked.send({ hello: "world" })

Kod child.js proses anak:

process.on("message", msg => {
  console.log("Message from parent:", msg)
})

let counter = 0
setInterval(() => {
  process.send({ counter: counter++ })
}, 1000)

Apabila memanggil fork("child.js"), nod sebenarnya digunakan untuk melaksanakan fail Kod dalam adalah bersamaan dengan spawn('node', ['./child.js']). Senario aplikasi biasa

fork adalah seperti berikut: Jika anda menggunakan Node.js untuk mencipta perkhidmatan http, apabila laluannya compute, operasi yang memakan masa dilakukan.

const http = require("http")
const server = http.createServer()
server.on("request", (req, res) => {
  if (req.url === "/compute") {
    const sum = longComputation()
    return res.end(Sum is ${sum})
  } else {
    res.end("OK")
  }
})

server.listen(3000);

Anda boleh menggunakan kod berikut untuk mensimulasikan operasi yang memakan masa ini:

const longComputation = () => {
  let sum = 0;
  for (let i = 0; i < 1e9; i++) {
    sum += i
  }
  return sum
}

那么在上线后,只要服务端收到了 compute 请求,由于 Node.js 是单线程的,耗时运算占用了 CPU,用户的其他请求都会阻塞在这里,表现出来的现象就是服务器无响应。

解决这个问题最简单的方法就是把耗时运算放到子进程中去处理,例如创建一个 compute.js 的文件,代码如下:

const longComputation = () => {
  let sum = 0;
  for (let i = 0; i < 1e9; i++) {
    sum += i;
  }
  return sum
}

process.on("message", msg => {
  const sum = longComputation()
  process.send(sum)
})

再把服务端的代码稍作改造:

const http = require("http")
const { fork } = require("child_process")
const server = http.createServer()
server.on("request", (req, res) => {
  if (req.url === "/compute") {
    const compute = fork("compute.js")
    compute.send("start")
    compute.on("message", sum => {
      res.end(Sum is ${sum})
    })
  } else {
    res.end("OK")
  }
})
server.listen(3000)

这样的话,主线程就不会阻塞,而是继续处理其他的请求,当耗时运算的结果返回后,再做出响应。其实更简单的处理方式是利用 cluster 模块,限于篇幅原因,后面再展开讲。

总结

掌握了上面四种创建子进程的方法之后,总结了以下三条规律:

  • 创建 node 子进程用 fork,因为自带通道方便通信。
  • 创建非 node 子进程用 execFile 或 spawn。如果输出内容较少用 execFile,会缓存结果并传给回调方便处理;如果输出内容多用 spawn,使用流的方式不会占用大量内存。
  • 执行复杂的、固定的终端命令用 exec,写起来更方便。但一定要记住 exec 会创建 shell,效率不如 execFile 和 spawn,且存在命令行注入的风险。

更多编程相关知识,请访问:编程视频!!

Atas ialah kandungan terperinci Analisis mendalam tentang cara membuat proses anak dalam Node.js. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:juejin.cn. Jika ada pelanggaran, sila hubungi admin@php.cn Padam