如何處理新 HTTPS 連接中的突然爆發?
我在負載均衡器後面有一組 Java Vertx 伺服器來處理峰值流量。一分鐘它可能處理 150k r/m,下一分鐘它可能處理 2mm r/m,然後又回到 150k r/m。我發現在這些高峰期間,整個機隊可能會在幾分鐘內變得無響應並斷開連接,而任何一個機器上的 cpu 和 mem 壓力幾乎沒有達到 50% 的使用率。
為了測試導致中斷的確切原因,我設置了一個與我的生產機隊中的一個規格相匹配的測試伺服器,以查看在它放棄之前我可以投入多少。我的測試涉及使用 10 台其他機器,每台機器打開 500 個到伺服器的 https 連接,並發送 1mm 請求,每個請求有效負載約 2kb。這總共打開了 5k 個並發連接,總共發送了 10mm 的請求,用於大約 20gb 的數據傳輸。
一旦打開連接,我可以每分鐘發出大約 700k 個請求。我只需通過向健康端點發出請求並記錄響應時間來監控伺服器的可用性。響應時間很快,幾十毫秒。我對這些結果很滿意。
但在數據洪流開始之前,這 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 問題。
感謝@MichaelHampton 的幫助。
我找到了解決我的問題的方法,希望它可以幫助其他人(特別是如果您使用的是 Java)。
我聽過很多建議,只是增加
nofiles
以允許更多連接,但我想首先重申問題不在於伺服器無法建立更多連接,而是它無法足夠快地建立連接並且丟棄連接。我第一次嘗試解決這個問題是通過增加連接隊列
net.ipv4.tcp_max_syn_backlog
,net.core.somaxconn
並在適當的地方再次在應用程序的伺服器配置中。對於 vertx,這是server.setAcceptBacklog(...);
. 這導致在隊列中接受更多連接,但並沒有使建立連接更快。從連接客戶端的角度來看,它們不再因溢出而重置連接,建立連接只需要更長的時間。出於這個原因,增加連接隊列並不是一個真正的解決方案,只是將一個問題換成了另一個問題。為了縮小連接過程中瓶頸所在的位置,我嘗試了使用 HTTP 而不是 HTTPS 的相同基準測試,發現問題完全消失了。我的特殊問題是 TLS 握手本身和伺服器滿足它的能力。
隨著對我自己的應用程序的深入研究,我發現將 Java 的預設 SSLHandler 替換為原生的(OpenSSL)大大提高了通過 HTTPS 連接的速度。
以下是我為特定應用程序所做的更改(使用 Vertx 3.9.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>
第一個依賴項是 osx 在執行時進行測試。第二個是編譯時的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
- 要告訴 vertx 伺服器使用 OpenSSL 而不是 Java 版本,請在伺服器上設置 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 秒而無需重置。可能會更好,但與我的位置相比有了很大的改進。