Rumah > Artikel > hujung hadapan web > Nod JS Dalaman
Andaikan anda pergi ke restoran dan ada seorang chef yang berjanji bahawa "Saya boleh memasak untuk beratus-ratus orang pada masa yang sama dan tiada seorang pun daripada anda akan kelaparan", Bunyinya mustahil, bukan? Anda boleh menganggap cek tunggal ini sebagai Node JS yang menguruskan semua pesanan berbilang ini dan masih menghidangkan makanan kepada semua pelanggan.
Setiap kali anda bertanya kepada seseorang soalan "Apakah Node JS?", seseorang sentiasa mendapat jawapan "Node JS ialah masa jalan yang digunakan untuk menjalankan JavaScript di luar persekitaran penyemak imbas".
Tetapi, Apakah maksud masa jalan?... Persekitaran masa jalan ialah infrastruktur perisian di mana pelaksanaan kod ditulis ke dalam bahasa pengaturcaraan tertentu. Ia mempunyai semua alat, perpustakaan dan ciri untuk menjalankan kod, mengendalikan ralat, mengurus memori dan boleh berinteraksi dengan sistem pengendalian atau perkakasan asas.
Node JS mempunyai semua ini.
Enjin Google V8 untuk menjalankan kod.
Perpustakaan teras dan API seperti fs, crypto, http, dll.
Infrastruktur seperti Libuv dan Event Loop untuk menyokong operasi I/O tak segerak dan tidak menyekat.
Jadi, kini kita boleh tahu sebab Node JS dipanggil masa jalan.
Masa larian ini terdiri daripada dua kebergantungan bebas, V8 dan libuv.
V8 ialah enjin yang turut digunakan dalam Google Chrome dan ia dibangunkan serta diuruskan oleh Google. Dalam Node JS ia melaksanakan kod JavaScript. Apabila kita menjalankan arahan node index.js maka Node JS menghantar kod ini kepada enjin V8. V8 memproses kod ini, melaksanakannya dan memberikan hasilnya. Contohnya, jika kod anda mencatatkan "Hello, World!" kepada konsol, V8 mengendalikan pelaksanaan sebenar yang menyebabkan perkara ini berlaku.
Pustaka libuv mengandungi kod C yang membolehkan akses kepada sistem pengendalian apabila kita mahukan fungsi seperti rangkaian, operasi I/O atau operasi berkaitan masa. Ia berfungsi sebagai jambatan antara Node JS dan sistem pengendalian.
Libuv mengendalikan operasi berikut:
Operasi sistem fail: Membaca atau menulis fail (fs.readFile, fs.writeFile).
Rangkaian: Mengendalikan permintaan HTTP, soket atau menyambung ke pelayan.
Pemasa: Menguruskan fungsi seperti setTimeout atau setInterval.
Tugas seperti membaca fail dikendalikan oleh kumpulan benang Libuv, pemasa oleh sistem pemasa Libuv dan panggilan rangkaian oleh API peringkat OS.
Lihat contoh berikut.
const fs = require('fs'); const path = require('path'); const filePath = path.join(__dirname, 'file.txt'); const readFileWithTiming = (index) => { const start = Date.now(); fs.readFile(filePath, 'utf8', (err, data) => { if (err) { console.error(`Error reading the file for task ${index}:`, err); return; } const end = Date.now(); console.log(`Task ${index} completed in ${end - start}ms`); }); }; const startOverall = Date.now(); for (let i = 1; i <= 4; i++) { readFileWithTiming(i); } process.on('exit', () => { const endOverall = Date.now(); console.log(`Total execution time: ${endOverall - startOverall}ms`); });
Kami membaca fail yang sama empat kali dan kami mencatat masa untuk membaca fail tersebut.
Kami mendapat output berikut bagi kod ini.
Task 1 completed in 50ms Task 2 completed in 51ms Task 3 completed in 52ms Task 4 completed in 53ms Total execution time: 54ms
Kami dapat melihat bahawa kami melengkapkan keempat-empat fail membaca hampir pada ms ke-50. Jika Node JS berbenang tunggal maka bagaimanakah semua operasi membaca fail ini selesai pada masa yang sama?
Soalan ini menjawab bahawa perpustakaan libuv menggunakan kumpulan benang. kolam benang ialah sekumpulan benang. Secara lalai, saiz kumpulan benang ialah 4 bermakna 4 permintaan boleh diproses sekaligus oleh libuv.
Pertimbangkan senario lain di mana daripada membaca satu fail 4 kali kita membaca fail ini sebanyak 6 kali.
const fs = require('fs'); const path = require('path'); const filePath = path.join(__dirname, 'file.txt'); const readFileWithTiming = (index) => { const start = Date.now(); fs.readFile(filePath, 'utf8', (err, data) => { if (err) { console.error(`Error reading the file for task ${index}:`, err); return; } const end = Date.now(); console.log(`Task ${index} completed in ${end - start}ms`); }); }; const startOverall = Date.now(); for (let i = 1; i <= 4; i++) { readFileWithTiming(i); } process.on('exit', () => { const endOverall = Date.now(); console.log(`Total execution time: ${endOverall - startOverall}ms`); });
Output akan kelihatan seperti:
Task 1 completed in 50ms Task 2 completed in 51ms Task 3 completed in 52ms Task 4 completed in 53ms Total execution time: 54ms
Andaikan operasi Baca 1 dan 2 selesai dan urutan 1 dan 2 menjadi percuma.
Anda boleh lihat bahawa 4 kali pertama kita mendapat masa yang hampir sama untuk membaca fail tetapi apabila kita membaca fail ini pada kali ke-5 dan ke-6 maka ia mengambil hampir dua kali ganda masa untuk menyelesaikan operasi baca daripada empat operasi bacaan pertama .
Ini berlaku kerana saiz kolam benang secara lalai 4 jadi empat operasi membaca dikendalikan pada masa yang sama tetapi sekali lagi 2 (ke-5 dan ke-6) kita membaca fail kemudian libuv menunggu kerana semua benang mempunyai beberapa kerja. Apabila salah satu daripada empat utas menyelesaikan pelaksanaan maka operasi baca ke-5 dikendalikan ke utas itu dan operasi bacaan kali ke-6 akan dilakukan. itulah sebab mengapa ia mengambil lebih banyak masa.
Jadi, Node JS bukan satu benang.
Tetapi, kenapa sesetengah orang merujuknya sebagai satu benang?
Ini kerana gelung acara utama adalah satu benang. Benang ini bertanggungjawab untuk melaksanakan kod Node JS, termasuk mengendalikan panggilan balik tak segerak dan tugas penyelarasan. Ia tidak secara langsung mengendalikan operasi menyekat seperti fail I/O.
Aliran pelaksanaan kod adalah seperti ini.
Node.js melaksanakan semua kod segerak (menyekat) baris demi baris menggunakan enjin JavaScript V8.
Operasi tak segerak seperti fs.readFile, setTimeout atau permintaan http dihantar ke pustaka Libuv atau subsistem lain (cth., OS).
Tugas seperti membaca fail dikendalikan oleh kumpulan benang Libuv, pemasa oleh sistem pemasa Libuv dan panggilan rangkaian oleh API peringkat OS.
Setelah tugas async selesai, panggil balik yang berkaitan dihantar ke baris gilir gelung acara.
Gelung acara mengambil panggilan balik daripada baris gilir dan melaksanakannya satu demi satu, memastikan pelaksanaan tidak menyekat.
Anda boleh menukar saiz kolam benang menggunakan process.env.UV_THREADPOOL_SIZE = 8.
Sekarang, saya fikir jika kita menetapkan bilangan utas yang tinggi maka kita akan dapat mengendalikan bilangan permintaan yang tinggi juga. Saya harap anda akan berfikir seperti saya tentang perkara ini.
Tetapi, Ia adalah bertentangan dengan apa yang kami fikirkan.
Jika kami menambah bilangan utas melebihi had tertentu maka ia akan memperlahankan pelaksanaan kod anda.
Lihat contoh berikut.
const fs = require('fs'); const path = require('path'); const filePath = path.join(__dirname, 'file.txt'); const readFileWithTiming = (index) => { const start = Date.now(); fs.readFile(filePath, 'utf8', (err, data) => { if (err) { console.error(`Error reading the file for task ${index}:`, err); return; } const end = Date.now(); console.log(`Task ${index} completed in ${end - start}ms`); }); }; const startOverall = Date.now(); for (let i = 1; i <= 4; i++) { readFileWithTiming(i); } process.on('exit', () => { const endOverall = Date.now(); console.log(`Total execution time: ${endOverall - startOverall}ms`); });
output:
Dengan Saiz Kolam Benang Tinggi (100 utas)
Task 1 completed in 50ms Task 2 completed in 51ms Task 3 completed in 52ms Task 4 completed in 53ms Total execution time: 54ms
Sekarang, output berikut ialah apabila kita menetapkan saiz kolam benang sebagai 4 (saiz lalai).
Dengan Saiz Kolam Benang Lalai (4 utas)
const fs = require('fs'); const path = require('path'); const filePath = path.join(__dirname, 'file.txt'); const readFileWithTiming = (index) => { const start = Date.now(); fs.readFile(filePath, 'utf8', (err, data) => { if (err) { console.error(`Error reading the file for task ${index}:`, err); return; } const end = Date.now(); console.log(`Task ${index} completed in ${end - start}ms`); }); }; const startOverall = Date.now(); for (let i = 1; i <= 6; i++) { readFileWithTiming(i); } process.on('exit', () => { const endOverall = Date.now(); console.log(`Total execution time: ${endOverall - startOverall}ms`); });
Anda dapat melihat bahawa jumlah masa pelaksanaan mempunyai perbezaan 100ms. jumlah masa pelaksanaan ( thread pool saiz 4) ialah 600ms dan jumlah masa pelaksanaan (thread pool saiz 100) ialah 700ms. jadi, saiz kolam benang 4 mengambil masa yang lebih singkat.
Mengapa bilangan utas yang tinggi != lebih banyak tugasan boleh diproses secara serentak?
Sebab pertama ialah setiap rangkaian mempunyai timbunan dan keperluan sumber sendiri. Jika anda menambah bilangan utas maka akhirnya ia membawa kepada keadaan kehabisan memori atau sumber CPU.
Sebab kedua ialah Sistem Pengendalian perlu menjadualkan urutan. Jika terdapat terlalu banyak utas, OS akan menghabiskan banyak masa bertukar antara mereka (penukaran konteks), yang menambah overhed dan memperlahankan prestasi dan bukannya memperbaikinya.
Sekarang, kita boleh katakan bahawa ini bukan tentang meningkatkan saiz kumpulan benang untuk mencapai kebolehskalaan dan prestasi tinggi tetapi, Ini mengenai menggunakan seni bina yang betul, seperti pengelompokan, dan memahami sifat tugas (I/O vs terikat CPU ) dan cara model dipacu peristiwa Node.js berfungsi.
Terima kasih kerana membaca.
Atas ialah kandungan terperinci Nod JS Dalaman. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!