Node.js kini telah menjadi anggota toolbox untuk membangun layanan aplikasi jaringan konkurensi tinggi. Mengapa Node.js menjadi kesayangan masyarakat? Artikel ini akan dimulai dengan konsep dasar proses, thread, coroutine, dan model I/O, serta memberi Anda pengenalan komprehensif tentang Node.js dan model konkurensi.
Biasanya kita menyebut program yang sedang berjalan sebagai proses. Ini adalah unit dasar untuk alokasi sumber daya dan penjadwalan oleh sistem operasi. Biasanya mencakup bagian-bagian berikut:
进程表
. Setiap proses menempati进程表项
(juga disebut进程控制块
). Entri ini berisi status proses penting seperti penghitung program, penunjuk tumpukan, alokasi memori, status file terbuka, dan informasi penjadwalan .informasi untuk memastikan bahwa setelah proses dihentikan, sistem operasi dapat menghidupkan kembali proses dengan benar.Proses tersebut memiliki ciri-ciri sebagai berikut:
Perlu dicatat bahwa jika suatu program dijalankan dua kali, bahkan jika sistem operasi dapat memungkinkan mereka untuk berbagi kode (yaitu hanya satu salinan kode yang ada di memori), hal ini tidak dapat mengubah bahwa dua contoh dari program yang sedang berjalan adalah dua hal yang berbeda. fakta proses.
Selama eksekusi proses, karena berbagai alasan seperti gangguan dan penjadwalan CPU, proses akan beralih antara status berikut:
Dari diagram peralihan status proses di atas terlihat bahwa suatu proses dapat berpindah dari status berjalan ke status siap dan status diblokir, namun hanya status siap yang dapat langsung dialihkan ke status berjalan. Hal ini karena:
Terkadang, kita perlu menggunakan thread untuk memecahkan masalah berikut:
Mengenai thread, kita perlu mengetahui hal-hal berikut:
Sekarang setelah kita memahami karakteristik dasar thread, mari kita bahas beberapa jenis thread umum.
Thread status kernel adalah thread yang didukung langsung oleh sistem operasi. Fitur utamanya adalah sebagai berikut:
Utas mode pengguna adalah utas yang sepenuhnya dibangun di ruang pengguna. Karakteristik utamanya adalah sebagai berikut:
Proses ringan (LWP) adalah thread pengguna yang dibangun dan didukung oleh kernel. Fitur utamanya adalah sebagai berikut:
Ruang pengguna hanya dapat menggunakan thread kernel melalui proses ringan (LWP). menjembatani antara thread mode pengguna dan thread kernel. Oleh karena itu, hanya dengan mendukung thread kernel barulah terdapat proses ringan (LWP).
Sebagian besar operasi proses ringan (LWP) memerlukan ruang mode pengguna untuk memulai sistem relatif mahal (memerlukan peralihan antara mode pengguna dan mode kernel);
setiap proses ringan (LWP) perlu dikaitkan dengan thread kernel tertentu, oleh karena itu:
mereka dapat mengakses proses mereka sendiri Semua ruang alamat dan sumber daya sistem bersama;
Di atas, kami secara singkat memperkenalkan jenis thread umum (thread status kernel, thread status pengguna, proses ringan). Masing-masing memiliki cakupan penerapannya sendiri, Anda dapat dengan bebas menggunakannya sesuai dengan kebutuhan Anda kombinasi, seperti model umum satu-ke-satu, banyak-ke-satu, banyak-ke-banyak dan lainnya. Karena keterbatasan ruang, artikel ini tidak akan memperkenalkan terlalu banyak tentang hal ini.
, juga disebut Fiber, adalah mekanisme berjalan program yang dibangun di atas thread yang memungkinkan pengembang mengelola sendiri penjadwalan eksekusi, pemeliharaan status, dan perilaku lainnya. Fitur utamanya adalah
Pada JavaScript, async/await
yang sering kita gunakan merupakan implementasi dari coroutine, seperti contoh berikut:
function updateUserName(id, name) { const pengguna = getUserById(id); pengguna.updateName(nama); kembali benar; } fungsi async updateUserNameAsync(id, nama) { const pengguna = menunggu getUserById(id); menunggu pengguna.updateName(nama); kembali benar; }
Dalam contoh di atas, urutan eksekusi logis dalam fungsi updateUserName
dan updateUserNameAsync
adalah:
true
getUserById
menetapkan nilai kembaliannya ke variabel user
;updateName
dari user
;Perbedaan utama antara keduanya terletak pada kontrol status selama operasi sebenarnya:
updateUserName
, ini dijalankan secara berurutan sesuai dengan urutan logis yang disebutkan di atas;updateUserNameAsync
, itu juga dijalankan secara berurutan sesuai dengan urutan logis yang disebutkan di atas, tetapi ketika bertemu await
, updateUserNameAsync
akan ditangguhkan dan menyimpan status program saat ini di lokasi yang ditangguhkan. Ini tidak akan membangunkan updateUserNameAsync
lagi hingga fragmen program setelah await
kembali dan memulihkan status program sebelum ditangguhkan, lalu Lanjutkan ke program berikutnya.Dari analisis di atas, kita dapat dengan berani menebak: Yang perlu dipecahkan oleh coroutine bukanlah masalah konkurensi program yang perlu diselesaikan oleh proses dan thread, tetapi masalah yang dihadapi saat memproses tugas asinkron (seperti operasi file, permintaan jaringan, dll.); di Sebelum async/await
, kita hanya dapat menangani tugas-tugas asinkron melalui fungsi panggilan balik, yang dapat dengan mudah membuat kita jatuh ke dalam回调地狱
dan menghasilkan kode yang berantakan yang umumnya sulit dipertahankan. Melalui coroutine, kita dapat mencapai sinkronisasi kode asinkron .
Yang perlu diingat adalah bahwa kemampuan inti coroutine adalah mampu menangguhkan program tertentu dan mempertahankan status posisi penangguhan program, dan melanjutkannya pada posisi penangguhan di masa mendatang, dan melanjutkannya. jalankan segmen berikutnya setelah program suspensi.
Operasi I/O
yang lengkap harus melalui tahapan berikut:
I/O
ke kernel melalui panggilan sistem;I/O
(dibagi ke dalam tahap persiapan dan tahap eksekusi sebenarnya), dan mengembalikan hasil pemrosesan ke thread pengguna.Secara kasar kita dapat membagi operasi I/O
menjadi empat jenis:阻塞I/O
,非阻塞I/O
,同步I/O
, dan异步I/O
Sebelum membahas jenis-jenis ini, pertama-tama kita kenali dua rangkaian berikut ini konsep (di sini Asumsikan bahwa layanan A memanggil layanan B):
阻塞/非阻塞
:
阻塞调用
;非阻塞调用
.同步/异步
:
同步
;回调
setelah eksekusi selesai . Hasilnya diberitahukan ke A, maka layanan B bersifat异步
.Banyak orang sering mengacaukan阻塞/非阻塞
dengan同步/异步
, sehingga perhatian khusus perlu diberikan:
阻塞/非阻塞
adalah untuk调用者
layanan;同步/异步
adalah untuk被调用者
layanan.Setelah memahami阻塞/非阻塞
dan同步/异步
, mari kita lihat I/O 模型
spesifiknya.
: Setelah thread pengguna memulai panggilan sistem I/O
, thread pengguna akan segera阻塞
hingga seluruh operasi I/O
diproses dan hasilnya dikembalikan ke thread pengguna (utas) proses dapatkah status阻塞
dilepaskan dan terus melakukan operasi selanjutnya.
Fitur:
I/O
, proses (utas) pengguna tidak dapat melakukan operasi lain;I/O
dapat memblokir thread (utas) masuk, jadi untuk merespons permintaan I/O
tepat waktu, perlu mengalokasikan thread (utas) masuk untuk setiap permintaan, yang akan menyebabkan sumber daya yang sangat besar penggunaan , dan untuk permintaan koneksi yang lama, karena sumber daya (utas) yang masuk tidak dapat dilepaskan untuk waktu yang lama, jika ada permintaan baru di masa mendatang, akan terjadi hambatan kinerja yang serius.Definisi
I/O
di thread (utas), jika operasi I/O
belum siap, panggilan I/O
akan mengembalikan kesalahan, dan pengguna tidak perlu memasukkan thread (utas). Tunggu, tetapi gunakan polling untuk mendeteksi apakah operasi I/O
siapI/O
yang sebenarnya akan memblokir thread pengguna hingga hasil eksekusi dikembalikan ke benang pengguna.Fitur:
I/O
(biasanya menggunakan loop while
), model perlu menempati CPU dan menggunakan sumber daya CPUI/O
siap, pengguna perlu memasukkan (Utas) utas tidak akan diblokir. Ketika operasi I/O
siap, operasi I/O
aktual selanjutnya akan memblokir pengguna untuk memasuki utas (utas);Setelah proses pengguna (utas) memulai panggilan sistem I/O
, jika panggilan I/O
menyebabkan proses pengguna (utas) diblokir, maka panggilan I/O
adalah同步I/O
jika tidak, itu adalah异步I/O
.
Kriteria untuk menilai apakah operasi I/O
同步
atau异步
adalah mekanisme komunikasi antara thread pengguna dan operasi I/O
. Dalam
同步
, interaksi antara thread pengguna dan I/O
disinkronkan melalui buffer kernel. yaitu, kernel akan menyinkronkan hasil eksekusi operasi I/O
ke buffer, dan kemudian menyalin data di buffer ke thread pengguna. Proses ini akan memblokir thread pengguna hingga operasi I/O
Selesai异步
situasi, interaksi antara thread pengguna (thread) dan I/O
langsung disinkronkan melalui kernel, yaitu kernel akan langsung menyalin hasil eksekusi operasi I/O
ke thread pengguna (thread). tidak Memblokir proses (utas) pengguna.Node.js menggunakan model I/O
asinkron berbasis peristiwa berulir tunggal. Secara pribadi, menurut saya alasan memilih model ini adalah:
I/O
. Cara mengelola sumber daya multi-thread secara wajar dan efisien sambil memastikan konkurensi tinggi lebih rumit daripada pengelolaan sumber daya single-thread.Singkatnya, untuk tujuan kesederhanaan dan efisiensi, Node.js mengadopsi model I/O
asinkron berbasis peristiwa berulir tunggal, dan mengimplementasikan modelnya melalui EventLoop dari thread utama dan thread Pekerja tambahan:
Perlu dicatat bahwa Node.js tidak cocok untuk melakukan tugas-tugas yang intensif CPU (yaitu memerlukan banyak perhitungan); ini karena EventLoop dan kode JavaScript (kode tugas peristiwa non-asinkron) berjalan di thread yang sama (yaitu, thread utama), dan salah satunya Jika dijalankan terlalu lama, hal ini dapat menyebabkan thread utama diblokir. Jika aplikasi berisi banyak tugas yang memerlukan eksekusi lama, hal ini akan mengurangi throughput server dan bahkan mungkin menyebabkan server menjadi tidak responsif.
Node.js adalah teknologi yang harus dihadapi oleh pengembang front-end saat ini dan bahkan di masa depan. Namun, sebagian besar pengembang front-end hanya memiliki pengetahuan dangkal tentang Node.js agar semua orang lebih memahami model konkurensi Node .js, Artikel ini pertama-tama memperkenalkan proses, thread, dan coroutine, kemudian memperkenalkan model I/O
yang berbeda, dan terakhir memberikan pengenalan singkat tentang model konkurensi Node.js. Meskipun tidak banyak ruang untuk memperkenalkan model konkurensi Node.js, penulis yakin bahwa hal tersebut tidak pernah lepas dari prinsip dasar. Menguasai dasar-dasar yang relevan dan kemudian memahami secara mendalam desain dan implementasi Node.js akan mendapatkan hasil dua kali lipat dengan setengah usaha.
Terakhir, jika ada kesalahan pada artikel ini, saya harap Anda dapat memperbaikinya. Saya berharap Anda semua senang coding setiap hari.