Molti blocchi contro singoli lavoratori non bloccanti

7

Supponiamo che ci sia un server HTTP che accetta le connessioni e poi in qualche modo aspetta l'invio completo delle intestazioni. Mi chiedo quale sia il modo più comune di implementarlo e quali sono gli altri vantaggi e contro. Posso solo pensare a questi:

Molti lavoratori di blocco sono bravi perché:

  • È più reattivo.
  • È più facile introdurre nuove connessioni (i lavoratori le prendono in carico anziché estranee fino a quando non possono aggiungerle a un elenco sincronizzato).
  • I saldi di utilizzo della CPU automaticamente (senza ulteriori sforzi) in quanto il numero di connessioni aumenta o diminuisce.
  • Minore utilizzo della CPU (i thread bloccati vengono esclusi dal ciclo di esecuzione e non richiedono alcuna logica per saltare tra i client).

Un singolo lavoratore non bloccante è valido perché:

  • Utilizza meno memoria.
  • Meno vulnerabili ai client pigri (che si connettono al server e inviano le intestazioni lentamente o non inviano affatto).

Come probabilmente puoi vedere, a mio parere, più thread di lavoro sembrano una soluzione un po 'migliore in generale. L'unico problema è che è più facile attaccare tale server.

Modifica (ulteriori ricerche): Alcune risorse che ho trovato sul web ( Migliaia di thread e blocco I / O - Il vecchio modo di scrivere Java Servers è di nuovo nuovo (e molto meglio) di Paul Tyma) suggerisce che l'approccio di blocco è generalmente migliore, ma non so ancora come gestire le connessioni false.

P.S. Non suggerire l'uso di alcune librerie o applicazioni per l'attività. Sono più interessato a sapere come funziona o potrebbe funzionare piuttosto che farlo funzionare.

P.S.S. Ho diviso la logica in più parti e questo gestisce solo l'accettazione di intestazioni HTTP. Non li processa.

    
posta Pijusn 03.01.2013 - 18:51
fonte

1 risposta

3

Non ci sono puntini d'argento

In pratica dipende ...

tl; dr - soluzione facile, usa nginx ...

Blocco:

Ad esempio, Apache utilizza per default uno schema di blocco in cui il processo è biforcato per ogni connessione. Ciò significa che ogni connessione ha bisogno del proprio spazio di memoria e l'enorme quantità di overhead di commutazione del contesto aumenta con l'aumentare del numero di connessioni. Ma il vantaggio è che, una volta chiusa una connessione, il contesto può essere eliminato e tutta la memoria può essere facilmente recuperata.

Un approccio multi-thread sarebbe simile in quanto il sovraccarico del switching di contesto aumenta con il numero di connessioni ma può essere più efficiente in termini di memoria in un contesto condiviso. Il problema con un simile approccio è che è difficile gestire la memoria condivisa in un modo sicuro. Gli approcci per superare i problemi di sincronizzazione della memoria spesso includono il proprio overhead, ad esempio il blocco può bloccare il thread principale su carichi intensi della CPU, e l'uso di tipi immutabili aggiunge un sacco di copie non necessarie dei dati.

AFAIK, utilizzando un approccio multi-processo su un server HTTP di blocco è generalmente preferito perché è più sicuro / più semplice da gestire / ripristinare la memoria in un modo sicuro. La raccolta dei dati inutili diventa un problema quando il recupero della memoria è semplice come arrestare un processo. Per i processi a esecuzione prolungata (ovvero un daemon), questa caratteristica è particolarmente importante.

Mentre il sovraccarico di commutazione del contesto può sembrare insignificante con un numero limitato di lavoratori, gli svantaggi diventano più rilevanti in quanto il carico scala fino a centinaia-a-migliaia di connessioni simultanee. Nella migliore delle ipotesi, il cambio di contesto scala O (n) al numero di lavoratori presenti, ma in pratica è più probabile che sia peggio.

Laddove i server che utilizzano il blocco non siano la scelta ideale per carichi pesanti di I / O, sono ideali per il lavoro intensivo della CPU e il passaggio dei messaggi è ridotto al minimo.

non-blocking:

Il non blocco sarebbe qualcosa come Node.js o nginx. Questi sono particolarmente noti per il ridimensionamento a un numero molto più grande di connessioni per nodo sotto carico intensivo di IO. Fondamentalmente, una volta che le persone raggiungevano il limite massimo di ciò che i server thread / basati potevano gestire, iniziarono a esplorare opzioni alternative. Questo è altrimenti noto come problema C10K (ovvero la possibilità di gestire 10.000 connessioni simultanee).

I server asincroni non bloccanti generalmente condividono molte caratteristiche con un approccio multi-thread con blocco in quanto bisogna fare attenzione a evitare carichi intensivi della CPU perché non si vuole sovraccaricare il thread principale. Il vantaggio è che il sovraccarico dovuto al cambio di contesto è essenzialmente eliminato e con un solo messaggio di contesto che passa diventa un non-problema.

Anche se potrebbe non funzionare per molti protocolli di rete, la natura stateless di HTTP funziona particolarmente bene per le architetture non bloccanti. Utilizzando la combinazione di un proxy inverso e di più server HTTP non bloccanti, è possibile identificare e instradare i nodi in presenza di carichi pesanti.

Anche su un server che ha un solo nodo, è molto comune che l'installazione includa un server per core del processore per massimizzare il throughput.

Entrambi:

Il caso d'uso "ideale" sarebbe una combinazione di entrambi. Un proxy inverso nella parte anteriore dedicato alle richieste di routing nella parte superiore, quindi un mix di server bloccanti e non bloccanti. Non bloccante per attività di I / O come la pubblicazione di contenuto statico, contenuto della cache, contenuto HTML. Blocco per attività pesanti come la codifica di immagini / video, streaming di contenuti, numero di crunch, scritture di database, ecc.

Nel tuo caso:

Se stai solo controllando le intestazioni ma non elaborando effettivamente le richieste, ciò che stai essenzialmente descrivendo è un proxy inverso. In tal caso, andrei sicuramente con un approccio asincrono.

Suggerisco di consultare la documentazione del proxy inverso nginx .

A parte:

Ho letto il write-up dal link che hai fornito e ha senso che async fosse una scelta sbagliata per la loro particolare implementazione. Il problema può essere riassunto in una dichiarazione.

Found that when switching between clients, the code for saving and restoring values/state was difficult

Stavano costruendo una piattaforma di stato. In tal caso, un approccio asincrono significherebbe che si dovrebbe costantemente salvare / caricare lo stato ogni volta che il contesto cambia (vale a dire quando un evento si attiva). Inoltre, sul lato SMTP stanno facendo un sacco di lavoro intensivo della CPU.

Sembra che avessero una conoscenza piuttosto scarsa dell'async e, di conseguenza, hanno fatto un sacco di ipotesi sbagliate.

    
risposta data 03.01.2013 - 21:46
fonte

Leggi altre domande sui tag