如何处理新的 HTTPS 连接中的突然爆发?

mic*_*cah 6 networking linux http java tcp

我在负载平衡器后面有一组 Java Vertx 服务器,用于处理峰值流量。一分钟它可能处理 150k r/m,下一分钟它可能处理 2mm r/m,然后又回到 150k r/m。我发现在这些高峰期间,整个队列可能会在几分钟内无响应并断开连接,而任何一台机器上的 cpu 和 mem 压力几乎没有达到 50% 的利用率。

为了测试究竟是什么导致了中断,我设置了一个测试服务器,它与我的生产队列中的一个服务器的规格相匹配,以查看在它发出之前我可以投入多少。我的测试涉及使用其他 10 台机器,每台机器打开 500 个到服务器的 https 连接,并发送 1mm 请求,每个请求有效负载约 2kb。这总共打开了 5k 个并发连接,发送了总共 10mm 的请求,用于大约 20gb 的数据传输。

一旦连接打开,我每分钟可以发出大约 70 万个请求。我只是通过向健康端点发出请求并记录响应时间来监控服务器的可用性。响应时间快,几十毫秒。我对这些结果很满意。

但是在大量数据开始进入之前,这 10 台机器必须首先建立 5k 连接。在此期间,服务器无响应,甚至在我尝试检查运行状况端点时可能超时。我相信这就是导致我的生产车队中断的原因 - 新连接的突然增加。一旦建立连接,服务器就可以轻松处理传入的所有数据。

我已经更新了 nofile ulimit、net.core.netdev_max_backlog、net.ipv4.tcp_max_syn_backlog 和 net.core.somaxconn,但在几秒钟内收到一连串 5k 新连接请求时它仍然挂起。

我可以做些什么来更快地建立新的连接?

编辑:

实际的服务器在 docker 容器中运行。我的网络设置没有应用于容器。接下来要尝试一下,看看它是否有所作为。

编辑编辑:

这一切都在 SSL 中。通过普通 HTTP 快速建立如此多的连接几乎是即时的。所以我必须弄清楚如何更快地建立 TLS 连接。

编辑编辑编辑:

我发现本机 java 安全 ssl 处理程序是瓶颈。切换到netty-tcnative(又名本机 OpenSSL)几乎解决了我的 HTTPS 问题。

mic*_*cah 5

谢谢@MichaelHampton 的帮助。

我为我的问题找到了解决方案,希望它可以帮助其他人(特别是如果您使用的是 Java)。

我听说过很多建议只是增加nofiles以允许更多连接,但我想首先重申问题不是服务器无法建立更多连接,而是它无法足够快地建立连接并且断开连接。

我解决这个问题的第一次尝试是通过net.ipv4.tcp_max_syn_backlognet.core.somaxconn并在适当的应用程序的服务器配置中增加连接队列。对于 vertx,这是server.setAcceptBacklog(...);. 这导致在队列中接受更多连接,但并没有加快建立连接的速度。从连接客户端的角度来看,它们不再由于溢出而重置连接,建立连接所需的时间更长。出于这个原因,增加连接队列并不是一个真正的解决方案,只是将一个问题换成了另一个问题。

为了缩小连接过程中瓶颈所在的范围,我尝试使用 HTTP 而不是 HTTPS 进行相同的基准测试,发现问题完全消失了。我的特殊问题是 TLS Handshake 本身和服务器满足它的能力。

通过深入研究我自己的应用程序,我发现用原生的 (OpenSSL) 替换 Java 的默认 SSLHandler 大大提高了通过 HTTPS 连接的速度。

以下是我为我的特定应用程序所做的更改(使用 Vertx 3.9.1)。

  1. 添加 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>
Run Code Online (Sandbox Code Playgroud)

第一个依赖项是让 osx 在运行时进行测试。第二个是编译时用于centos linux。linux-x86_64也可用于其他口味。我尝试使用,boringssl因为openssl不支持,ALPN但几个小时后我无法让它工作,所以我决定暂时不使用 http2。大多数连接在断开连接之前只发送 1-2 个小请求,这对我来说真的不是问题。如果您可以boringssl改用,那可能是首选。

  1. 因为我没有使用依赖项的 uber 版本。我需要为centos安装操作系统依赖项。这已添加到 Dockerfile
RUN yum -y install openssl
RUN yum -y install apr
Run Code Online (Sandbox Code Playgroud)
  1. 要告诉 vertx 服务器使用 OpenSSL 而不是 Java 版本,请在服务器上设置 OpenSSL 选项(即使只是默认对象)
httpServerOptions.setOpenSslEngineOptions(new OpenSSLEngineOptions());
Run Code Online (Sandbox Code Playgroud)
  1. 最后,在我的运行脚本中,我将io.netty.handler.ssl.openssl.useTasks=true选项添加到 Java。这告诉 ssl 处理程序在处理请求时使用任务,以便它是非阻塞的。
java -Dio.netty.handler.ssl.openssl.useTasks=true -jar /app/application.jar
Run Code Online (Sandbox Code Playgroud)

在这些更改之后,我能够以更少的开销更快地建立连接。之前需要数十秒并导致频繁连接重置的内容现在需要 1-2 秒而无需重置。可能会更好,但与我所处的位置相比有了很大的改进。