javascript - Hvordan håndterer Nodejs input/output under motorhjelmen?


Jeg ved, at nodejs bruger trådpuljen leveret af libuv til at udføre samtidige opgaver. Jeg har læst, at nodejs kun har brug for en af disse tråde til at udføre alle i/o-bundne aktiviteter.

Hvordan sker de ikke-blokerende filoperationer så?

Hvad vil faktisk ske bag kulisserne mellem node, disk og OS, hvis jeg itererer over en mappe, der indeholder 100 filer, og åbner alle dem ved hjælp af readFile()?

Hvis man udfører den samme operation i et andet multitrådet og blokerende sprog, vil det resultere i, at tråde venter på, at i/o afsluttes. Så hvem venter på, at i/o afsluttes i Node.js, især når der sker flere filoperationer samtidig?

Tak!

Svar #1

Fil-I/O i Node.js bruger en trådpulje. Som standard er der 4 tråde i puljen.

Når noget Javascript kalder en filoperation såsom fs.open() i nodejs, bliver kaldet sendt til noget native kode inden for libuv-biblioteket i nodejs-implementeringen. Den kode tilføjer den specifikke filoperation til en kø af elementer, der venter på en OS-tråd. Hvis en OS-tråd er straks tilgængelig, bliver denne OS-tråd tildelt opgaven med at udføre filoperationen, og derefter returnerer den oprindelige libuv-funktion, som returnerer tilbage til den oprindelige Javascript, og nodejs fortsætter med at køre anden Javascript.

Derefter, senere færdiggør tråden filoperationen og har et resultat. Den indsætter en begivenhed i Node.js-begivenhedskøen med det fuldførte resultat og den oprindelige callback, der blev sendt med den oprindelige filoperation.

Når Node.js er færdig med at køre enhver anden Javascript, det kørte, trækker det den næste begivenhed fra begivenhedskøen og kalder den Javascript-callback, der er knyttet til den begivenhed, og sender de data, der er knyttet til begivenheden. Dette udløser, at den Javascript-callback, der oprindeligt blev sendt til fs.open(), bliver kaldt.

Hvis der i det andet trin ovenfor ikke var nogen tilgængelige tråde i trådpuljen, bliver opgaven tilføjet til en kø, og libuv returnerer straks kontrollen tilbage til nodejs, så det kan køre andet Javascript. Nogle gange senere, når en af de eksisterende tråde i puljen er færdig og har afsluttet sit arbejde med at tilføje en hændelse til hændelseskøen, vil den tage det næste element ud af trådpuljekøen, give det den nu tilgængelige tråd og lade det begynde at køre sin operation.

På denne måde ser alle filoperationer ud til at være ikke-blokerende og asynkrone fra Javascript-siden af tingene. De bliver enten startet med det samme, hvis der er en tilgængelig tråd i trådpuljen, eller de sidder i en kø for trådpuljen, indtil en tråd bliver tilgængelig. Den del er usynlig for Javascript-siden af tingene. Under alle omstændigheder bliver operationen enten igangsat eller sat i kø, og kontrollen returnerer straks tilbage til nodejs for at fortsætte med at køre anden Javascript. Og under alle omstændigheder afsluttes operationen på et tidspunkt senere, og den Javascript-callback, der er knyttet til filoperationen, bliver kaldt via Javascript-hændelseskøen. Dette giver den asynkrone, ikke-blokerende, hændelsesdrevne mekanisme, som nodejs bruger til al fil-I/O.