Solución 1:
Gracias @MichaelHampton por tu ayuda.
Encontré una solución para mi problema y espero que pueda ayudar a otros (especialmente si está usando Java).
He escuchado muchas sugerencias para simplemente aumentar nofiles
para permitir más conexiones, pero me gustaría comenzar reiterando que el problema no es que el servidor no pueda hacer más conexiones, es que no puede hacer las conexiones lo suficientemente rápido y las interrumpe.
Mi primer intento de resolver este problema fue aumentar la cola de conexión a través de net.ipv4.tcp_max_syn_backlog
, net.core.somaxconn
y nuevamente en la configuración del servidor de la aplicación, según corresponda. Para vertx esto es server.setAcceptBacklog(...);
. Esto resultó en la aceptación de más conexiones en la cola, pero no hizo que el establecimiento de las conexiones fuera más rápido. Desde el punto de vista de un cliente que se conecta, ya no se restablecieron las conexiones debido al desbordamiento, sino que el establecimiento de las conexiones tomó mucho más tiempo. Por esta razón, aumentar la cola de conexión no fue una solución real y simplemente cambió un problema por otro.
Tratando de reducir en qué parte del proceso de conexión estaba el cuello de botella, probé los mismos puntos de referencia con HTTP en lugar de HTTPS y descubrí que el problema desapareció por completo. Mi problema particular fue con el TLS Handshake en sí mismo y la capacidad de los servidores para satisfacerlo.
Al investigar un poco más en mi propia aplicación, descubrí que reemplazar el SSLHandler predeterminado de Java con uno nativo (OpenSSL) aumentó en gran medida la velocidad de conexión a través de HTTPS.
Estos fueron los cambios que hice para mi aplicación específica (usando Vertx 3.9.1).
- Añadir dependencias 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 primera dependencia es para que osx pruebe en tiempo de ejecución. El segundo es para centos linux cuando se compila. linux-x86_64
también está disponible para otros sabores. Intenté usar boringssl
porque openssl
no es compatible con ALPN
pero después de muchas horas no pude hacer que funcionara, así que decidí vivir sin http2 por ahora. Con la mayoría de las conexiones que solo envían 1 o 2 solicitudes pequeñas antes de desconectarse, esto realmente no es un problema para mí de todos modos. Si pudieras usar boringssl
en su lugar, probablemente se prefiera.
- Porque no estoy usando una versión súper de la dependencia. Necesitaba instalar las dependencias del sistema operativo para centos. Esto se agregó al Dockerfile
RUN yum -y install openssl
RUN yum -y install apr
- Para decirle al servidor vertx que use OpenSSL en lugar de la versión de Java, configure las opciones de OpenSSL en el servidor (incluso si solo es el objeto predeterminado)
httpServerOptions.setOpenSslEngineOptions(new OpenSSLEngineOptions());
- Finalmente, en mi script de ejecución, agregué el
io.netty.handler.ssl.openssl.useTasks=true
opción a Java. Esto le dice al controlador ssl que use tareas cuando maneje las solicitudes para que no bloquee.
java -Dio.netty.handler.ssl.openssl.useTasks=true -jar /app/application.jar
Después de estos cambios, puedo establecer conexiones mucho más rápido con menos gastos generales. Lo que antes tomaba decenas de segundos y resultaba en reinicios frecuentes de la conexión, ahora toma de 1 a 2 segundos sin reinicios. Podría ser mejor, pero una gran mejora desde donde estaba.
Solución 2:
¡Buen arreglo!.
Entonces parece ser la capa SSL, ciertamente tiene que hacer mucho más procesamiento, en términos de protocolos de enlace de red y transformaciones criptográficas que consumen recursos. A menos que su SSL pueda descargar parte del procesamiento en el hardware, SSL sin duda puede aumentar la carga en sus servidores y, como descubrió, ¡no todas las bibliotecas SSL son iguales!
Estos problemas son un gran candidato para un proxy inverso front-end. Idealmente, esto se puede colocar antes de su aplicación, y manejar todas las conexiones SSL a los clientes, y luego hacer http en su back-end.
Su aplicación original tiene un poco menos que hacer, ya que su proxy inverso front-end puede absorber todo el trabajo de SSL y la administración de conexiones tcp.
Apache y NGNIX pueden hacer esto y tienen bastantes opciones para equilibrar la carga de esas conexiones al servidor backend menos cargado.
Encontrará que NGNIX puede hacer terminaciones SSL mucho más rápido que Java, e incluso si Java puede, está distribuyendo el procesamiento de la administración de la conexión entre las máquinas, reduciendo así la carga (memoria/cpu/disco io) en su servidor back-end. Obtiene el efecto secundario de simplificar la configuración del back-end.
La desventaja es que usa http entre su proxy y las aplicaciones, lo que en algunos entornos ultra seguros no es deseable.
¡Buena suerte!