【編者按】本文作者為 Owen Garrett,主要介紹使用 nginx 進行微程序緩存的好處,輔之以生動的實例。文章系國內 ITOM 管理平臺 OneAPM 編譯呈現。
NGINX 和 NGINX Plus 被廣泛應用于網站內容緩存,小到個人網站,大到一些世界大型內容分發(fā)網站(CDNs),例如 MaxCDN 和 CloudFlare。
微程序緩存通過將動態(tài)、非個人化的內容緩存很短的時間,能有效加速這些內容的傳遞。在本文中,筆者將展示如何利用微程序緩存技術將一個基于 WordPress 的應用程序最高提速400倍。
為什么要緩存內容?
緩存能夠一舉兩得:通過更快地傳遞內容,緩存可以改善網站性能,同時減輕源服務器的負擔。緩存的效率取決于內容的緩存度。這些內容可以存儲多長時間,如何檢查更新,相同的緩存內容可以發(fā)給多少用戶?

緩存靜態(tài)內容,例如圖片、JavaScript 和 CSS 文件和幾乎不變的網頁內容是個相當簡單的過程。緩存更新的處理方法包括常規(guī)暫停、條件 Get,如果有必要,還可以用cache-busting技術來替換引用對象的URL。
緩存個人化內容(即通過服務器應用為每位用戶定制的內容)幾乎不可能,因為服務器對同一資源的每次請求的回復都不相同。服務器端引用(SSI)和頁面片段緩存(ESI)等技術可以協(xié)助組合網頁,但是這些技術很難實行,而且不一定能改善性能。
兩者中間是個有趣的待緩存對象:可能會無計劃更換,但是并非針對每位用戶(或者在客戶端通過 JavaScript實現個性化)的動態(tài)內容。這類內容的生成代價很高,提供過時版本又會帶來新的問題。
適合緩存的動態(tài)內容包括:
- 經常更新的新聞或博客網站的首頁,每隔幾秒就有新文章發(fā)布
- 最近資訊 RSS
- 持續(xù)整合(CI)或搭建平臺的進度頁面
- 庫存、進度或籌款計數
- 彩票開獎結果
- 日歷數據
- 在客戶端呈現的個人化動態(tài)內容,例如利用 cookie 數據展示的廣告內容或數據(“你好,你的名字”)
動態(tài)內容的微程序緩存
微程序緩存是一種緩存技術,將內容緩存1秒左右很短的時間。這意味著網站更新會延遲不到1秒鐘,這在很多情況下是可以接受的。
這種短暫緩存能給網站性能帶來可察覺的改觀嗎?來試試看!
測試應用程序
在本次測試中,筆者用的是標準 WordPress 設置,并填充了一些樣本內容。

顯然,即便是處理基本內容,WordPress 服務器也存在性能問題:以 ab 為基準時,它一秒鐘只能服務5.53個請求:
root@nginx-client:~## ab -c 10 -t 30 -k http://nginx-server/
Requests per second: 5.53 [#/sec] (mean)
Time per request: 1809.260 [ms] (mean)
Time per request: 180.926 [ms] (mean, across all concurrent requests)
Transfer rate: 319.74 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.3 0 3
Processing: 1430 1735 259.4 1580 2228
Waiting: 537 683 119.7 624 980
Total: 1430 1735 259.4 1580 2228
測試中,vmstat 顯示造成瓶頸的原因是利用 PHP 生成頁面的 CPU 消耗(在 cpu 范圍的 us 一列,數值為96到98。)
root@nginx-server:/var/www/html## vmstat 3
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
10 0 0 136076 44944 585920 0 0 0 0 476 1665 96 4 0 0 0
10 0 0 140112 44952 585924 0 0 0 4 506 1773 98 2 0 0 0
10 0 0 136208 44952 585924 0 0 0 0 576 2057 97 3 0 0 0
熱門使用量顯示,CPU 被10個執(zhí)行 PHP 解釋器的 Apache httpd 進程占用。
這種設置本身就是問題——它限制了網站每秒鐘處理請求的數量不能超過5個,很容易遭到 DOS攻擊,而通過添加 CPU 來解決這個問題意味著每年的托管費用都要增加1000美元。
利用 NGINX 簡化微程序緩存
利用 NGINX 來加速服務只需兩步。
第一步: 通過 NGINX 代理服務器
在 WordPress 服務器安裝 NGINX 或 NGINX Plus 并進行配置,讓它接收訪問流量并在內部轉發(fā)到 WordPress 服務器:

NGINX 代理服務器配置比較簡單:
server {
listen external-ip:80; # External IP address
location / {
proxy_http_version 1.1; # Always upgrade to HTTP/1.1
proxy_set_header Connection ""; # Enable keepalives
proxy_set_header Accept-Encoding ""; # Optimize encoding
proxy_pass http://wordpress-upstreams;
}
status_zone wordpress; # NGINX Plus status monitoring
}
upstream wordpress-upstreams {
zone wordpress 128k;
keepalive 20; # Keepalive pool to upstream
server localhost:80;
}
筆者還修改了 Apache 配置(監(jiān)聽端口號和虛擬服務器),這樣 Apache 就綁定到了 localhost:80。
你可能以為添加額外的代理服務器會對性能造成負面影響,但是實際上性能變化可以忽略不計:
root@nginx-client:~# ab -c 10 -t 30 -k http://nginx-server/
Requests per second: 5.63 [#/sec] (mean)
Time per request: 1774.708 [ms] (mean)
Time per request: 177.471 [ms] (mean, across all concurrent requests)
Transfer rate: 324.44 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.2 0 1
Processing: 1423 1709 341.3 1532 2794
Waiting: 554 703 165.0 608 1165
Total: 1423 1709 341.4 1532 2794
在更繁忙的服務器(處理更多并發(fā)請求)中,僅靠 NGINX 實現的優(yōu)化就能帶來顯著的性能提升。
第二步: 啟動短期緩存
在服務器配置中只添加了兩條指令,NGINX 或 NGINX Plus 就可以緩存所有可緩存的響應。帶有 200 OK 狀態(tài)碼的響應只緩存1秒鐘。
proxy_cache_path /tmp/cache keys_zone=cache:10m levels=1:2 inactive=600s max_size=100m;
server {
proxy_cache cache;
proxy_cache_valid 200 1s;
...
}
筆者再次運行基準測試時,看到了性能顯著提升:
root@nginx-client:~# ab -c 10 -t 30 -k http://nginx-server/
Complete requests: 18022
Requests per second: 600.73 [#/sec] (mean)
Time per request: 16.646 [ms] (mean)
Time per request: 1.665 [ms] (mean, across all concurrent requests)
Transfer rate: 33374.96 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 0.5 1 10
Processing: 0 16 141.5 3 2119
Waiting: 0 6 54.6 1 818
Total: 1 17 141.5 4 2121
這是120倍的性能優(yōu)化,從每秒鐘處理5條請求到600條;這聽起來太棒了,不過還有個問題。
緩存進展順利,筆者驗證了內容的確是每秒更新的(因此永不過時),但是未曾預料到的情況發(fā)生了。你會發(fā)現處理時間的標準偏差很大(141.5毫秒)。CPU 使用率還是100%(用 vmstat 測量),熱門使用量顯示有10個活躍的 httpd 進程。
筆者還從 NGINX Plus 的活動檢測控制面板找到進一步的線索。測試前:

測試后:

控制面板報告顯示,NGINX 在測試期間處理了18032條請求(ab 匯報的18022條請求,以及基準在30秒結束時突出的10條請求)。但是,NGINX 轉發(fā)了150條請求到上游服務器,在緩存內容1秒鐘的情況下,這比我們期望的30秒測試應有的請求數多得多。
怎么回事?為什么 CPU 使用率很高,緩存更新比預期數字更大?
這是因為每次緩存條目過期時,NGINX 就會停止使用它。NGINX 將所有請求都轉發(fā)給上游 WordPress 服務器,直到它收到響應,可以用新內容來緩存。
這導致了 WordPress 服務器收到的請求經常激增到10條。這些請求會占用 CPU,比緩存響應的請求延遲更多,這就解釋了測試結果中的高標準差。
用 NGINX 優(yōu)化微程序緩存
筆者想要的策略很清晰:需要在確保緩存內容最新的情況下,盡可能少地向上游源服務器轉發(fā)請求。在緩存內容不斷更新的前提下,筆者愿意從緩存獲取舊的(延后1到2秒)響應。要實現這一目標,需要添加兩條指令:
- proxy_cache_lock ——限制填充緩存的并發(fā)嘗試數量,這樣當一條緩存入口被創(chuàng)建后,對該資源的請求將會在 NGINX 中排隊。
- proxy_cache_use_stale ——配置 NGINX,使它提供舊的(最近緩存的)內容,同時更新緩存入口。
加上之前已經添加的緩存指令,筆者得到如下服務器配置:
server {
proxy_cache one;
proxy_cache_lock on;
proxy_cache_valid 200 1s;
proxy_cache_use_stale updating;
...
}
基準測試結果的變化十分驚人。每秒鐘的請求數量從600跳躍到接近2200:
root@nginx-client:~# ab -c 10 -t 30 -n 100000 -k http://nginx-server/
Concurrency Level: 10
Time taken for tests: 30.001 seconds
Complete requests: 65553
Failed requests: 0
Keep-Alive requests: 0
Total transferred: 3728905623 bytes
HTML transferred: 3712974057 bytes
Requests per second: 2185.03 [#/sec] (mean)
Time per request: 4.577 [ms] (mean)
Time per request: 0.458 [ms] (mean, across all concurrent requests)
Transfer rate: 121379.72 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 0.3 1 5
Processing: 1 4 8.1 3 661
Waiting: 0 1 2.6 1 250
Total: 1 5 8.1 4 661
CPU 使用率也低多了(注意 cpu 下面 id 一欄的空閑時間):
root@nginx-server:/var/www/html# vmstat 3
procs -----------memory---------- ---swap-- -----io---- -system--- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 106512 53192 641116 0 0 0 37 11016 3727 19 45 36 0 0
1 0 0 105832 53192 641808 0 0 0 68 17116 3521 13 56 31 0 0
1 0 0 104624 53192 643132 0 0 0 64 14120 4487 15 51 33 0 0
數據傳輸率(121379.72千字節(jié)/秒,或121兆字節(jié)每秒)相當于0.97千兆,因此該測試受網絡限制。CPU 平均使用率為66%,該服務器的峰值性能應該大概為2185/0.66 = 3300 個請求/秒。

另外,關注 ab 報告的連續(xù)響應時間(標準偏差只有8.1毫秒),以及操作面板顯示的30秒測試中轉發(fā)給上游服務器的請求數量很少(16):

為什么只有16條請求?我們知道緩存到1秒鐘時會清零,這個更新過程最多需要0.661秒(從 ab 結果來看),因此可以推測,更新頻率不會快于每1.66秒一次。在30秒鐘的時間之外,只會收到最多18(30/1.66)條請求。
了解更多
本文簡單展示了在短時間內緩存動態(tài)內容可能帶來的好處,以及 NGINX Plus 的活動監(jiān)測數據在調整和診斷緩存配置時的用處。如果你想在生產環(huán)境中使用微程序緩存,筆者建議你創(chuàng)建并測試一個更為復雜的緩存規(guī)則,針對更長時間內的微程序緩存動態(tài)和靜態(tài)內容。
NGINX Plus 還有一項緩存清除功能,可以用來迅速清除 NGINX 緩存中的特定內容。如果你想緩存更長時間的內容,可以將該功能編入程序,但是一旦你更改原始內容,就要立即更新該程序。
要想了解更多信息,請查閱以下資源:
- 利用 NGINX 緩存指南——概述和方法介紹
- 內容緩存——NGINX Plus 向導
- 利用 NGINX Plus 進行內容緩存— NGINX Plus 功能描述
本文系 OneAPM 工程師編譯呈現。OneAPM Browser Insight 是一個基于真實用戶的 Web 前端性能監(jiān)控平臺,能夠幫大家定位網站性能瓶頸,網站加速效果可視化;支持瀏覽器、微信、App 瀏覽 HTML 和 HTML5 頁面。想閱讀更多技術文章,請訪問 OneAPM 官方技術博客。
本文轉自 OneAPM 官方博客
原文地址: https://www.nginx.com/blog/benefits-of-microcaching-nginx/