化復(fù)盤:從 2.8 秒到 0.9 秒)
Wiki.js 首屏優(yōu)化復(fù)盤從 2.8 秒到 0.9 秒【免費(fèi)下載鏈接】wiki-Wiki.js | Next Generation Open Source Wiki項(xiàng)目地址: https://gitcode.com/GitHub_Trending/wiki78/wiki-上個(gè)月我們對生產(chǎn)環(huán)境一套 Wiki.js 知識(shí)庫做了輪首屏優(yōu)化五十多人共用一個(gè) Node 實(shí)例首屏平均 2.8 秒。沒加機(jī)器、沒改架構(gòu)只動(dòng)了反代、進(jìn)程、構(gòu)建三層最終壓到 0.9 秒。以下是完整復(fù)盤。第一反應(yīng)是加內(nèi)存、升帶寬錢花完體感沒變。打開瀏覽器 Network 面板一看真相很直接。先看請求到底卡在哪冷加載 JS/CSS 合計(jì) 3.1 MB其中主包app.js一家 1.2 MB而且/_assets/js/app.js每次訪問都回 200響應(yīng)里沒有任何 Cache-Control瀏覽器每次全量重下。服務(wù)端日志顯示一次匿名首頁請求耗時(shí) 860 ms其中渲染 job 占約 500 ms整條管線在 server/jobs/render-page.js每個(gè)訪問者都重新跑一遍。打開 SQL 日志數(shù)了一遍一次首頁請求打出 63 條 SQL其中 41 條是同一條 SELECT 逐頁重復(fù)——教科書級(jí) N1。連接池保持默認(rèn)值并發(fā)峰值 15 時(shí)接口 p95 從 300 ms 爬到 900 ms。結(jié)論機(jī)器沒問題該緩存的沒緩存、該壓縮的沒壓縮、該按需加載的還在全量加載。反代層先能薅的這一層純 Nginx 配置不改 Wiki.js 一行源碼投入產(chǎn)出比最高。給資源目錄開 Gzip 和一年緩存先看構(gòu)建產(chǎn)物dev/webpack/webpack.prod.js 里filename: js/[name].js?${now}URL 帶了版本號(hào)查詢串每次重新打包 URL 就變所以敢放心給長緩存gzip on; gzip_comp_level 5; gzip_types text/css application/javascript application/json image/svgxml; location /_assets/ { expires 1y; add_header Cache-Control public, immutable; }只壓文本類圖片不進(jìn) gzip 列表——那是后面踩過的坑。改完靜態(tài)資源體積降約七成老用戶二次訪問資源請求基本歸零。給匿名 GET 請求加 5 分鐘頁面緩存靜態(tài)資源全命中之后頁面 HTML 每次仍要走一遍渲染管線Markdown 渲染、cheerio 解析目錄樹開銷不小。我們七成流量是匿名讀者這部分直接在反代層截住proxy_cache_path /var/cache/nginx/wiki levels1:2 keys_zonewiki:10m max_size1g inactive60m; location / { proxy_cache wiki; proxy_cache_key $scheme$request_method$host$request_uri; proxy_cache_bypass $http_cookie; proxy_cache_valid 200 5m; }proxy_cache_bypass $http_cookie表示帶 Cookie 就不寫緩存效果上只有匿名請求進(jìn)緩存。千萬別把登錄態(tài)頁面也緩存登錄用戶頁面內(nèi)容隨權(quán)限變化緩存串了就是安全事故。這一步收益最直接首屏平均從 2.8 秒降到 0.9 秒左右。Node 進(jìn)程內(nèi)部的三個(gè)漏點(diǎn)給 NodeCache 補(bǔ)上保質(zhì)期Wiki.js 內(nèi)置內(nèi)存緩存在 server/core/cache.js現(xiàn)狀就一行module.exports { init() { return new NodeCache() } }不傳任何參數(shù)等于 key 永不過期、內(nèi)存只進(jìn)不出像一臺(tái)只往里塞、從不開門的冰箱。長跑一段時(shí)間后緩存堆滿沒人看的歷史頁面。補(bǔ)上默認(rèn)過期時(shí)間和定期清理return new NodeCache({ stdTTL: 600, checkperiod: 120 })stdTTL讓緩存鍵 10 分鐘后自動(dòng)失效checkperiod讓它每 120 秒打掃一次。重啟即生效。改完內(nèi)存不再隨運(yùn)行時(shí)長線性上漲內(nèi)容更新后最多 10 分鐘可見新版。給連接池設(shè)明確上下限config.sample.yml 里pool段整段被注釋掉Knex 用保守默認(rèn)值。連接池像餐館座位太少高峰期全在門口排隊(duì)太多白占內(nèi)存和數(shù)據(jù)庫連接。按實(shí)例規(guī)模顯式給值pool: min: 2 max: 8改完重啟高峰接口等待肉眼可見地降了p95 從 900 ms 回到 310 ms。這是投入最小、見效最快的動(dòng)作之一。把 SQL 日志開一天揪出 N1猜沒用看證據(jù)。開關(guān)在 server/core/config.js它把flags.sqllog直接映射到 Knex 的 debugWIKI.models.knex.client.config.debug WIKI.config.flags.sqllog在config.yml的flags下寫sqllog: true服務(wù)端就會(huì)打印每一條 SQL配合數(shù)據(jù)庫的EXPLAIN過一遍目錄樹那 41 條重復(fù) SELECT 自己會(huì)跳出來。補(bǔ)完索引、改掉查詢后關(guān)回去——這個(gè)日志很吵生產(chǎn)環(huán)境別常開。構(gòu)建產(chǎn)物把主包切開把 vendor 拆成獨(dú)立 chunkwebpack.prod.js 現(xiàn)有 splitChunks 參數(shù)偏保守只有name: vendor、minChunks: 2。把第三方庫顯式歸入獨(dú)立 chunk主包只留應(yīng)用自身代碼splitChunks: { chunks: all, cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, name: vendor, priority: 10 } } }重新打包后app.js從 1.2 MB 降到約 730 KBvendor 包幾乎不變長緩存命中率極高應(yīng)用代碼更新時(shí)只下一小段 diff。順帶審計(jì)了重型組件編輯器在 client/client-app.js 里已經(jīng)是Vue.component(Editor, () import(/* webpackChunkName: editor */ ./components/editor.vue))懶加載各編輯器子頁也是webpackMode: lazy。這個(gè)寫法保留住重構(gòu)時(shí)別退回靜態(tài) require。收緊時(shí)區(qū)數(shù)據(jù)窗口MomentTimezoneDataPlugin會(huì)打進(jìn)全量時(shí)區(qū)數(shù)據(jù)默認(rèn)窗口是 2017 到當(dāng)前年份加 5。用戶集中在哪幾個(gè)時(shí)區(qū)就把窗口收窄甚至裁掉用不到的時(shí)區(qū)表體積立省對首屏是實(shí)打?qū)嵉臏p負(fù)。別讓 CI 清掉構(gòu)建緩存webpack.prod.js 里cache-loader和 Babel 的cacheDirectory都已配好緩存在.webpack-cache/下。只要 CI 構(gòu)建前不清掉它增量構(gòu)建從 4 分 40 秒降到 2 分 15 秒。 我們回滾過的優(yōu)化全量開 Gzip把圖片也壓一遍CPU 飆升、收益為零只留文本類。TTL 設(shè)太長頁面緩存放到 1 小時(shí)內(nèi)容更新后半個(gè)辦公室盯著舊版回滾到 5 分鐘。過度拆包c(diǎn)hunk 拆出 30 多塊HTTP/1.1 下請求數(shù)爆炸反而更慢。緩存登錄態(tài)頁面發(fā)現(xiàn)權(quán)限串了當(dāng)天下線這條是紅線再說一遍。無腦復(fù)制實(shí)例Docker 里一個(gè)實(shí)例復(fù)制成四個(gè)共享同一數(shù)據(jù)庫各實(shí)例內(nèi)存緩存各管各的改完內(nèi)容一會(huì)兒不一致config.sample.yml 里的ha標(biāo)志就是為此存在。先看慢查詢和渲染耗時(shí)再談擴(kuò)容。? 數(shù)據(jù)與三件事行動(dòng)清單優(yōu)化項(xiàng)實(shí)測收益實(shí)施成本反代 Gzip 一年緩存資源總量 -70%重復(fù)訪問零下載低10 分鐘匿名頁面緩存5 分鐘首屏平均 2.8 s → 0.9 s中改反代配置NodeCache TTL 連接池 2/8高峰接口 p95 900 ms → 310 ms低改配置重啟splitChunks 時(shí)區(qū)數(shù)據(jù)收窄app.js 1.2 MB → 730 KB中需重新打包構(gòu)建增量緩存構(gòu)建 4 分 40 秒 → 2 分 15 秒低改 CI如果只保留三件事按這個(gè)順序改 Nginx 配置 → 靜態(tài)資源加 Gzip 和/_assets/一年緩存 → 立即生效約 10 分鐘server/core/cache.js → 給new NodeCache()補(bǔ)stdTTL: 600, checkperiod: 120→ 重啟生效5 分鐘config.yml→ 加pool: { min: 2, max: 8 }并開flags.sqllog跑一天把 N1 和慢查詢揪出來再關(guān)掉 → 重啟生效觀察 1 天。性能優(yōu)化是一場小步快跑先拿配置層的收益再深入進(jìn)程和構(gòu)建用數(shù)據(jù)說話、用日志定位最后守住安全與一致性的底線。【免費(fèi)下載鏈接】wiki-Wiki.js | Next Generation Open Source Wiki項(xiàng)目地址: https://gitcode.com/GitHub_Trending/wiki78/wiki-創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考