Lösung 1:
Danke @MichaelHampton für deine Hilfe.
Ich habe eine Lösung für mein Problem gefunden und hoffe, dass sie anderen helfen kann (insbesondere wenn Sie Java verwenden).
Ich habe viele Vorschläge gehört, einfach nofiles
zu erhöhen mehr Verbindungen zuzulassen, aber ich möchte zunächst wiederholen, dass das Problem nicht darin besteht, dass der Server keine weiteren Verbindungen herstellen kann, sondern dass er nicht in der Lage ist, Verbindungen schnell genug herzustellen und Verbindungen abbricht.
Mein erster Versuch, dieses Problem zu lösen, bestand darin, die Verbindungswarteschlange um net.ipv4.tcp_max_syn_backlog
zu erhöhen , net.core.somaxconn
und ggf. erneut in der Serverkonfiguration der Anwendung. Für vertx ist dies server.setAcceptBacklog(...);
. Dies führte dazu, dass mehr Verbindungen in der Warteschlange akzeptiert wurden, der Verbindungsaufbau jedoch nicht schneller wurde. Aus Sicht eines sich verbindenden Clients wurden die Verbindungen nicht mehr aufgrund von Überlauf zurückgesetzt, der Verbindungsaufbau dauerte nur viel länger. Aus diesem Grund war das Erhöhen der Verbindungswarteschlange keine wirkliche Lösung und tauschte nur ein Problem gegen ein anderes aus.
Beim Versuch einzugrenzen, wo im Verbindungsprozess der Engpass war, habe ich dieselben Benchmarks mit HTTP anstelle von HTTPS ausprobiert und festgestellt, dass das Problem vollständig verschwunden ist. Mein besonderes Problem war der TLS-Handshake selbst und die Fähigkeit des Servers, ihn zu erfüllen.
Als ich mich weiter mit meiner eigenen Anwendung beschäftigte, stellte ich fest, dass das Ersetzen des Standard-SSLHandlers von Java durch einen nativen (OpenSSL) die Verbindungsgeschwindigkeit über HTTPS erheblich erhöhte.
Hier sind die Änderungen, die ich für meine spezielle Anwendung vorgenommen habe (mit Vertx 3.9.1).
- Netty-tcnative-Abhängigkeiten hinzufügen
<!-- 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>
Die erste Abhängigkeit besteht darin, dass osx zur Laufzeit testet. Die zweite ist für Centos Linux, wenn sie kompiliert wird. linux-x86_64
ist auch für andere Geschmacksrichtungen erhältlich. Ich habe versucht, boringssl
zu verwenden weil openssl
unterstützt ALPN
nicht aber nach vielen Stunden konnte ich es nicht zum Laufen bringen, also habe ich mich entschieden, vorerst ohne http2 zu leben. Da die meisten Verbindungen nur 1-2 kleine Anfragen senden, bevor die Verbindung getrennt wird, ist dies sowieso kein Problem für mich. Wenn Sie boringssl
verwenden könnten stattdessen wird das wahrscheinlich bevorzugt.
- Weil ich keine Uber-Version der Abhängigkeit verwende. Ich musste die Betriebssystemabhängigkeiten für Centos installieren. Dies wurde dem Dockerfile hinzugefügt
RUN yum -y install openssl
RUN yum -y install apr
- Um dem Vertx-Server mitzuteilen, dass er OpenSSL anstelle der Java-Version verwenden soll, legen Sie die OpenSSL-Optionen auf dem Server fest (selbst wenn es nur das Standardobjekt ist)
httpServerOptions.setOpenSslEngineOptions(new OpenSSLEngineOptions());
- Schließlich habe ich in meinem Run-Skript den
io.netty.handler.ssl.openssl.useTasks=true
hinzugefügt Option zu Java. Dies weist den SSL-Handler an, bei der Bearbeitung der Anfragen Aufgaben zu verwenden, damit es nicht blockiert.
java -Dio.netty.handler.ssl.openssl.useTasks=true -jar /app/application.jar
Nach diesen Änderungen kann ich Verbindungen viel schneller und mit weniger Overhead herstellen. Was vorher Dutzende von Sekunden dauerte und zu häufigen Verbindungs-Resets führte, dauert jetzt 1-2 Sekunden ohne Resets. Könnte besser sein, aber eine große Verbesserung gegenüber dem, wo ich war.
Lösung 2:
Gute Lösung!.
Es scheint also die SSL-Schicht zu sein, die sicherlich viel mehr Verarbeitung in Bezug auf Netzwerk-Handshakes und Kryptotransformationen leisten muss, die Ressourcen beanspruchen. Sofern Ihr SSL nicht einen Teil der Verarbeitung auf Hardware auslagern kann, kann SSL sicherlich die Last auf Ihren Servern erhöhen, und wie Sie herausgefunden haben, sind nicht alle SSL-Bibliotheken gleich!.
Diese Probleme sind ein großartiger Kandidat für einen Front-End-Reverse-Proxy. Dies kann idealerweise vor Ihrer Anwendung platziert werden und alle SSL-Verbindungen zu Clients verarbeiten und dann http zu Ihrem Back-End ausführen.
Ihre ursprüngliche Anwendung hat etwas weniger zu tun, da Ihr Front-End-Reverse-Proxy die gesamte SSL-Arbeit und die TCP-Verbindungsverwaltung aufsaugen kann.
Apache und NGNIX können dies tun und haben einige Optionen für den Lastausgleich dieser Verbindungen zum am wenigsten belasteten Backend-Server.
Sie werden feststellen, dass NGNIX SSL-Beendigungen viel schneller durchführen kann als Java, und selbst wenn Java dies kann, verteilen Sie die Verarbeitung der Verbindungsverwaltung auf mehrere Maschinen und reduzieren so die Last (Speicher/CPU/Festplatten-IO) auf Ihrem Back-End-Server. Als Nebeneffekt wird die Konfiguration des Backends vereinfacht.
Der Nachteil ist, dass Sie http zwischen Ihrem Proxy und Anwendungen verwenden, was in einigen ultrasicheren Umgebungen nicht wünschenswert ist.
Viel Glück!