簡單一招竟把nginx服務器性能提升50倍

2024年2月6日 21点热度 0人点赞

需求背景

接到重點業務需求要分輪次展示數據,預估最高承接 9w 的 QPS,作為後端工程師下意識的就是把接口寫好,分級緩存、機器擴容、線程拉滿等等一系列連招準備,再因為數據更新頻次兩隻手都數得過來,我們采取了最穩妥的處理方式,直接生成靜態文件拿 CDN 抗量

架構流程大致如下所示:

數據更新後會重新生成新一輪次的文件,刷新 CDN 的時候會觸發大量回源請求,應用服務器極端情況得 hold 住這 9w 的 QPS

第一次壓測

雙機房一共 40 臺 4C 的機器,25KB 數據文件,5w 的 QPS 直接把 CPU 打到 90%

這明顯不符合業務需求啊,咋辦?先無腦加機器試試唄

就在這時測試同學反饋壓測的數據不對,最後一輪文件最大會有 125KB,雪上加霜

於是乎文件替換,機器數量整體翻一倍擴到 80 臺,服務端 CPU 依然是瓶頸,QPS 加不上去了

到底是哪裡在消耗 CPU 資源呢,整體架構已經簡單到不能再簡單了

這時候我們註意到為了節省網絡帶寬 nginx 開啟了 gzip 壓縮,是不是這小子搞的鬼

server
{
      listen 80;
      gzip on;
      gzip_disable "msie6";
      gzip_vary on;
      gzip_proxied any;
      gzip_comp_level 6;
      gzip_buffers 16 8k;
      gzip_http_version 1.1;
      gzip_types text/plain application/css text/css application/xml text/javascript application/javascript application/x-javascript;
......
}

第二次壓測

為了驗證這個猜想,我們把 nginx 中的 gzip 壓縮率從 6 調成 2,以減少 CPU 的計算量

gzip_comp_level 2;

這輪壓下來 CPU 還是很快被打滿,但 QPS 勉強能達到 9w,坐實了確實是 gzip 在耗 CPU

nginx 作為傢喻戶曉的 web 服務器,以高性能高並發著稱,區區一個靜態數據文件就把應用服務器壓的這麼高,一定是哪裡不對

第三次壓測

明確了 gzip 在耗 CPU 之後我們潛下心來查閱了相關資料,發現了一絲進展

html/css/js 等靜態文件通常包含大量空格、標簽等重復字符,重復出現的部分使用「距離加長度」表達可以減少字符數,進而大幅降低帶寬,這就是 gzip 無損壓縮的基本原理

作為一種端到端的壓縮技術,gzip 約定文件在服務端壓縮完成,傳輸中保持不變,直到抵達客戶端。這不妥妥的理論依據嘛~

nginx 中的 gzip 壓縮分為動態壓縮和靜態壓縮兩種

•動態壓縮

服務器給客戶端返回響應時,消耗自身的資源進行實時壓縮,保證客戶端拿到 gzip 格式的文件

這個模塊是默認編譯的,詳情可以查看
https://nginx.org/en/docs/http/ngx_http_gzip_module.html


•靜態壓縮

直接將預先壓縮過的 .gz 文件返回給客戶端,不再實時壓縮文件,如果找不到 .gz 文件,會使用對應的原始文件

這個模塊需要單獨編譯,詳情可以查看
https://nginx.org/en/docs/http/ngx_http_gzip_static_module.html

如果開啟了 gzip_static always,而且客戶端不支持 gzip,還可以在服務端加裝 gunzip 來幫助客戶端解壓,這裡我們就不需要了

查了一下 jdos 自帶的 nginx 已經編譯了
ngx_http_gzip_static_module,省去了重新編譯的麻煩事

接下來通過 GZIPOutputStream 在本地額外生成一個 .gz 的文件,nginx 配置上靜態壓縮再來一次

gzip_static on;

面對 9w 的QPS,40 臺機器隻用了 7% 的 CPU 使用率完美扛下

為了探底繼續加壓,應用服務器 CPU 增長緩慢,直到網絡流出速率被拉到了 89MB/s,擔心影響宿主機其他容器停止壓力,此時 QPS 已經來到 27w

qps 5w->27w 提升 5 倍,CPU 90%->7% 降低 10 倍,整體性能翻了 50 倍不止,這回舒服了~

寫在最後

經過一連串的分析實踐,似乎靜態壓縮存在“壓倒性”優勢,那什麼場景適合動態壓縮,什麼場景適合靜態壓縮呢?一番探討後得出以下結論

純靜態不會變化的文件適合靜態壓縮,提前使用gzip壓縮好避免CPU和帶寬的浪費。動態壓縮適合API接口返回給前端數據這種動態的場景,數據會發生變化,這時候就需要nginx根據返回內容動態壓縮,以節省服務器帶寬

作為一名後端工程師,nginx 是我們的老相識了,抬頭不見低頭見。日常工作中配一配轉發規則,查一查 header 設置,基本都是把 nginx 作為反向代理使用。這次是直接訪問靜態資源,調整過程的一系列優化加深了我們對 gzip 的動態壓縮和靜態壓縮的基本認識,這在 NG 老炮兒眼裡顯得微不足道,但對於我們來說卻是一次難得的技能拓展機會

在之前的職業生涯裡,我們一直聚焦於業務架構設計與開發,對性能的優化似乎已經形成思維慣性。面對大數據量長事務請求,減少循環變批量,增大並發,增加緩存,實在不行走異步任務解決,一般瓶頸都出現在 I/O 層面,畢竟磁盤慢嘛,減少與數據庫的交互次數往往就有效果,其他大概率不是問題。這回有點兒不一樣,CPU 被打起來的原因就是出現了大量數據計算,在高並發請求前,任何一個環節都可能產生性能問題

作者:京東零售 閆創

來源:京東雲開發者社區 轉載請註明來源