化實(shí)戰(zhàn)指南:2.8s 干到 0.9s)
Wiki.js 首屏優(yōu)化實(shí)戰(zhàn)指南2.8s 干到 0.9s【免費(fèi)下載鏈接】wiki-Wiki.js | A modern and powerful wiki app built on Node.js項(xiàng)目地址: https://gitcode.com/GitHub_Trending/wiki78/wiki-Wiki.js 升級(jí)之后首頁(yè)首屏從 1.5 秒拖到了 2.8 秒截圖吐槽塞滿了群聊。一周性能優(yōu)化下來(lái)我們把平均首屏壓回了 0.9 秒編輯保存后的轉(zhuǎn)圈也基本消失。摸清時(shí)間都花在哪了動(dòng)手之前別猜先收集證據(jù)。我們翻 Network 面板、服務(wù)端日志和數(shù)據(jù)庫(kù)監(jiān)控拿到三條結(jié)論Network 面板里JS 和 CSS 動(dòng)輒幾 MB每次打開(kāi)頁(yè)面都像第一次一樣重新下載服務(wù)端日志顯示每個(gè)匿名請(qǐng)求都在完整跑一遍渲染管線Markdown 從頭渲染、目錄重新解析數(shù)據(jù)庫(kù)連接數(shù)長(zhǎng)期貼著低位高峰期請(qǐng)求開(kāi)始排隊(duì)而配置里連接池默認(rèn)值整段是被注釋掉的。機(jī)器沒(méi)毛病卡住的全是重復(fù)勞動(dòng)。下面按動(dòng)配置 → 動(dòng)緩存/反代 → 動(dòng)構(gòu)建/進(jìn)程的順序把改過(guò)的東西交代清楚。動(dòng)配置讓瀏覽器和數(shù)據(jù)庫(kù)各干各的活把連接池?cái)Q到 min 2、max 8配置文件里pool段的 min/max 都被注釋掉了底下的 Knex 只能退回一套很保守的默認(rèn)值。連接池就像高速路口的收費(fèi)站車(chē)道車(chē)道開(kāi)少了高峰全堵在入口處開(kāi)多了又白占資源。我們按實(shí)例規(guī)模把它擰開(kāi)pool: min: 2 max: 8改完重啟服務(wù)高峰時(shí)段接口的排隊(duì)肉眼可見(jiàn)地少了這是整輪優(yōu)化里投入產(chǎn)出比最高的一處。給 /_assets/ 上一年的 immutable 緩存前端產(chǎn)物的文件名都拼了時(shí)間戳生產(chǎn) webpack 配置里output.filename的${now}等于文件名變了才是新版。這正是長(zhǎng)緩存的前提。反代上只加兩件事文本類(lèi)資源走 Gzip/_assets/前綴給一年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; }改完 reload Nginx靜態(tài)資源體積縮了約七成老用戶再訪問(wèn)時(shí)資源請(qǐng)求基本歸零。動(dòng)緩存與反代重復(fù)渲染變一次計(jì)算給 NodeCache 擰上 10 分鐘鬧鐘內(nèi)存緩存入口只有一行無(wú)參的new NodeCache()鍵永不過(guò)期——服務(wù)一長(zhǎng)跑進(jìn)程里就攢滿了早沒(méi)人看的舊頁(yè)面 HTML內(nèi)存只進(jìn)不出。給它擰上默認(rèn)過(guò)期時(shí)間和定期清理module.exports { init() { return new NodeCache({ stdTTL: 600, checkperiod: 120 }) } }改完重啟鍵 10 分鐘后自動(dòng)失效NodeCache 每 2 分鐘清一次過(guò)期項(xiàng)。再講究點(diǎn)可以分層字典類(lèi)配置給長(zhǎng) TTL熱點(diǎn)頁(yè)面給短 TTL免得內(nèi)容改了用戶還看舊版。讓 Nginx 直接吐出匿名頁(yè)面 HTML內(nèi)存緩存就算全部命中頁(yè)面 HTML 仍要進(jìn)服務(wù)端渲染一次render-page.js里那一長(zhǎng)串 pipeline 加 cheerio 解析的開(kāi)銷(xiāo)不小。匿名讀者占大頭的知識(shí)庫(kù)這部分基本是白算的。我們的做法是在反代加一層頁(yè)面緩存只放進(jìn)匿名 GET命中就直接吐 HTMLproxy_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_valid 200 5m; }改完上線后匿名讀者的首屏掉到了 1 秒以內(nèi)。但有一條紅線必須單獨(dú)立起來(lái)登錄用戶的頁(yè)面絕對(duì)不允許進(jìn)這層緩存——登錄態(tài)內(nèi)容隨權(quán)限不同而變化一旦串掉就是越權(quán)事故。只對(duì)匿名請(qǐng)求啟用或用Vary頭區(qū)分響應(yīng)并讓緩存鍵攜帶鑒權(quán)信息。打開(kāi) sqllog 讓慢查詢現(xiàn)原形猜是猜不出 N1 的。把 core/config.js 預(yù)留的開(kāi)關(guān)打開(kāi)默認(rèn)值false見(jiàn)server/app/data.ymlflags: sqllog: true改完服務(wù)端會(huì)把每條 SQL 都打出來(lái)配合數(shù)據(jù)庫(kù)的 EXPLAIN 過(guò)一遍N1 和缺索引的語(yǔ)句會(huì)自己跳出來(lái)。這份日志非常吵定位完記得關(guān)回去。動(dòng)構(gòu)建與進(jìn)程主包瘦身、重活分家把第三方庫(kù)單獨(dú)打成 vendor chunkwebpack.prod.js里原有的splitChunks只有name: vendor, minChunks: 2偏保守。我們把 node_modules 顯式歸進(jìn)獨(dú)立 chunk并打開(kāi)chunks: all讓主包只留自家代碼optimization: { splitChunks: { chunks: all, cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, name: vendor, priority: 10 } } } }改完重新構(gòu)建vendor 塊幾乎不再變動(dòng)瀏覽器長(zhǎng)緩存命中率穩(wěn)定應(yīng)用代碼更新時(shí)只需重下小塊 diff主包體積縮了約四成。讓編輯器點(diǎn)擊時(shí)才加載編輯器是重型組件絕大多數(shù)用戶打開(kāi)頁(yè)面根本用不上不該占首屏const Editor () import(/* webpackChunkName: editor */ ./components/editor.vue)改完之后編輯器 chunk 只在按下編輯按鈕那一刻才下載首屏體積又砍掉一截。還有兩個(gè)細(xì)節(jié)其一cache-loader與 babel 的cacheDirectory已經(jīng)配好緩存在.webpack-cache/下別順手清掉它增量構(gòu)建快很多其二MomentTimezoneDataPlugin默認(rèn)打包了 2017 到當(dāng)前年份加 5 的全量時(shí)區(qū)數(shù)據(jù)用戶集中在少數(shù)時(shí)區(qū)的話收緊startYear/endYear甚至直接裁掉時(shí)區(qū)表體積當(dāng)場(chǎng)省出一塊。進(jìn)程層面Node 單線程事件循環(huán)怕同步 CPU 密集任務(wù)生產(chǎn)環(huán)境把渲染這類(lèi)重活交給獨(dú)立 workerrender-page本身就是獨(dú)立 job再上多實(shí)例吃滿多核按機(jī)器內(nèi)存給堆設(shè)上限一般取內(nèi)存的四分之一GC 抖動(dòng)拖慢響應(yīng)的問(wèn)題就穩(wěn)了。試過(guò)又撤掉的東西Gzip 全量壓連圖片都過(guò)了一遍壓縮CPU 躥高、收益為零壓縮只對(duì)文本類(lèi)資源有意義。頁(yè)面緩存 TTL 拉太長(zhǎng)更新后用戶長(zhǎng)時(shí)間看舊版短 TTL 加發(fā)布時(shí)主動(dòng)失效更穩(wěn)。chunk 拆得太碎HTTP/1.1 下小文件請(qǐng)求數(shù)爆炸反而更慢要么合并要么先升 HTTP/2。登錄態(tài)頁(yè)面進(jìn)了反代緩存就是上面那條紅線發(fā)現(xiàn)即回滾。Docker 里無(wú)腦復(fù)制實(shí)例四個(gè)實(shí)例共用一套庫(kù)內(nèi)存緩存和連接池各是各的問(wèn)題一點(diǎn)沒(méi)少先看慢查詢?cè)僬剶U(kuò)容。多實(shí)例不做失效同步配置里的ha標(biāo)志不是擺設(shè)多實(shí)例共享數(shù)據(jù)庫(kù)時(shí)發(fā)布流程必須統(tǒng)一失效緩存否則各實(shí)例看到的內(nèi)容不一致。最終賬單與三件事改了什么省了多少要?jiǎng)訋仔信渲眠B接池 min 2 / max 8高峰排隊(duì)減少接口 p95 明顯下降2 行 yaml/_assets/ Gzip 一年 immutable資源體積降約七成重復(fù)訪問(wèn)零下載約 8 行 nginxNodeCache 加 TTL 匿名頁(yè)面緩存首屏 2.8s → 0.9s1 行 js 約 7 行 nginx構(gòu)建瘦身 路由懶加載主包縮小約四成構(gòu)建時(shí)間減半約 11 行 webpack只做三件事在反代配置里寫(xiě)入/_assets/的 gzip 與expires 1y段落reload Nginx打開(kāi)server/core/cache.js把new NodeCache()改成new NodeCache({ stdTTL: 600, checkperiod: 120 })重啟服務(wù)啟用flags.sqllog跑一天按耗時(shí)排序的 SQL 逐條過(guò) EXPLAIN定位完再關(guān)回去。性能優(yōu)化是一場(chǎng)持續(xù)的小步快跑先吃下配置層的正反饋再逐步深入緩存和構(gòu)建用日志定位、用數(shù)據(jù)說(shuō)話。守住安全與一致性兩條底線Wiki.js 就能從能用走到好用。【免費(fèi)下載鏈接】wiki-Wiki.js | A modern and powerful wiki app built on Node.js項(xiàng)目地址: https://gitcode.com/GitHub_Trending/wiki78/wiki-創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考