Solution 1 :
Merci @MichaelHampton pour votre aide.
J'ai trouvé une solution à mon problème et j'espère que cela pourra aider les autres (en particulier si vous utilisez Java).
J'ai entendu de nombreuses suggestions pour simplement augmenter nofiles
pour autoriser plus de connexions, mais je voudrais commencer par réitérer que le problème n'est pas que le serveur n'est pas capable d'établir plus de connexions, c'est qu'il n'est pas capable d'établir des connexions assez rapidement et de perdre des connexions.
Ma première tentative pour résoudre ce problème a été d'augmenter la file d'attente de connexion jusqu'à net.ipv4.tcp_max_syn_backlog
, net.core.somaxconn
et à nouveau dans la configuration du serveur de l'application, le cas échéant. Pour vertx c'est server.setAcceptBacklog(...);
. Cela a entraîné l'acceptation de plus de connexions dans la file d'attente, mais cela n'a pas accéléré l'établissement des connexions. Du point de vue d'un client qui se connecte, les connexions ne sont plus réinitialisées en raison d'un débordement, l'établissement de connexions prend simplement beaucoup plus de temps. Pour cette raison, augmenter la file d'attente de connexion n'était pas une vraie solution et ne faisait qu'échanger un problème contre un autre.
En essayant de déterminer où se trouvait le goulot d'étranglement dans le processus de connexion, j'ai essayé les mêmes repères avec HTTP au lieu de HTTPS et j'ai constaté que le problème avait complètement disparu. Mon problème particulier était avec le TLS Handshake lui-même et la capacité des serveurs à le satisfaire.
En creusant un peu plus dans ma propre application, j'ai constaté que le remplacement du SSLHandler par défaut de Java par un natif (OpenSSL) augmentait considérablement la vitesse de connexion via HTTPS.
Voici les modifications que j'ai apportées à mon application spécifique (en utilisant Vertx 3.9.1).
- Ajouter des dépendances 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 première dépendance est pour osx de tester au moment de l'exécution. Le second est pour Centos Linux lors de la compilation. linux-x86_64
est également disponible pour d'autres saveurs. J'ai essayé d'utiliser boringssl
parce que openssl
ne prend pas en charge ALPN
mais après de nombreuses heures, je n'arrivais pas à le faire fonctionner, j'ai donc décidé de vivre sans http2 pour l'instant. Avec la plupart des connexions n'envoyant que 1 à 2 petites requêtes avant de se déconnecter, ce n'est vraiment pas un problème pour moi de toute façon. Si vous pouviez utiliser boringssl
à la place, c'est probablement préféré.
- Parce que je n'utilise pas une version uber de la dépendance. J'avais besoin d'installer les dépendances du système d'exploitation pour centos. Ceci a été ajouté au Dockerfile
RUN yum -y install openssl
RUN yum -y install apr
- Pour indiquer au serveur vertx d'utiliser OpenSSL au lieu de la version Java, définissez les options OpenSSL sur le serveur (même s'il ne s'agit que de l'objet par défaut)
httpServerOptions.setOpenSslEngineOptions(new OpenSSLEngineOptions());
- Enfin, dans mon script d'exécution, j'ai ajouté le
io.netty.handler.ssl.openssl.useTasks=true
option à Java. Cela indique au gestionnaire SSL d'utiliser des tâches lors du traitement des requêtes afin qu'il ne soit pas bloquant.
java -Dio.netty.handler.ssl.openssl.useTasks=true -jar /app/application.jar
Après ces changements, je suis en mesure d'établir des connexions beaucoup plus rapidement avec moins de frais généraux. Ce qui prenait des dizaines de secondes auparavant et entraînait de fréquentes réinitialisations de connexion prend désormais 1 à 2 secondes sans réinitialisation. Cela pourrait être mieux, mais une grande amélioration par rapport à ce que j'étais.
Solution 2 :
Bonne solution !
Il semble donc que ce soit la couche SSL, elle doit certainement faire beaucoup plus de traitement, en termes de poignées de main réseau et de transformations cryptographiques qui prennent des ressources. À moins que votre SSL ne puisse décharger une partie du traitement sur le matériel, SSL peut certainement augmenter la charge sur vos serveurs, et comme vous l'avez découvert, toutes les bibliothèques SSL ne sont pas créées égales !.
Ces problèmes sont un excellent candidat pour un proxy inverse frontal. Cela peut idéalement être placé avant votre application et gérer toutes les connexions SSL aux clients, puis faire http à votre back-end.
Votre application d'origine a un peu moins à faire, car votre proxy inverse frontal peut absorber tout le travail SSL et la gestion des connexions TCP.
Apache et NGNIX peuvent le faire et disposent de plusieurs options pour équilibrer la charge de ces connexions vers le serveur principal le moins chargé.
Vous constaterez que NGNIX peut effectuer des terminaisons SSL beaucoup plus rapidement que Java, et même si Java le peut, vous distribuez le traitement de la gestion des connexions entre les machines, réduisant ainsi la charge (mémoire/cpu/disk io) sur votre serveur principal. Vous obtenez l'effet secondaire de simplifier la configuration du back-end.
L'inconvénient est que vous utilisez http entre votre proxy et les applications, ce qui n'est pas souhaitable dans certains environnements ultra sécurisés.
Bonne chance !