Jadi saya memiliki pemahaman tentang cara kerja Node.js: ia memiliki utas pendengar tunggal yang menerima acara dan kemudian mendelegasikannya ke kumpulan pekerja. Rangkaian pekerja memberi tahu pendengar setelah menyelesaikan pekerjaan, dan pendengar kemudian mengembalikan respons ke pemanggil.
Pertanyaan saya adalah ini: jika saya menjalankan server HTTP di Node.js dan memanggil sleep di salah satu peristiwa jalur yang dirutekan (seperti "/ test / sleep"), seluruh sistem akan berhenti. Bahkan utas pendengar tunggal. Tetapi pemahaman saya adalah bahwa kode ini terjadi di kumpulan pekerja.
Sekarang, sebaliknya, ketika saya menggunakan Mongoose untuk berbicara dengan MongoDB, pembacaan DB adalah operasi I / O yang mahal. Node tampaknya dapat mendelegasikan pekerjaan ke utas dan menerima callback ketika selesai; waktu yang dibutuhkan untuk memuat dari DB tampaknya tidak menghalangi sistem.
Bagaimana cara Node.js memutuskan untuk menggunakan utas kumpulan utas vs utas pendengar? Mengapa saya tidak dapat menulis kode peristiwa yang tidur dan hanya memblokir utas kumpulan utas?
Jawaban:
Pemahaman Anda tentang cara kerja node tidak benar ... tetapi ini adalah kesalahpahaman umum, karena realitas situasinya sebenarnya cukup kompleks, dan biasanya diringkas menjadi frasa kecil yang bernas seperti "node is single threaded" yang terlalu menyederhanakan berbagai hal .
Untuk saat ini, kami akan mengabaikan multi-pemrosesan / multi-threading eksplisit melalui cluster dan webworker-threads , dan hanya berbicara tentang node non-threaded yang khas.
Node berjalan dalam satu event loop. Itu satu utas, dan Anda hanya mendapatkan satu utas itu. Semua javascript yang Anda tulis dijalankan dalam loop ini, dan jika operasi pemblokiran terjadi dalam kode itu, maka itu akan memblokir seluruh loop dan tidak ada hal lain yang akan terjadi sampai selesai. Ini biasanya sifat node berulir tunggal yang sering Anda dengar. Tapi, ini bukan gambaran keseluruhan.
Fungsi dan modul tertentu, biasanya ditulis dalam C / C ++, mendukung I / O asinkron. Saat Anda memanggil fungsi dan metode ini, mereka secara internal mengelola penerusan panggilan ke thread pekerja. Misalnya, saat Anda menggunakan
fs
modul untuk meminta file,fs
modul meneruskan panggilan itu ke thread pekerja, dan pekerja itu menunggu responsnya, yang kemudian disajikan kembali ke loop peristiwa yang telah berputar tanpa itu di sementara itu. Semua ini dipisahkan dari Anda, pengembang node, dan beberapa di antaranya dipisahkan dari pengembang modul melalui penggunaan libuv .Seperti yang ditunjukkan oleh Denis Dollfus di komentar (dari jawaban ini untuk pertanyaan serupa), strategi yang digunakan oleh libuv untuk mencapai asynchronous I / O tidak selalu merupakan kumpulan utas, khususnya dalam kasus
http
modul, tampaknya strategi yang berbeda digunakan saat ini. Untuk tujuan kita di sini, penting untuk diperhatikan bagaimana konteks asinkron dicapai (dengan menggunakan libuv) dan bahwa kumpulan utas yang dikelola oleh libuv adalah salah satu dari beberapa strategi yang ditawarkan oleh pustaka tersebut untuk mencapai asinkronitas.Pada garis singgung yang sebagian besar terkait, ada analisis yang jauh lebih dalam tentang bagaimana node mencapai asinkronitas, dan beberapa masalah potensial terkait dan cara mengatasinya, dalam artikel yang sangat bagus ini . Sebagian besar memperluas apa yang saya tulis di atas, tetapi selain itu menunjukkan:
UV_THREADPOOL_SIZE
variabel lingkungan, selama Anda melakukannya sebelum kumpulan utas diperlukan dan dibuat:process.env.UV_THREADPOOL_SIZE = 10;
Jika Anda menginginkan multi-processing atau multi-threading in node tradisional, Anda bisa mendapatkannya melalui
cluster
modul bawaan atau berbagai modul lain seperti yang disebutkan di ataswebworker-threads
, atau Anda dapat memalsukannya dengan menerapkan beberapa cara memotong pekerjaan Anda dan secara manual menggunakansetTimeout
atausetImmediate
atauprocess.nextTick
untuk menghentikan sementara pekerjaan Anda dan melanjutkannya di loop berikutnya untuk membiarkan proses lain selesai (tapi itu tidak disarankan).Harap dicatat, jika Anda menulis kode yang berjalan lama / memblokir di javascript, Anda mungkin membuat kesalahan. Bahasa lain akan bekerja jauh lebih efisien.
sumber
Ini tidak terlalu akurat. Node.js hanya memiliki satu utas "pekerja" yang melakukan eksekusi javascript. Ada thread dalam node yang menangani pemrosesan IO, tetapi menganggapnya sebagai "pekerja" adalah kesalahpahaman. Sebenarnya hanya ada penanganan IO dan beberapa detail lain dari implementasi internal node, tetapi sebagai programmer Anda tidak dapat mempengaruhi perilaku mereka selain beberapa parameter misc seperti MAX_LISTENERS.
Tidak ada mekanisme tidur dalam JavaScript. Kami dapat membahas ini secara lebih konkrit jika Anda memposting potongan kode yang menurut Anda berarti "tidur". Tidak ada fungsi yang dipanggil untuk mensimulasikan sesuatu seperti
time.sleep(30)
di python, misalnya. AdasetTimeout
tapi itu pada dasarnya BUKAN tidur.setTimeout
dansetInterval
secara eksplisit melepaskan , bukan memblokir, loop peristiwa sehingga bit kode lain dapat dieksekusi pada thread eksekusi utama. Satu-satunya hal yang dapat Anda lakukan adalah melakukan loop sibuk pada CPU dengan komputasi dalam memori, yang memang akan membuat thread eksekusi utama kelaparan dan membuat program Anda tidak responsif.Jaringan IO selalu asinkron. Akhir dari cerita. Disk IO memiliki API sinkron dan asinkron, jadi tidak ada "keputusan". node.js akan berperilaku sesuai dengan fungsi inti API yang Anda panggil sync vs async normal. Sebagai contoh:
fs.readFile
vsfs.readFileSync
. Untuk proses anak, ada juga yang terpisahchild_process.exec
danchild_process.execSync
API .Aturan praktis selalu menggunakan API asinkron. Alasan yang valid untuk menggunakan API sinkronisasi adalah untuk kode inisialisasi dalam layanan jaringan sebelum mendengarkan koneksi atau dalam skrip sederhana yang tidak menerima permintaan jaringan untuk alat build dan sejenisnya.
sumber
fs
, sejauh yang saya tahuKumpulan benang bagaimana kapan dan siapa yang digunakan:
Pertama ketika kita menggunakan / menginstal Node di komputer, itu memulai proses antara proses lain yang disebut proses node di komputer, dan itu terus berjalan sampai Anda mematikannya. Dan proses yang berjalan ini adalah apa yang kami sebut sebagai utas tunggal.
Jadi mekanisme utas tunggal memudahkan untuk memblokir aplikasi node tetapi ini adalah salah satu fitur unik yang dibawa Node.js ke tabel. Jadi, sekali lagi jika Anda menjalankan aplikasi node Anda, itu akan berjalan hanya dalam satu thread. Tidak masalah jika Anda memiliki 1 atau jutaan pengguna yang mengakses aplikasi Anda pada saat yang bersamaan.
Jadi mari kita pahami apa yang terjadi di single thread nodejs saat Anda memulai aplikasi node. Pada awalnya program diinisialisasi, kemudian semua kode tingkat atas dijalankan, yang berarti semua kode yang tidak ada di dalam fungsi panggilan balik ( ingat semua kode di dalam semua fungsi panggilan balik akan dieksekusi di bawah loop peristiwa ).
Setelah itu, semua kode modul dieksekusi kemudian daftarkan semua callback, akhirnya event loop dimulai untuk aplikasi Anda.
Jadi seperti yang kita bahas sebelumnya, semua fungsi dan kode callback di dalam fungsi tersebut akan dijalankan di bawah event loop. Dalam event loop, beban didistribusikan dalam fase yang berbeda. Bagaimanapun, saya tidak akan membahas tentang event loop di sini.
Nah untuk pemahaman yang lebih baik tentang Thread pool I meminta Anda untuk membayangkan bahwa dalam event loop, kode di dalam satu fungsi callback dijalankan setelah menyelesaikan eksekusi kode di dalam fungsi callback lain, sekarang jika ada beberapa tugas yang sebenarnya terlalu berat. Mereka kemudian akan memblokir utas tunggal nodejs kami. Jadi, di situlah kumpulan utas masuk, yang seperti loop peristiwa, disediakan untuk Node.js oleh perpustakaan libuv.
Jadi kumpulan utas bukan bagian dari nodej itu sendiri, itu disediakan oleh libuv untuk memindahkan tugas berat ke libuv, dan libuv akan mengeksekusi kode-kode itu di utasnya sendiri dan setelah eksekusi libuv akan mengembalikan hasilnya ke acara di loop acara.
Kumpulan benang memberi kita empat utas tambahan, yang benar-benar terpisah dari utas tunggal utama. Dan kami benar-benar dapat mengkonfigurasinya hingga 128 utas.
Jadi semua utas ini bersama-sama membentuk kumpulan utas. dan event loop kemudian dapat secara otomatis memindahkan tugas berat ke kumpulan thread.
Bagian yang menyenangkan adalah semua ini terjadi secara otomatis di belakang layar. Bukan kami pengembang yang memutuskan apa yang masuk ke kumpulan utas dan apa yang tidak.
Ada banyak tugas yang masuk ke kumpulan utas, seperti
sumber
Kesalahpahaman ini hanyalah perbedaan antara multi-tasking pre-emptive dan multitasking kooperatif ...
Tidur mematikan seluruh karnaval karena sebenarnya hanya ada satu baris untuk semua wahana, dan Anda menutup gerbangnya. Anggap saja sebagai "juru bahasa JS dan beberapa hal lainnya" dan abaikan utasnya ... untuk Anda, hanya ada satu utas, ...
... jadi jangan memblokirnya.
sumber