做者 | 卡拉先生java
爲何要優化 Ngin HTTPS 延遲
Nginx 常做爲最多見的服務器,常被用做負載均衡 (Load Balancer)、反向代理 (Reverse Proxy),以及網關 (Gateway) 等等。一個配置得當的 Nginx 服務器單機應該能夠指望承受住 50K 到 80K 左右每秒的請求,同時將 CPU 負載在可控範圍內。nginx
但在不少時候,負載並非須要首要優化的重點。好比對於卡拉搜索來講,咱們但願用戶在每次擊鍵的時候,能夠體驗即時搜索的感受,也就是說,每一個搜索請求必須在 100ms - 200ms 的時間內端對端地返回給用戶,才能讓用戶搜索時沒有「卡頓」和「加載」。所以,對於咱們來講,優化請求延遲纔是最重要的優化方向。web
這篇文章中,咱們先介紹 Nginx 中的 TLS 設置有哪些與請求延遲可能相關,如何調整才能最大化加速。而後咱們用優化Nginx 服務器的實例來分享如何調整 Nginx TLS/SSL 設置,爲首次搜索的用戶提速 30% 左右。咱們會詳細討論每一步咱們作了一些什麼優化,優化的動機和效果。但願能夠對其它遇到相似問題的同窗提供幫助。瀏覽器
TLS 握手和延遲
不少時候開發者會認爲:若是不是絕對在乎性能,那麼瞭解底層和更細節的優化沒有必要。這句話在不少時候是恰當的,由於不少時候複雜的底層邏輯必須包起來,才能讓更高層的應用開發複雜度可控。好比說,若是你就只須要開發一個 APP 或者網站,可能並無必要關注彙編細節,關注編譯器如何優化你的代碼——畢竟在蘋果或者安卓上不少優化在底層就作好了。緩存
那麼,瞭解底層的 TLS 和應用層的 Nginx 延遲優化有什麼關係呢?安全
答案是多數狀況下,優化網絡延遲實際上是在嘗試減小用戶和服務器之間的數據傳輸次數,也就是所謂的 roundtrip。因爲物理限制,北京到雲南的光速傳播差很少就是要跑 20 來毫秒,若是你不當心讓數據必須屢次往返於北京和雲南之間,那麼必然延遲就上去了。服務器
所以若是你須要優化請求延遲,那麼瞭解一點底層網絡的上下文則會大有裨益,不少時候甚至是你是否能夠輕鬆理解一個優化的關鍵。本文中咱們不深刻討論太多 TCP 或者 TLS 機制的細節,若是有興趣的話請參考 High Performance Browser Networking 一書,能夠免費閱讀。微信
舉個例子,下圖中展現了若是你的服務啓用了 HTTPS,在開始傳輸任何數據以前的數據傳輸狀況。網絡
![](http://static.javashuo.com/static/loading.gif)
能夠看到,在你的用戶拿到他須要的數據前,底層的數據包就已經在用戶和你的服務器之間跑了 3 個來回。session
假設每次來回須要 28 毫秒的話,用戶已經等了 224 毫秒以後纔開始接收數據。
同時這個 28 毫秒實際上是很是樂觀的假設,在國內電信、聯通和移動以及各類複雜的網絡情況下,用戶與服務器之間的延遲更不可控。另外一方面,一般一個網頁須要數十個請求,這些請求不必定能夠所有並行,所以幾十乘以 224 毫秒,頁面打開可能就是數秒以後了。
因此,原則上若是可能的話,咱們須要儘可能減小用戶和服務器之間的往返程 (roundtrip),在下文的設置中,對於每一個設置咱們會討論爲何這個設置有可能幫助減小往返程。
Nginx 中的 TLS 設置
那麼在 Nginx 設置中,怎樣調整參數會減小延遲呢?
開啓 HTTP/2
HTTP/2 標準是從 Google 的 SPDY 上進行的改進,比起 HTTP 1.1 提高了很多性能,尤爲是須要並行多個請求的時候能夠顯著減小延遲。在如今的網絡上,一個網頁平均須要請求幾十次,而在 HTTP 1.1 時代瀏覽器能作的就是多開幾個鏈接(一般是 6 個)進行並行請求,而 HTTP 2 中能夠在一個鏈接中進行並行請求。HTTP 2 原生支持多個並行請求,所以大大減小了順序執行的請求的往返程,能夠首要考慮開啓。
若是你想本身看一下 HTTP 1.1 和 HTTP 2.0 的速度差別,能夠試一下:https://www.httpvshttps.com/。個人網絡測試下來 HTTP/2 比 HTTP 1.1 快了 66%。
![](http://static.javashuo.com/static/loading.gif)
在 Nginx 中開啓 HTTP 2.0 很是簡單,只須要增長一個 http2 標誌便可
listen 443 ssl;
# 改成
listen 443 ssl http2;
若是你擔憂你的用戶用的是舊的客戶端,好比 Python 的 requests,暫時還不支持 HTTP 2 的話,那麼其實不用擔憂。若是用戶的客戶端不支持 HTTP 2,那麼鏈接會自動降級爲 HTTP 1.1,保持了後向兼容。所以,全部使用舊 Client 的用戶,仍然不受影響,而新的客戶端則能夠享受 HTTP/2 的新特性。
如何確認你的網站或者 API 開啓了 HTTP 2
在 Chrome 中打開開發者工具,點開 Protocol
以後在全部的請求中均可以看到請求用的協議了。若是 protocol
這列的值是 h2
的話,那麼用的就是 HTTP 2 了
![](http://static.javashuo.com/static/loading.gif)
固然另外一個辦法是直接用 curl
若是返回的 status 前有 HTTP/2
的話天然也就是 HTTP/2 開啓了。
➜ ~ curl --http2 -I https://kalasearch.cn
HTTP/2 403
server: Tengine
content-type: application/xml
content-length: 264
date: Tue, 22 Dec 2020 18:38:46 GMT
x-oss-request-id: 5FE23D363ADDB93430197043
x-oss-cdn-auth: success
x-oss-server-time: 0
x-alicdn-da-ups-status: endOs,0,403
via: cache13.l2et2[148,0], cache10.l2ot7[291,0], cache4.us13[360,0]
timing-allow-origin: *
eagleid: 2ff6169816086623266688093e
調整 Cipher 優先級
儘可能挑選更新更快的 Cipher,有助於減小延遲:
# 手動啓用 cipher 列表
ssl_prefer_server_ciphers on; # prefer a list of ciphers to prevent old and slow ciphers
ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
啓用 OCSP Stapling
在國內這多是對使用 Let's Encrypt 證書的服務或網站影響最大的延遲優化了。若是不啓用 OCSP Stapling 的話,在用戶鏈接你的服務器的時候,有時候須要去驗證證書。而由於一些不可知的緣由(這個就不說穿了)Let's Encrypt 的驗證服務器並非很是通暢,所以能夠形成有時候數秒甚至十幾秒延遲的問題,這個問題在 iOS 設備上特別嚴重
解決這個問題的方法有兩個:
-
不使用 Let's Encrypt,能夠嘗試替換爲阿里雲提供的免費 DV 證書 -
開啓 OCSP Stapling
開啓了 OCSP Stapling 的話,跑到證書驗證這一步能夠省略掉。省掉一個 roundtrip,特別是網絡情況不可控的 roundtrip,可能能夠將你的延遲大大減小。
在 Nginx 中啓用 OCSP Stapling 也很是簡單,只須要設置:
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /path/to/full_chain.pem;
如何檢測 OCSP Stapling 是否已經開啓?
能夠經過如下命令
openssl s_client -connect test.kalasearch.cn:443 -servername kalasearch.cn -status -tlsextdebug < /dev/null 2>&1 | grep -i "OCSP response"
來測試。若是結果爲
OCSP response:
OCSP Response Data:
OCSP Response Status: successful (0x0)
Response Type: Basic OCSP Response
則代表已經開啓。參考 HTTPS 在 iPhone 上慢的問題 一文
調整 ssl_buffer_size
sslbuffersize 控制在發送數據時的 buffer 大小,默認設置是 16k。這個值越小,則延遲越小,而添加的報頭之類會使 overhead 會變大,反之則延遲越大,overhead 越小。
所以若是你的服務是 REST API 或者網站的話,將這個值調小能夠減少延遲和 TTFB,但若是你的服務器是用來傳輸大文件的,那麼能夠維持 16k。關於這個值的討論和更通用的 TLS Record Size 的討論,能夠參考:Best value for nginx's sslbuffersize option
若是是網站或者 REST API,建議值爲 4k,可是這個值的最佳取值顯然會由於數據的不一樣而不同,所以請嘗試 2 - 16k 間不一樣的值。在 Nginx 中調整這個值也很是容易
ssl_buffer_size 4k;
啓用 SSL Session 緩存
啓用 SSL Session 緩存能夠大大減小 TLS 的反覆驗證,減小 TLS 握手的 roundtrip。雖然 session 緩存會佔用必定內存,可是用 1M 的內存就能夠緩存 4000 個鏈接,能夠說是很是很是划算的。同時,對於絕大多數網站和服務,要達到 4000 個同時鏈接自己就須要很是很是大的用戶基數,所以能夠放心開啓。
這裏 ssl_session_cache
設置爲使用 50M 內存,以及 4 小時的鏈接超時關閉時間 ssl_session_timeout
# Enable SSL cache to speed up for return visitors
ssl_session_cache shared:SSL:50m; # speed up first time. 1m ~= 4000 connections
ssl_session_timeout 4h;
卡拉搜索如何減小 30% 的請求延遲
卡拉搜索是國內的 Algolia,致力於幫助開發者快速搭建即時搜索功能(instant search),作國內最快最易用的搜索即服務。
開發者接入後,全部搜索請求經過卡拉 API 便可直接返回給終端用戶。爲了讓用戶有即時搜索的體驗,咱們須要在用戶每次擊鍵後極短的時間內(一般是 100ms 到 200ms)將結果返回給用戶。所以每次搜索須要能夠達到 50 毫秒之內的引擎處理時間和 200 毫秒之內的端對端時間。
咱們用豆瓣電影的數據作了一個電影搜索的 Demo,若是感興趣的話歡迎體驗一下即時搜索,嘗試一下搜索「無間道」或者「大話西遊」體驗一下速度和相關度:https://movies-demo.kalasearch.cn/
對於每一個請求只有 100 到 200 毫秒的延遲預算,咱們必須把每一步的延遲都考慮在內。
簡化一下,每一個搜索請求須要經歷的延遲有
![](http://static.javashuo.com/static/loading.gif)
總延遲 = 用戶請求到達服務器(T1) + 反代處理(Nginx T2) + 數據中心延遲(T3) + 服務器處理 (卡拉引擎 T4) + 用戶請求返回(T3+T1)
在上述延遲中,T1 只與用戶與服務器的物理距離相關,而 T3 很是小(參考Jeff Dean Number)能夠忽略不計。
因此咱們能控制的大體只有 T2 和 T4,即 Nginx 服務器的處理時間和卡拉的引擎處理時間。
Nginx 在這裏做爲反向代理,處理一些安全、流量控制和 TLS 的邏輯,而卡拉的引擎則是一個在 Lucene 基礎上的倒排引擎。
咱們首先考慮的第一個可能性是:延遲是否是來自卡拉引擎呢?
在下圖展現的 Grafana 儀表盤中,咱們看到除了幾個時不時的慢查詢,搜索的 95% 服務器處理延遲小於 20 毫秒。對比一樣的數據集上 benchmark 的 Elastic Search 引擎的 P95 搜索延遲則在 200 毫秒左右,因此排除了引擎速度慢的可能。
![](http://static.javashuo.com/static/loading.gif)
而在阿里雲監控中,咱們設置了從全國各地向卡拉服務器發送搜索請求。咱們終於發現 SSL 處理時間時常會超過 300 毫秒,也就是說在 T2 這一步,光處理 TLS 握手之類的事情,Nginx 已經用掉了咱們全部的請求時間預算。
同時檢查以後咱們發現,在蘋果設備上搜索速度格外慢,特別是第一次訪問的設備。所以咱們大體判斷應該是由於咱們使用的 Let's Encrypt 證書的問題。
咱們按照上文中的步驟對 Nginx 設置進行了調整,並將步驟總結出來寫了這篇文章。在調整了 Nginx TLS 的設置後,SSL 時間從平均的 140ms 下降到了 110ms 左右(全國全部省份聯通和移動測試點),同時蘋果設備上首次訪問慢的問題也消失了。
![](http://static.javashuo.com/static/loading.gif)
在調整事後,全國範圍內測試的搜索延遲下降到了 150 毫秒左右。
總結
調整 Nginx 中的 TLS 設置對於使用 HTTPS 的服務和網站延遲有很是大的影響。本文中總結了 Nginx 中與 TLS 相關的設置,詳細討論各個設置可能對延遲的影響,並給出了調整建議。以後咱們會繼續討論 HTTP/2 對比 HTTP 1.x 有哪些具體改進,以及在 REST API 使用 HTTP/2 有哪些優缺點,請繼續關注
本文分享自微信公衆號 - 肥朝(feichao_java)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。