Soluzione 1:
Grazie @MichaelHampton per il tuo aiuto.
Ho trovato una soluzione per il mio problema e spero che possa aiutare gli altri (in particolare se stai usando Java).
Ho sentito molti suggerimenti per aumentare semplicemente nofiles
per consentire più connessioni, ma vorrei iniziare ribadendo che il problema non è che il server non è in grado di stabilire più connessioni, è che non è in grado di effettuare connessioni abbastanza veloci e interrompe le connessioni.
Il mio primo tentativo di risolvere questo problema è stato aumentare la coda di connessione tramite net.ipv4.tcp_max_syn_backlog
, net.core.somaxconn
e di nuovo nella configurazione del server dell'applicazione, se del caso. Per vertx questo è server.setAcceptBacklog(...);
. Ciò ha comportato l'accettazione di più connessioni in coda, ma non ha reso più veloce la creazione delle connessioni. Dal punto di vista di un client in connessione, non venivano più reimpostate le connessioni a causa dell'overflow, la creazione delle connessioni richiedeva solo molto più tempo. Per questo motivo, l'aumento della coda di connessione non era una vera soluzione e si limitava a scambiare un problema con un altro.
Cercando di restringere il punto in cui si trovava il collo di bottiglia nel processo di connessione, ho provato gli stessi benchmark con HTTP anziché HTTPS e ho scoperto che il problema è scomparso completamente. Il mio problema particolare riguardava lo stesso TLS Handshake e la capacità dei server di soddisfarlo.
Dopo aver approfondito ulteriormente la mia applicazione, ho scoperto che la sostituzione dell'SSLHandler predefinito di Java con uno nativo (OpenSSL) ha notevolmente aumentato la velocità di connessione tramite HTTPS.
Ecco le modifiche che ho apportato per la mia applicazione specifica (utilizzando Vertx 3.9.1).
- Aggiungi dipendenze netty-tcnative
<!-- https://mvnrepository.com/artifact/io.netty/netty-tcnative -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative</artifactId>
<version>2.0.31.Final</version>
<classifier>osx-x86_64</classifier>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/io.netty/netty-tcnative -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative</artifactId>
<version>2.0.31.Final</version>
<classifier>linux-x86_64-fedora</classifier>
<scope>compile</scope>
</dependency>
La prima dipendenza è per osx da testare in fase di esecuzione. Il secondo è per centos linux una volta compilato. linux-x86_64
è disponibile anche per altri gusti. Ho provato a usare boringssl
perché openssl
non supporta ALPN
ma dopo molte ore non riuscivo a farlo funzionare così ho deciso di vivere senza http2 per ora. Con la maggior parte delle connessioni che inviano solo 1-2 piccole richieste prima di disconnettersi, questo non è comunque un problema per me. Se potessi usare boringssl
invece, probabilmente è preferibile.
- Perché non sto usando una versione uber della dipendenza. Avevo bisogno di installare le dipendenze del sistema operativo per centos. Questo è stato aggiunto al Dockerfile
RUN yum -y install openssl
RUN yum -y install apr
- Per dire al server vertx di usare OpenSSL invece della versione Java, imposta le opzioni OpenSSL sul server (anche se solo l'oggetto predefinito)
httpServerOptions.setOpenSslEngineOptions(new OpenSSLEngineOptions());
- Infine, nel mio script run, ho aggiunto il
io.netty.handler.ssl.openssl.useTasks=true
opzione per Java. Questo dice al gestore ssl di utilizzare le attività durante la gestione delle richieste in modo che non sia bloccante.
java -Dio.netty.handler.ssl.openssl.useTasks=true -jar /app/application.jar
Dopo queste modifiche, sono in grado di stabilire connessioni molto più rapidamente con meno spese generali. Ciò che prima richiedeva decine di secondi e comportava frequenti ripristini della connessione, ora richiede 1-2 secondi senza ripristini. Potrebbe essere migliore, ma un grande miglioramento rispetto a dove mi trovavo.
Soluzione 2:
Bella soluzione!.
Quindi sembra essere il livello SSL, deve certamente eseguire molte più elaborazioni, in termini di strette di mano di rete e trasformazioni crittografiche che richiedono risorse. A meno che il tuo SSL non possa scaricare parte dell'elaborazione sull'hardware, SSL può certamente aumentare il carico sui tuoi server e, come hai scoperto, non tutte le librerie SSL sono uguali!.
Questi problemi sono un ottimo candidato per un proxy inverso front-end. Idealmente, questo può essere posizionato prima della tua applicazione e gestire tutte le connessioni SSL ai client, quindi eseguire http sul tuo back-end.
La tua applicazione originale ha un po' meno da fare, poiché il tuo proxy inverso front-end può assorbire tutto il lavoro SSL e la gestione della connessione tcp.
Apache e NGNIX possono farlo e hanno diverse opzioni per bilanciare il carico di quelle connessioni al server di backend meno carico.
Scoprirai che NGNIX può eseguire terminazioni SSL molto più velocemente di quanto possa fare java, e anche se java può, distribuendo l'elaborazione della gestione della connessione tra le macchine, riducendo così il carico (memoria/cpu/disco io) sul tuo back-end server. Ottieni l'effetto collaterale di semplificare la configurazione del back-end.
Lo svantaggio è l'utilizzo di http tra il proxy e le applicazioni, che in alcuni ambienti ultra sicuri non è desiderabile.
Buona fortuna!