Nodejs Olay Döngüsü


141

Düğüm mimarisinde dahili olarak iki olay döngüsü var mı?

  • libev / libuv
  • v8 javascript olay döngüsü

Bir G / Ç isteğinde, düğüm libeio'ya istekte bulunur ve bu da libev kullanan olaylar aracılığıyla verilerin kullanılabilirliğini bildirir ve son olarak bu olaylar geri çağrılar kullanılarak v8 olay döngüsü tarafından işlenir mi?

Temel olarak, libev ve libeio nodejs mimarisine nasıl entegre edilir?

Düğüm iç mimarisinin net bir resmini vermek için herhangi bir belge var mı?

Yanıtlar:


175

Şahsen node.js & v8 kaynak kodunu okuyorum.

Yerel modüller yazmak için node.js mimarisini anlamaya çalıştığımda benzer bir sorunla karşılaştım.

Burada yayınladığım şey node.js anlayışım ve bu da biraz pist dışında olabilir.

  1. Libev , basit olay döngü işlemlerini gerçekleştirmek için aslında node.js'de dahili olarak çalışan olay döngüsüdür. Başlangıçta * nix sistemleri için yazılmıştır. Libev, sürecin çalışması için basit ama optimize edilmiş bir olay döngüsü sağlar. Burada libev hakkında daha fazla bilgi edinebilirsiniz .

  2. LibEio , giriş çıkışını eşzamansız olarak gerçekleştiren bir kütüphanedir. Dosya tanımlayıcılarını, veri işleyicilerini, soketleri vb. İşler . Burada daha fazla bilgi edinebilirsiniz .

  3. LibUv , libeio, libev, c-ares (DNS için) ve iocp (eşzamansız pencereler için) üstündeki bir soyutlama katmanıdır. LibUv, olay havuzundaki tüm olay ve etkinlikleri gerçekleştirir, sürdürür ve yönetir. (libeio threadpool durumunda). Dışarı kontrol etmelisiniz Ryan Dahl'ın öğretici libUv üzerinde. Bu, libUv'un nasıl çalıştığı konusunda size daha anlamlı gelmeye başlayacak ve daha sonra node.js'nin libuv ve v8'in üstünde nasıl çalıştığını anlayacaksınız.

Sadece javascript olay döngüsünü anlamak için bu videoları izlemeyi düşünmelisiniz

Libeio'nun zaman uyumsuz modüller oluşturmak için node.js ile nasıl kullanıldığını görmek için bu örneği görmelisiniz .

Temel olarak node.js içinde olan şey, v8 döngüsünün tüm javascript parçalarının yanı sıra C ++ modüllerini çalıştırması ve işlemesidir (bir ana iş parçacığında çalıştıklarında (resmi belgelere göre node.js'nin kendisi tek iş parçacıklıdır)]. Ana iş parçacığının dışında, libev ve libeio onu iş parçacığı havuzunda tutar ve libev ana döngü ile etkileşimi sağlar. Anladığım kadarıyla, node.js'nin 1 kalıcı olay döngüsü var: bu v8 olay döngüsü. C ++ zaman uyumsuz görevleri işlemek için bir threadpool [libeio & libev aracılığıyla] kullanıyor.

Örneğin:

eio_custom(Task,FLAG,AfterTask,Eio_REQUEST);

Hangi tüm modüllerde görünen genellikle Taskthreadpool işlevi çağırıyor . Tamamlandığında, AfterTaskana iş parçacığında işlevi çağırır . Oysa Eio_REQUESTisteği, iş parçacığı ve ana iş parçacığı arasında iletişim sağlamak olan bir yapı / nesne olabilen istek işleyicidir.


Libuv'un dahili olarak libev'i kullandığı gerçeğine güvenmek, kodunuzu çapraz platform yapmak için iyi bir yoldur. Sadece libuv'nin genel arayüzü ile ilgilenmelisiniz.
Raynos

1
@Raynos libuv, x-platfousing çoklu kitaplıklarından emin olmayı amaçlamaktadır. Sağ ? bu nedenle libuv kullanmak iyi bir fikirdir
ShrekOverflow

1
@Abhishek Doc'tan process.nextTick- Olay döngüsünün bir sonraki döngüsünde bu geri aramayı çağırın. Bu setTimeout (fn, 0) için basit bir takma ad değildir, çok daha verimlidir. Bu hangi olay döngüsüne atıfta bulunuyor? V8 olay döngüsü?
Tamil


4
Bu olay kuyruğunu 'görmenin' bir yolu var mı? Yığındaki çağrıların sırasını görmek ve neler olduğunu daha iyi anlamak için oraya itilen yeni işlevleri görmek istiyorum ... olay que'sine ne itildiğini söyleyen bir değişken var mı?
tbarbe

20

Anlaşılan bazı varlıkların (örneğin: libev vb.) Bir süredir olması nedeniyle ilgisini kaybettiği görülüyor, ancak bence sorunun hala büyük bir potansiyeli var.

Olay temelli modelin çalışmasını soyut UNIX ortamında soyut bir örnek yardımıyla Düğüm bağlamında bugün itibariyle açıklamaya çalışayım.

Programın bakış açısı:

  • Komut dosyası altyapısı komut dosyasının yürütülmesini başlatır.
  • CPU bağlantılı bir işlemle her karşılaşıldığında, tam olarak satır içi (gerçek makine) yürütülür.
  • Bir G / Ç bağlantılı işlemle her karşılaşıldığında, istek ve tamamlama işleyicisi bir 'olay makinesi' (sanal makine) ile kaydedilir
  • Komut dosyası bitene kadar işlemleri aynı şekilde tekrarlayın. CPU bağlı çalışma - satır içi, I / O bağlı olanları çalıştırın, yukarıdaki gibi makineye istekte bulunun.
  • G / Ç tamamlandığında, dinleyiciler geri aranır.

Yukarıdaki olay makinesine libuv AKA olay döngüsü çerçevesi denir. Düğüm, olay odaklı programlama modelini uygulamak için bu kitaplığı kullanır.

Düğümün bakış açısı:

  • Çalışma zamanını barındırmak için bir iş parçacığına sahip olun.
  • Kullanıcı komut dosyasını alın.
  • Doğal olarak derleyin [kaldıraç v8]
  • İkili dosyayı yükleyin ve giriş noktasına atlayın.
  • Derlenen kod, programlama ilkellerini kullanarak CPU'ya bağlı faaliyetleri yerinde yürütür.
  • Birçok I / O ve zamanlayıcı ile ilgili kod yerel sargılara sahiptir. Örneğin, ağ G / Ç.
  • Bu nedenle, G / Ç çağrıları koddan C ++ köprülerine yönlendirilir, G / Ç tutamacı ve tamamlama işleyicisi bağımsız değişken olarak iletilir.
  • Yerel kod libuv döngüsünü kullanır. Döngüyü alır, G / Ç'yi temsil eden düşük seviyeli bir olayı ve libuv döngü yapısına yerel bir geri arama sarıcısını enqueque eder.
  • Yerel kod betiğe geri döner - şu anda hiçbir G / Ç gerçekleşmez!
  • Yukarıdaki öğeler, I / O olmayan tüm kodlar yürütülene ve tüm I / O kodu kaydedilinceye kadar birçok kez tekrarlanır.
  • Son olarak, sistemde yürütülecek hiçbir şey kalmadığında, düğüm kontrolü libuv'a geçirir.
  • libuv harekete geçer, tüm kayıtlı olayları alır, işletim sistemlerini çalışabilirliklerini sorgular.
  • Engellemesiz modda G / Ç için hazır olanlar alınır, G / Ç gerçekleştirilir ve geri çağrıları yapılır. Birbiri ardına.
  • Henüz hazır olmayanlar (örneğin, diğer bitiş noktasının henüz bir şey yazmadığı bir soket okuması), kullanılabilir olana kadar işletim sistemi ile incelenmeye devam edecektir.
  • Döngü dahili olarak sürekli artan bir zamanlayıcı tutar. Uygulama ertelenmiş bir geri arama (setTimeout gibi) istediğinde, bu dahili zamanlayıcı değeri geri aramanın tetiklenmesi için doğru zamanı hesaplamak için kullanılır.

İşlevlerin çoğu bu şekilde karşılanırken, dosya işlemlerinin bazıları (zaman uyumsuz sürümleri), libuv'a iyi entegre edilmiş ek iş parçacıklarının yardımıyla gerçekleştirilir. Ağ G / Ç işlemleri, verilerle yanıt veren diğer uç nokta gibi harici bir olay beklentisiyle beklerken, dosya işlemleri düğümün kendisinden biraz çalışmaya ihtiyaç duyar. Örneğin, bir dosyayı açar ve fd'nin verilerle hazır olmasını beklerseniz, kimse gerçekte okumadığından bu gerçekleşmez! Aynı zamanda, ana iş parçacığındaki dosya satırından okursanız, programdaki diğer etkinlikleri potansiyel olarak engelleyebilir ve dosya işlemleri cpu bağlantılı etkinliklere kıyasla çok yavaş olduğundan, görünür sorunlar yaratabilir. Bu nedenle dosyalar üzerinde çalışmak için dahili çalışan iş parçacıkları (UV_THREADPOOL_SIZE ortam değişkeni ile yapılandırılabilir) kullanılır,

Bu yardımcı olur umarım.


Bunları nasıl bildin, beni kaynağa yönlendirebilir misin?
Suraj Jain

18

Libuv'a Giriş

Node.js bir JavaScript ortamı tarayıcıdan üretimden bağımsız olarak proje 2009 yılında başladı. Google'ın V8 ve Marc Lehmann'ın libev'ini kullanan node.js , bir I / O modelini - olayla birlikte - programlama stiline çok uygun bir dille birleştirdi; tarayıcılar tarafından şekillendirilmiş olması nedeniyle. Node.js popülaritesi arttıkça, Windows üzerinde çalışmasını sağlamak önemliydi, ancak libev yalnızca Unix'te çalıştı. Kqueue veya (e) anket gibi çekirdek olay bildirim mekanizmalarının Windows eşdeğeri IOCP'dir. libuv platforma bağlı olarak libev veya IOCP etrafında bir soyutlamadır ve kullanıcılara libev tabanlı bir API sağlar. Libuv libev düğümü-v0.9.0 sürümü kaldırıldı .

Ayrıca @ BusyRich tarafından Node.js'deki Olay Döngüsünü açıklayan bir resim


Güncelleme 05/09/2017

Bu doc Node.js olay döngüsü başına ,

Aşağıdaki şemada, olay döngüsünün işlem sırasına basitleştirilmiş bir genel bakış gösterilmektedir.

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

not: her kutuya olay döngüsünün "aşaması" olarak atıf yapılacaktır.

Aşamalara Genel Bakış

  • zamanlayıcılar : bu aşama setTimeout()ve tarafından zamanlanan geri aramaları yürütür setInterval().
  • G / Ç geri çağrıları : yakın geri çağrılar , zamanlayıcılar tarafından zamanlananlar ve neredeyse tüm geri çağrıları hariç tutar setImmediate().
  • boşta, hazırlayın : sadece dahili olarak kullanılır.
  • anket : yeni G / Ç olaylarını alma; düğüm uygun olduğunda burada engellenir.
  • kontrol : setImmediate()geri aramalar burada çağrılır.
  • geri aramaları kapat : örn socket.on('close', ...).

Olay döngüsünün her çalışması arasında, Node.js herhangi bir eşzamansız G / Ç veya süreölçer beklediğini kontrol eder ve yoksa temiz bir şekilde kapanır.


Bunu " In the node-v0.9.0 version of libuv libev was removed" aktardınız, ancak nodejs'de bununla ilgili bir açıklama yok changelog. github.com/nodejs/node/blob/master/CHANGELOG.md . Ve libev kaldırılırsa, şimdi düğümlerde asenkron G / Ç nasıl yapılıyor?
intekhab

@intekhab, Bu bağlantıya göre , libeio tabanlı libuv'nin node.js'de olay döngüsü olarak kullanılabileceğini düşünüyorum.
zangw

@intekhab bence libuv, I / O ve yoklama ile ilgili tüm özellikleri uyguluyor. bu dokümanı buradan kontrol edin: docs.libuv.org/tr/v1.x/loop.html
mohit kaushik

13

NodeJs Mimarisinde bir olay döngüsü vardır.

Node.js Olay Döngüsü Modeli

Düğüm uygulamaları, tek iş parçacıklı olay güdümlü bir modelde çalışır. Ancak Düğüm, işin yapılabilmesi için arka planda bir iş parçacığı havuzu uygular.

Node.js bir olay kuyruğuna iş ekler ve daha sonra bir olay döngüsünü çalıştıran tek bir iş parçacığı vardır. Olay döngüsü, olay kuyruğundaki en üst öğeyi alır, yürütür ve sonra bir sonraki öğeyi alır.

Daha uzun ömürlü olan veya G / Ç'yi engelleyen kod yürütülürken, işlevi doğrudan çağırmak yerine, işlev tamamlandıktan sonra yürütülecek bir geri çağırma ile birlikte işlevi olay kuyruğuna ekler. Node.js olay kuyruğundaki tüm olaylar yürütüldüğünde, Node.js uygulaması sona erer.

Uygulama döngümüz G / Ç üzerinde bloke edildiğinde olay döngüsü kodlayıcı sorunlarını çözmeye başlar.

Node.js, G / Ç'yi engellemeyi beklemek zorunda kalmamak için olay geri çağrılarını kullanır. Bu nedenle, engelleme G / Ç gerçekleştiren tüm istekler arka planda farklı bir iş parçacığında gerçekleştirilir.

G / Ç'yi engelleyen bir olay olay kuyruğundan alındığında, Node.js iş parçacığı havuzundan bir iş parçacığı alır ve ana olay döngü iş parçacığı yerine burada işlevi yürütür. Bu, engelleme G / Ç'sinin olay kuyruğundaki diğer olayları beklemesini önler.


8

Libuv tarafından sağlanan tek bir olay döngüsü vardır, V8 sadece bir JS çalışma zamanı motorudur.


1

Bir javascript acemi olarak, aynı şüphe vardı, NodeJS 2 olay döngü içeriyor mu ?. Uzun bir araştırmadan ve V8 katılımcılarından biriyle tartıştıktan sonra, aşağıdaki kavramları aldım.

  • Olay döngüsü, JavaScript programlama modelinin temel bir soyut konseptidir. Böylece V8 motoru, katıştırıcıların (tarayıcı, düğüm) değiştirebileceği veya genişletebileceği olay döngüsü için varsayılan bir uygulama sağlar . Olay döngüsünün V8 varsayılan uygulamasını burada bulabilirsiniz
  • NodeJS'de, düğüm çalışma zamanı tarafından sağlanan yalnızca bir olay döngüsü vardır . V8 varsayılan olay döngüsü uygulaması, NodeJS olay döngüsü uygulaması ile değiştirildi

0

Bu pbkdf2işlevin JavaScript uygulaması vardır, ancak aslında C ++ tarafına yapılacak tüm işleri devreder.

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>);

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

Libuv modülünün, standart kütüphanedeki bazı çok özel işlevlerle ilgili başka bir sorumluluğu vardır.

Bazı standart kütüphane fonksiyon çağrıları için, Düğüm C ++ tarafı ve Libuv olay döngüsü dışında tamamen pahalı hesaplamalar yapmaya karar verir.

İş parçacığı havuzu adı verilen bir şey kullanmak yerine, iş parçacığı havuzu pbkdf2işlevi gibi hesaplamalı olarak pahalı görevleri çalıştırmak için kullanılabilecek dört iş parçacığı bir dizi .

Varsayılan olarak Libuv, bu iş parçacığı havuzunda 4 iş parçacığı oluşturur.

Olay döngüsünde kullanılan iş parçacıklarına ek olarak, uygulamamız içinde gerçekleşmesi gereken pahalı hesaplamaları boşaltmak için kullanılabilecek dört iş parçacığı daha vardır.

Düğüm standart kitaplığında bulunan işlevlerin çoğu otomatik olarak bu iş parçacığı havuzunu kullanır. pbkdf2Fonksiyon bunlardan biri.

Bu iş parçacığı havuzunun varlığı çok önemlidir.

Düğüm gerçekten tek iş parçacıklı değildir, çünkü Düğüm'ün hesaplamalı olarak pahalı bazı görevleri yapmak için kullandığı başka iş parçacıkları da vardır.

Olay havuzu, hesaplamalı olarak pahalı görevi yapmaktan sorumlu olsaydı, Düğüm uygulamamız başka hiçbir şey yapamazdı.

CPU'muz bir evre içindeki tüm talimatları tek tek çalıştırır.

İş parçacığı havuzunu kullanarak, bir olay döngüsü içinde hesaplamalar yapılırken başka şeyler yapabiliriz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.