Loop Acara Nodejs


141

Apakah ada internal dua loop acara dalam arsitektur nodejs?

  • libev / libuv
  • loop peristiwa javascript v8

Pada permintaan I / O apakah simpul mengantri permintaan ke libeio yang pada gilirannya memberitahukan ketersediaan data melalui peristiwa menggunakan libev dan akhirnya peristiwa-peristiwa tersebut ditangani oleh loop acara v8 menggunakan callback?

Pada dasarnya, Bagaimana libev dan libeio terintegrasi dalam arsitektur nodejs?

Apakah ada dokumentasi yang tersedia untuk memberikan gambaran yang jelas tentang arsitektur internal nodejs?

Jawaban:


175

Saya telah membaca kode sumber node.js & v8 secara pribadi.

Saya mengalami masalah yang sama seperti Anda ketika saya mencoba memahami arsitektur node.js untuk menulis modul asli.

Apa yang saya posting di sini adalah pemahaman saya tentang node.js dan ini mungkin agak keluar jalur juga.

  1. Libev adalah loop peristiwa yang sebenarnya berjalan secara internal di node.js untuk melakukan operasi loop peristiwa sederhana. Ini awalnya ditulis untuk sistem * nix. Libev menyediakan loop acara sederhana namun dioptimalkan untuk menjalankan proses. Anda dapat membaca lebih lanjut tentang libev sini .

  2. LibEio adalah pustaka untuk melakukan output input secara tidak sinkron. Ini menangani deskriptor file, penangan data, soket dll. Anda dapat membaca lebih lanjut di sini sini sini .

  3. LibUv adalah lapisan abstraksi di atas libeio, libev, c-ares (untuk DNS) dan iocp (untuk windows asynchronous-io). LibUv melakukan, mengelola, dan mengelola semua io dan acara di kumpulan acara. (dalam hal libeio threadpool). Anda harus memeriksa tutorial Ryan Dahl tentang libUv. Itu akan mulai lebih masuk akal bagi Anda tentang bagaimana libUv bekerja dengan sendirinya dan kemudian Anda akan memahami bagaimana node.js bekerja di atas libuv dan v8.

Untuk memahami hanya loop acara javascript Anda harus mempertimbangkan menonton video ini

Untuk melihat bagaimana libeio digunakan dengan node.js untuk membuat modul async, Anda harus melihatnya contoh ini .

Pada dasarnya apa yang terjadi di dalam node.js adalah bahwa v8 loop berjalan dan menangani semua bagian javascript serta modul C ++ [ketika mereka berjalan di utas utama (sesuai dokumentasi resmi, node.js sendiri berulir tunggal)]. Saat berada di luar utas utama, libev dan libeio menanganinya di kumpulan utas dan libev memberikan interaksi dengan loop utama. Jadi dari pemahaman saya, node.js memiliki 1 loop acara permanen: itulah loop acara v8. Untuk menangani tugas async C ++ itu menggunakan threadpool [via libeio & libev].

Sebagai contoh:

eio_custom(Task,FLAG,AfterTask,Eio_REQUEST);

Yang muncul di semua modul biasanya memanggil fungsi Taskdi threadpool. Setelah selesai, ia memanggil AfterTaskfungsi di utas utama. Sedangkan Eio_REQUESTrequest handler yang dapat berupa struktur / objek yang motifnya adalah untuk menyediakan komunikasi antara threadpool dan utas utama.


Mengandalkan fakta bahwa libuv menggunakan libev secara internal adalah cara yang baik untuk membuat kode Anda tidak lintas platform. Anda seharusnya hanya peduli dengan antarmuka publik libuv.
Raynos

1
@Raynos libuv bertujuan untuk memastikan beberapa pustaka x-platfousing-nya. Baik ? karenanya menggunakan libuv adalah ide yang bagus
ShrekOverflow

1
@Abhishek Dari Doc process.nextTick- Pada loop berikutnya di sekitar loop acara panggil panggilan balik ini. Ini bukan alias sederhana untuk setTimeout (fn, 0), ini jauh lebih efisien. Perulangan acara apa yang dirujuk oleh ini? V8 event loop?
Tamil

2
Perhatikan bahwa libuv tidak lagi diterapkan di atas libev .
strcat

4
Apakah ada cara untuk 'melihat' acara ini que? Saya ingin dapat melihat urutan panggilan di stack dan melihat fungsi-fungsi baru didorong di sana untuk lebih memahami apa yang terjadi ... apakah ada beberapa variabel yang memberi tahu Anda apa yang telah didorong ke que acara?
tbarbe

20

Sepertinya beberapa entitas yang dibahas (misalnya: libev dll.) Telah kehilangan relevansinya, karena faktanya sudah lama, tapi saya pikir pertanyaannya masih memiliki potensi besar.

Izinkan saya mencoba menjelaskan cara kerja model yang didorong oleh peristiwa dengan bantuan contoh abstrak, dalam lingkungan UNIX yang abstrak, dalam konteks Node, hingga hari ini.

Perspektif program:

  • Mesin skrip memulai eksekusi skrip.
  • Setiap kali operasi terikat CPU ditemui, itu dijalankan inline (mesin nyata), dalam kelengkapannya.
  • Setiap kali operasi terikat I / O ditemui, permintaan, dan penangan penyelesaiannya terdaftar dengan 'mesin acara' (mesin virtual)
  • Ulangi operasi dengan cara yang sama di atas hingga skrip berakhir. Operasi terikat CPU - jalankan in-line, yang terikat I / O, permintaan ke mesin seperti di atas.
  • Ketika I / O selesai, pendengar dipanggil kembali.

Mesin acara di atas disebut kerangka kerja libuv AKA event loop. Node memanfaatkan pustaka ini untuk mengimplementasikan model pemrograman yang didorong oleh peristiwa.

Perspektif Node:

  • Memiliki satu utas untuk meng-host runtime.
  • Ambil skrip pengguna.
  • Kompilasi menjadi asli [leverage v8]
  • Memuat biner, dan lompat ke titik masuk.
  • Kode yang dikompilasi mengeksekusi aktivitas terikat CPU in-line, menggunakan primitif pemrograman.
  • Banyak I / O dan kode terkait waktu memiliki pembungkus asli. Misalnya, jaringan I / O.
  • Jadi panggilan I / O dialihkan dari skrip ke jembatan C ++, dengan pegangan I / O dan pengendali penyelesaian disahkan sebagai argumen.
  • Kode asli melatih loop libuv. Ini memperoleh loop, enqueues acara tingkat rendah yang mewakili I / O, dan pembungkus panggilan balik asli ke dalam struktur loop libuv.
  • Kode asli kembali ke skrip - tidak ada I / O yang terjadi saat ini!
  • Item di atas diulang berkali-kali, sampai semua kode non-I / O dieksekusi, dan semua kode I / O terdaftar akan libuv.
  • Akhirnya, ketika tidak ada yang tersisa di sistem untuk dieksekusi, node meneruskan kontrol ke libuv
  • libuv mulai beraksi, mengambil semua acara yang terdaftar, menanyakan sistem operasi untuk mendapatkan operabilitasnya.
  • Mereka yang siap untuk I / O dalam mode non-blocking, diambil, I / O dilakukan, dan callback mereka dikeluarkan. Satu demi satu.
  • Mereka yang belum siap (misalnya soket baca, yang titik akhir lainnya belum menulis apa pun) akan terus diperiksa dengan OS sampai tersedia.
  • Loop secara internal mempertahankan timer yang semakin meningkat. Ketika aplikasi meminta callback yang ditangguhkan (seperti setTimeout), nilai timer internal ini dimanfaatkan untuk menghitung waktu yang tepat untuk menembakkan callback.

Sementara sebagian besar fungsi dipenuhi dengan cara ini, beberapa (versi async) operasi file dilakukan dengan bantuan utas tambahan, terintegrasi dengan baik ke dalam libuv. Sementara operasi jaringan I / O dapat menunggu dengan harapan peristiwa eksternal seperti titik akhir lainnya merespons dengan data dll. Operasi file memerlukan beberapa pekerjaan dari node itu sendiri. Misalnya, jika Anda membuka file dan menunggu fd siap dengan data, itu tidak akan terjadi, karena tidak ada yang membaca sebenarnya! Pada saat yang sama, jika Anda membaca dari file inline di utas utama, ini berpotensi memblokir aktivitas lain dalam program, dan dapat membuat masalah yang terlihat, karena operasi file sangat lambat dibandingkan dengan aktivitas cpu terikat. Jadi utas pekerja internal (dapat dikonfigurasi melalui variabel lingkungan UV_THREADPOOL_SIZE) digunakan untuk beroperasi pada file,

Semoga ini membantu.


Bagaimana Anda mengetahui hal-hal ini, dapatkah Anda mengarahkan saya ke sumbernya?
Suraj Jain

18

Pengantar libuv

Proyek node.js dimulai pada 2009 ketika lingkungan JavaScript dipisahkan dari peramban. Menggunakan libev Google V8 dan Marc Lehmann , node.js menggabungkan model I / O - evented - dengan bahasa yang sangat cocok dengan gaya pemrograman; karena cara itu telah dibentuk oleh browser. Sebagai node.js semakin populer, penting untuk membuatnya bekerja di Windows, tetapi libev hanya berjalan di Unix. Setara Windows mekanisme pemberitahuan acara kernel seperti kqueue atau (e) jajak pendapat adalah IOCP. libuv adalah abstraksi di sekitar libev atau IOCP tergantung pada platform, menyediakan pengguna API berdasarkan libev. Dalam versi libuv libev node-v0.9.0 telah dihapus .

Juga satu gambar yang menggambarkan Event Loop di Node.js oleh @ BusyRich


Perbarui 05/09/2017

Per loop acara Node.js doc ini ,

Diagram berikut menunjukkan ikhtisar yang disederhanakan dari urutan operasi loop acara.

   ┌───────────────────────┐
┌─>│        timers         
  └──────────┬────────────┘
  ┌──────────┴────────────┐
       I/O callbacks     
  └──────────┬────────────┘
  ┌──────────┴────────────┐
       idle, prepare     
  └──────────┬────────────┘      ┌───────────────┐
  ┌──────────┴────────────┐         incoming:   
           poll          │<─────┤  connections, 
  └──────────┬────────────┘         data, etc.  
  ┌──────────┴────────────┐      └───────────────┘
          check          
  └──────────┬────────────┘
  ┌──────────┴────────────┐
└──┤    close callbacks    
   └───────────────────────┘

catatan: setiap kotak akan disebut sebagai "fase" dari loop acara.

Ikhtisar Fase

  • timer : fase ini mengeksekusi callback yang dijadwalkan oleh setTimeout()dan setInterval().
  • Panggilan balik I / O : mengeksekusi hampir semua panggilan balik dengan pengecualian panggilan balik dekat , yang dijadwalkan oleh penghitung waktu, dan setImmediate().
  • menganggur, siapkan : hanya digunakan secara internal.
  • polling : mengambil acara I / O baru; simpul akan memblokir di sini saat yang tepat.
  • check : setImmediate()callback dipanggil di sini.
  • tutup panggilan balik : mis socket.on('close', ...).

Di antara setiap putaran acara, Node.js memeriksa apakah menunggu I / O atau timer tidak sinkron dan dimatikan dengan bersih jika tidak ada.


Anda telah mengutip itu " In the node-v0.9.0 version of libuv libev was removed", tetapi tidak ada deskripsi tentang hal itu di nodejs changelog. github.com/nodejs/node/blob/master/CHANGELOG.md . Dan jika libev dihapus maka sekarang bagaimana async I / O dilakukan di nodejs?
intekhab

@intekhab, Per tautan ini , saya pikir libuv berdasarkan libeio dapat digunakan sebagai event loop di node.js.
zangw

@intekhab saya pikir libuv mengimplementasikan semua fitur yang berkaitan dengan I / O dan polling. di sini periksa di dokumen ini: docs.libuv.org/en/v1.x/loop.html
mohit kaushik

13

Ada satu loop acara di Arsitektur NodeJs.

Model Loop Acara Node.js

Aplikasi Node berjalan dalam model event-driven single-threaded. Namun, Node mengimplementasikan kumpulan utas di latar belakang sehingga pekerjaan dapat dilakukan.

Node.js menambahkan pekerjaan ke antrian acara dan kemudian memiliki utas tunggal menjalankan perulangan acara. Perulangan acara mengambil item teratas dalam antrian acara, mengeksekusi, dan lalu mengambil item berikutnya.

Ketika mengeksekusi kode yang berumur panjang atau memblokir I / O, alih-alih memanggil fungsi secara langsung, ia menambahkan fungsi ke antrian acara bersama dengan callback yang akan dieksekusi setelah fungsi selesai. Ketika semua peristiwa di antrian acara Node.js telah dieksekusi, aplikasi Node.js berakhir.

Perulangan acara mulai menimbulkan masalah saat fungsi aplikasi kami memblokir pada I / O.

Node.js menggunakan event callback untuk menghindari keharusan menunggu pemblokiran I / O. Oleh karena itu, setiap permintaan yang melakukan pemblokiran I / O dilakukan pada utas yang berbeda di latar belakang.

Ketika acara yang memblokir I / O diambil dari antrian acara, Node.js mengambil utas dari kumpulan utas, dan menjalankan fungsi di sana alih-alih pada utas loop acara utama. Ini mencegah I / O pemblokiran menahan sisa acara dalam antrian acara.


8

Hanya ada satu loop acara yang disediakan oleh libuv, V8 hanyalah mesin runtime JS.


1

Sebagai pemula javascript, saya juga memiliki keraguan yang sama, apakah NodeJS berisi 2 loop peristiwa ?. Setelah riset panjang dan berdiskusi dengan salah satu kontributor V8, saya mendapat konsep berikut.

  • Perulangan acara adalah konsep abstrak mendasar dari model pemrograman JavaScript. Jadi mesin V8 menyediakan implementasi default untuk loop acara, yang dapat diganti atau diperluas oleh embedders (browser, node) . Kalian dapat menemukan implementasi default V8 dari loop acara di sini
  • Di NodeJS, hanya ada satu loop peristiwa yang ada , yang disediakan oleh runtime node. Implementasi loop acara V8 default digantikan dengan implementasi loop acara NodeJS

0

The pbkdf2fungsi memiliki implementasi JavaScript tetapi sebenarnya delegasi semua pekerjaan yang harus dilakukan untuk C ++ sisi.

env->SetMethod(target, "pbkdf2", PBKDF2);
  env->SetMethod(target, "generateKeyPairRSA", GenerateKeyPairRSA);
  env->SetMethod(target, "generateKeyPairDSA", GenerateKeyPairDSA);
  env->SetMethod(target, "generateKeyPairEC", GenerateKeyPairEC);
  NODE_DEFINE_CONSTANT(target, OPENSSL_EC_NAMED_CURVE);
  NODE_DEFINE_CONSTANT(target, OPENSSL_EC_EXPLICIT_CURVE);
  NODE_DEFINE_CONSTANT(target, kKeyEncodingPKCS1);
  NODE_DEFINE_CONSTANT(target, kKeyEncodingPKCS8);
  NODE_DEFINE_CONSTANT(target, kKeyEncodingSPKI);
  NODE_DEFINE_CONSTANT(target, kKeyEncodingSEC1);
  NODE_DEFINE_CONSTANT(target, kKeyFormatDER);
  NODE_DEFINE_CONSTANT(target, kKeyFormatPEM);
  NODE_DEFINE_CONSTANT(target, kKeyTypeSecret);
  NODE_DEFINE_CONSTANT(target, kKeyTypePublic);
  NODE_DEFINE_CONSTANT(target, kKeyTypePrivate);
  env->SetMethod(target, "randomBytes", RandomBytes);
  env->SetMethodNoSideEffect(target, "timingSafeEqual", TimingSafeEqual);
  env->SetMethodNoSideEffect(target, "getSSLCiphers", GetSSLCiphers);
  env->SetMethodNoSideEffect(target, "getCiphers", GetCiphers);
  env->SetMethodNoSideEffect(target, "getHashes", GetHashes);
  env->SetMethodNoSideEffect(target, "getCurves", GetCurves);
  env->SetMethod(target, "publicEncrypt",
                 PublicKeyCipher::Cipher<PublicKeyCipher::kPublic,
                                         EVP_PKEY_encrypt_init,
                                         EVP_PKEY_encrypt>);
  env->SetMethod(target, "privateDecrypt",
                 PublicKeyCipher::Cipher<PublicKeyCipher::kPrivate,
                                         EVP_PKEY_decrypt_init,
                                         EVP_PKEY_decrypt>);
  env->SetMethod(target, "privateEncrypt",
                 PublicKeyCipher::Cipher<PublicKeyCipher::kPrivate,
                                         EVP_PKEY_sign_init,
                                         EVP_PKEY_sign>);
  env->SetMethod(target, "publicDecrypt",
                 PublicKeyCipher::Cipher<PublicKeyCipher::kPublic,
                                         EVP_PKEY_verify_recover_init,
                                         EVP_PKEY_verify_recover>);

sumber daya: https://github.com/nodejs/node/blob/master/src/node_crypto.cc

Modul Libuv memiliki tanggung jawab lain yang relevan untuk beberapa fungsi yang sangat khusus di perpustakaan standar.

Untuk beberapa panggilan fungsi pustaka standar, sisi Node C ++ dan Libuv memutuskan untuk melakukan perhitungan mahal di luar loop acara sepenuhnya.

Alih-alih mereka menggunakan sesuatu yang disebut kolam ulir, kolam ulir adalah serangkaian empat utas yang dapat digunakan untuk menjalankan tugas yang mahal secara komputasi seperti pbkdf2fungsi.

Secara default Libuv membuat 4 utas di kumpulan utas ini.

Selain utas yang digunakan dalam event loop ada empat utas lain yang dapat digunakan untuk membongkar perhitungan mahal yang perlu terjadi di dalam aplikasi kita.

Banyak fungsi yang disertakan dalam pustaka Node standar secara otomatis memanfaatkan kumpulan utas ini. The pbkdf2fungsi menjadi salah satu dari mereka.

Kehadiran kumpulan utas ini sangat signifikan.

Jadi Node tidak benar-benar satu threaded, karena ada thread lain yang digunakan Node untuk melakukan beberapa tugas yang mahal secara komputasi.

Jika kumpulan acara bertanggung jawab untuk melakukan tugas yang mahal secara komputasi, maka aplikasi Node kami tidak dapat melakukan hal lain.

CPU kami menjalankan semua instruksi di dalam utas satu per satu.

Dengan menggunakan kumpulan utas kita dapat melakukan hal-hal lain di dalam perulangan peristiwa saat perhitungan terjadi.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.