解決策 1:
@MichaelHampton さん、ご協力ありがとうございます。
私の問題の解決策を見つけました。うまくいけば、それが他の人の助けになるかもしれません (特に Java を使用している場合)。
単純に nofiles
を増やすという提案をたくさん聞きました。 より多くの接続を許可しますが、問題はサーバーがより多くの接続を確立できないことではなく、十分な速度で接続を確立できず、接続をドロップすることであることを繰り返し述べたいと思います.
この問題を解決するための最初の試みは、接続キューを net.ipv4.tcp_max_syn_backlog
まで増やすことでした 、 net.core.somaxconn
必要に応じて、アプリケーションのサーバー構成でも同様です。頂点の場合、これは server.setAcceptBacklog(...);
です .これにより、キューでより多くの接続を受け入れることができましたが、接続の確立は速くなりませんでした。接続しているクライアントの観点からは、オーバーフローが原因で接続がリセットされなくなり、接続の確立に時間がかかりました。このため、接続キューを増やすことは実際の解決策ではなく、1 つの問題を別の問題と交換しただけでした。
接続プロセスのどこがボトルネックであるかを絞り込もうとして、HTTPS の代わりに HTTP で同じベンチマークを試したところ、問題は完全に解消されました。私の特定の問題は、TLS ハンドシェイク自体と、それを満たすサーバーの能力にありました。
自分のアプリケーションをさらに掘り下げた結果、Java のデフォルトの SSLHandler をネイティブのもの (OpenSSL) に置き換えると、HTTPS 経由の接続速度が大幅に向上することがわかりました。
以下は、特定のアプリケーションに対して行った変更です (Vertx 3.9.1 を使用)。
<オール><!-- 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>
最初の依存関係は、osx が実行時にテストすることです。 2 つ目は、コンパイル時の centos linux 用です。 linux-x86_64
他のフレーバーもご用意しています。 boringssl
を使ってみた なぜなら openssl
ALPN
をサポートしていません しかし、何時間も経っても機能しなくなったので、今のところ http2 なしで生活することにしました。ほとんどの接続では、切断する前に 1 ~ 2 個の小さなリクエストしか送信されないため、これはとにかく私にとっては問題ではありません。 boringssl
を使用できる場合 代わりに、おそらくそれが好まれます。
- 依存関係の uber バージョンを使用していないためです。 centos の OS 依存関係をインストールする必要がありました。これは Dockerfile に追加されました
RUN yum -y install openssl
RUN yum -y install apr
- Java バージョンの代わりに OpenSSL を使用するように vertx サーバーに指示するには、サーバーで OpenSSL オプションを設定します (たとえデフォルト オブジェクトであっても)
httpServerOptions.setOpenSslEngineOptions(new OpenSSLEngineOptions());
- 最後に、実行スクリプトに
io.netty.handler.ssl.openssl.useTasks=true
を追加しました Java のオプション。これにより、ssl ハンドラーはリクエストを処理するときにタスクを使用するように指示され、ブロックされなくなります。
java -Dio.netty.handler.ssl.openssl.useTasks=true -jar /app/application.jar
これらの変更の後、オーバーヘッドが少なくなり、接続をより迅速に確立できるようになりました。以前は数十秒かかっていた接続のリセットが頻繁に発生していましたが、現在はリセットなしで 1 ~ 2 秒かかります。もっと良くなるかもしれませんが、私がいた場所からは大幅に改善されました.
解決策 2:
良い修正です!.
したがって、それは SSL レイヤーのようです。ネットワーク ハンドシェイクや、リソースを必要とする暗号変換など、より多くの処理を行う必要があることは確かです。 SSL が処理の一部をハードウェアにオフロードできない限り、SSL は確実にサーバーの負荷を増加させる可能性があります。
これらの問題は、フロントエンド リバース プロキシの有力候補です。これは、アプリケーションの前に配置して、クライアントへのすべての SSL 接続を処理し、バックエンドに対して http を実行することが理想的です。
フロントエンドのリバース プロキシがすべての SSL 作業と tcp 接続管理を吸収できるため、元のアプリケーションで行うことは少し少なくなります。
Apache と NGNIX はこれを行うことができ、最も負荷の低いバックエンド サーバーへの接続を負荷分散するためのかなりの数のオプションを備えています。
NGNIX は Java よりもはるかに高速に SSL ターミネーションを実行できることがわかります。また、Java が実行できる場合でも、接続管理の処理を複数のマシンに分散することで、バックエンド サーバーの負荷 (メモリ/CPU/ディスク IO) を削減できます。バックエンドの構成を単純化するという副作用があります。
欠点は、プロキシとアプリケーションの間で http を使用することです。これは、一部の非常に安全な環境では望ましくありません。
幸運を祈ります!