Node.js
Eventloop
Ricerca…
introduzione
In questo post discuteremo come è emerso il concetto di Eventloop e come può essere utilizzato per server ad alte prestazioni e applicazioni guidate da eventi come le GUI.
Come si è evoluto il concetto di event loop.
Eventloop in pseudo codice
Un ciclo di eventi è un ciclo che attende gli eventi e quindi reagisce a quegli eventi
while true:
wait for something to happen
react to whatever happened
Esempio di un server HTTP a thread singolo senza loop di eventi
while true:
socket = wait for the next TCP connection
read the HTTP request headers from (socket)
file_contents = fetch the requested file from disk
write the HTTP response headers to (socket)
write the (file_contents) to (socket)
close(socket)
Ecco una semplice forma di un server HTTP che è un singolo thread ma nessun ciclo di eventi. Il problema qui è che attende fino a quando ogni richiesta è terminata prima di iniziare a elaborare il successivo. Se è necessario un po 'di tempo per leggere le intestazioni delle richieste HTTP o per recuperare il file dal disco, dovremmo essere in grado di avviare l'elaborazione della richiesta successiva mentre attendiamo che finisca.
La soluzione più comune è rendere il programma multi-threaded.
Esempio di un server HTTP multi-thread con nessun ciclo di eventi
function handle_connection(socket):
read the HTTP request headers from (socket)
file_contents = fetch the requested file from disk
write the HTTP response headers to (socket)
write the (file_contents) to (socket)
close(socket)
while true:
socket = wait for the next TCP connection
spawn a new thread doing handle_connection(socket)
Ora abbiamo reso il nostro piccolo server HTTP multi thread. In questo modo, possiamo passare immediatamente alla richiesta successiva perché la richiesta corrente è in esecuzione in un thread in background. Molti server, incluso Apache, utilizzano questo approccio.
Ma non è perfetto. Una limitazione è che puoi generare solo tanti thread. Per carichi di lavoro in cui si dispone di un numero enorme di connessioni, ma ogni connessione richiede solo l'attenzione di tanto in tanto, il modello multi-thread non funzionerà molto bene. La soluzione per questi casi è utilizzare un ciclo di eventi:
Esempio di un server HTTP con ciclo di eventi
while true:
event = wait for the next event to happen
if (event.type == NEW_TCP_CONNECTION):
conn = new Connection
conn.socket = event.socket
start reading HTTP request headers from (conn.socket) with userdata = (conn)
else if (event.type == FINISHED_READING_FROM_SOCKET):
conn = event.userdata
start fetching the requested file from disk with userdata = (conn)
else if (event.type == FINISHED_READING_FROM_DISK):
conn = event.userdata
conn.file_contents = the data we fetched from disk
conn.current_state = "writing headers"
start writing the HTTP response headers to (conn.socket) with userdata = (conn)
else if (event.type == FINISHED_WRITING_TO_SOCKET):
conn = event.userdata
if (conn.current_state == "writing headers"):
conn.current_state = "writing file contents"
start writing (conn.file_contents) to (conn.socket) with userdata = (conn)
else if (conn.current_state == "writing file contents"):
close(conn.socket)
Speriamo che questo pseudocodice sia comprensibile. Ecco cosa sta succedendo: Aspettiamo che le cose accadano. Ogni volta che viene creata una nuova connessione o una connessione esistente richiede la nostra attenzione, ci occupiamo di essa, quindi torniamo ad aspettare. In questo modo, ci comportiamo bene quando ci sono molte connessioni e ognuna raramente richiede attenzione.
In un'applicazione reale (non pseudocodice) in esecuzione su Linux, la parte "aspetta che succeda l'evento successivo" sarebbe implementata chiamando la chiamata di sistema poll () o epoll (). Le parti "inizia a leggere / scrivere qualcosa su un socket" sarebbero implementate chiamando le chiamate di sistema recv () o send () in modalità non bloccante.
Riferimento:
[1]. "Come funziona un ciclo di eventi?" [In linea]. Disponibile: https://www.quora.com/How-does-an-event-loop-work