SSR 頁面 CDN 緩存實踐

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"SSR 是一項資源密集型任務,要抵抗更大流量、提供更快的服務,緩存是其中的必修課。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而 CDN 緩存——作爲靜態資源的首要支撐,適合武裝到 SSR 頁面嗎?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"開始之前"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大家對 CDN 應該已經耳熟能詳,如果不甚瞭解也沒關係,我們先通過一系列問答帶諸位走近這個話題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"爲什麼接入 CDN?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"抽象一個簡單的請求鏈路,方便理解 CDN 的定位。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接入前:"}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/6e\/6e644df38d2f621bbb66d7a9b3de80ba.png","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用戶 -> Nginx -> App Server"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接入後:"}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/41\/41769e3f68eece0c136c1952b9e7cd1c.png","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用戶 -> CDN -> Nginx -> App Server"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看似增加了一層傳輸成本,其實不然。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CDN 利用自身廣大的服務器資源,能動態優化訪問路由、就近提供訪問節點,以更低延遲、更高帶寬從源站獲取數據,優化了網絡層面的用戶體驗。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"爲什麼開啓 CDN 緩存?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"開啓前:瀏覽器 -> CDN -> Nginx -> App Server1 -> App Server2 -> …"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"開啓後:瀏覽器 CDN"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CDN 能夠緩存用戶請求到的資源,並且可以包含 HTTP 響應頭。在下一次任意用戶請求同樣的資源時,用緩存的資源直接響應用戶,節省了本該由源站處理的所有後續步驟。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"更直觀的表達,就是截短了請求鏈路。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/9b\/9b44b9982a04de90ceb95b22abe1c32c.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"如何開啓 CDN 緩存?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在不考慮自研 CDN 的情況下,開啓 CDN 緩存的步驟非常簡單:"}]},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"域名接入 CDN 服務,同時針對路徑啓用緩存"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"在源站設置 Cache-Control 響應頭,爲了更靈活地控制緩存規則,但並不是必須"}]}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"哪些服務可以開啓 CDN 緩存?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大部分網站都適合接入 CDN,但 SSR 頁面只有滿足一定條件纔可以開啓 CDN 緩存"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"無用戶狀態"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對時效性要求不高,至少能接受分鐘級的延遲"}]}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"怎樣判斷是否命中緩存?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不同 CDN 平臺檢測的方法略有不同,本質上都是判斷響應頭的標識字段。以騰訊 CDN 爲例,響應頭 X-Cache-Lookup 分別表示"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Hit From MemCache: 命中 CDN 節點的內存"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Hit From Disktank: 命中 CDN 節點的磁盤"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Hit From Upstream: 未命中緩存,回源"}]}]}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/32\/32299705ffb70a5c5d07b9563ef0b72e.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果該字段不存在,說明該頁面沒有配置 CDN,或未開啓緩存。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"CDN 緩存優化"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用來衡量緩存效果的重要指標是緩存命中率,在正式設置 CDN 緩存之前,我們再來了解幾個提高緩存命中率的要點。這些要點也適合作爲評估系統是否應該接入 CDN 緩存的標準。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"延長緩存時間"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"提高 Cache-Control 的時間是最有效的措施,緩存持續時間越久,緩存失效的機會越少。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"即使頁面訪問量不大的時候也能顯著提高緩存命中率。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"需要注意,Cache-Control 只能告知 CDN 該緩存的時間上限,並不影響它被 CDN 提早淘汰。流量過低的資源,很快會被清理掉,CDN 用逐級沉澱的緩存機制保護自己的資源不被浪費。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"忽略 URL 參數"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用戶訪問的完整 URL 可能包含了各種參數,CDN 默認會把它們當作不同的資源,每個資源又是獨立的緩存。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而有些參數是明顯不合預期的,例如,頁面鏈接在微信等渠道分享後,末尾被掛上各種渠道自身設置的統計參數。平均到單個資源的訪問量就會大大降低,進而降低了緩存效果。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CDN 支持後臺開啓 "},{"type":"codeinline","content":[{"type":"text","text":"過濾參數"}]},{"type":"text","text":" 選項,來忽略 URL "},{"type":"codeinline","content":[{"type":"text","text":"?"}]},{"type":"text","text":" 後面的參數。此時同一個 URL 一律當作同一個資源文件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在騰訊 CDN 中,忽略參數的功能無法針對某個 URL,僅支持整個域名生效,這讓過濾參數成爲了極具風險的操作。除非域名緩存專用,否則不建議開啓這個選項,即便同域名內所有已接入 CDN 緩存的資源都不依賴 URL 參數,也不能保證將來不會因此踩坑。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"主動緩存"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"化被動爲主動,纔有可能實現 100% 的緩存命中率。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"常用的主動緩存是資源預熱,更適合 URL 路徑明確的靜態文件,動態路由無法交給 CDN 智能預熱,除非依次推送具體的地址。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"代碼演進"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"談過 CDN 緩存優化的幾個要點,便可得知 CDN 後臺的配置是需要謹慎對待的。我在實際操作中,也經過了幾個階段的調整,可畢竟具體配置方式取決於 CDN 服務商,因此本文不再深入討論。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在,我們要把目光轉到代碼層的演進了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"一、掌控緩存"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"代碼配置有一個前提,即 CDN 後臺需要開啓讀取源站 Cache-Control 的支持。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而後,只要簡單地添加響應頭,就能從運維手中接管設置 CDN 緩存規則的主動權。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以 Node.js Koa 中間件爲例,全局的初始化版本如下"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"app.use((ctx, next) => {\n  ctx.set('Cache-Control', `max-age=300`)\n})\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當然,上述代碼的疏漏是非常多的。在 SSR 應用中,不太需要緩存所有的頁面,這就要補充路徑的判斷條件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"二、控制路徑"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"雖然 CDN 後臺也可以配置路徑,但配置方式乃至路徑數量都有侷限性,不如代碼形式靈活。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假如我們只需要緩存 \/foo 頁面,就加入 if 判斷"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"app.use((ctx, next) => {\n  if (ctx.path === '\/foo') {\n    ctx.set('Cache-Control', `max-age=300`)\n  }\n})\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這就陷入了第一個陷阱,一定要注意路由對 path 的處理。一般地,'\/foo' 和 '\/foo\/' 是兩個獨立的 path。可能因爲 ctx.path === '\/foo' 而漏掉了請求 path 爲 \/foo\/ 的處理。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"三、補充路徑"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"僞代碼如下"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"app.use((ctx, next) => {\n  if ([ '\/foo', '\/foo\/' ].includes(ctx.path)) {\n    ctx.set('Cache-Control', `max-age=300`)\n  }\n})\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此外,CDN 後臺的配置也需要規避這個問題。在騰訊 CDN 中,目錄和文件適用於不同的頁面路徑。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"四、忽略降級頁面"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在服務端渲染失敗時,爲了提高容錯,我們會返回降級之後的頁面,轉爲客戶端渲染。如果因爲偶然的網絡波動,導致 CDN 緩存了降級頁面,將在一段時間內持續影響用戶體驗。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以我們又引入了 ctx._degrade 自定義變量,標識頁面是否觸發了降級"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"app.use(async (ctx, next) => {\n  if ([ '\/foo', '\/foo\/' ].includes(ctx.path)) {\n    ctx.set('Cache-Control', `max-age=300`)\n  }\n\n  await next()\n\n  \/\/ 頁面降級時,取消緩存\n  if (ctx._degrade) {\n    ctx.set('Cache-Control', 'no-cache')\n  }\n})\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"沒錯,這並不是最後一個陷阱。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"五、Cookie 和狀態治理"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面已經提到了 CDN 可以選擇性地緩存 HTTP 響應頭,可是此選項是對整個域名生效,又普遍需要開啓。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"新的問題正是來自一個不希望被緩存的響應頭。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"應用 Cookie 的設置依賴於響應頭 Set-Cookie 字段,Set-Cookie 的緩存直接會導致所有用戶的 Cookie 被刷新爲同一個。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有多個解決方案,一是該頁面不要設置任何 Cookie,二是代理層過濾掉 Set-Cookie 字段。可惜騰訊 CDN 目前還不支持對響應頭的過濾,這步容錯必須自己操作。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"app.use(async (ctx, next) => {\n  const enableCache = [ '\/foo', '\/foo\/' ].includes(ctx.path)\n\n  if (enableCache) {\n    ctx.set('Cache-Control', `max-age=300`)\n  }\n\n  await next()\n\n  \/\/ 頁面降級時,取消緩存\n  if (ctx._degrade) {\n    ctx.set('Cache-Control', 'no-cache')\n  }\n  \/\/ 緩存頁面不設 Set-Cookie\n  else if (enableCache) {\n    ctx.res.removeHeader('Set-Cookie')\n  }\n})\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面增加的代碼旨在頁面響應前移除 Set-Cookie,但是中間件的加載順序是難以控制的。特別是一些(中間件)插件,會隱式地創建 Cookie,這讓 Cookie 的清理工作異常麻煩。如果後續維護人員不知情,很可能將 Set-Cookie 重新加入到響應頭中。所以,這種擦屁股的工作,儘量在代理層處理,而不是放在代碼邏輯中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了 Cookie,還可能面臨其他狀態信息管理問題。比如在 Vuex 的 renderState 中存放請求用戶的登錄狀態,此時 HTML 頁面嵌入了用戶信息,如果被 CDN 緩存,在客戶端將發生和未清除 Set-Cookie 相似的問題。類似的例子還有很多,它們的解決思路非常相像,接入 CDN 緩存前務必對狀態信息做好全面的排查。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"六、定製緩存路徑"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在功能總算趨於正常,然而緩存規則複雜多變,如果想設置更多頁面,還要單獨定製緩存時間呢?這段代碼仍需要不斷地變動。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"例如,我們只想緩存 \/foo\/:id,而不緩存 \/foo\/foo、\/foo\/bar 等路徑。"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意 CDN 後臺可能只支持配置一個 \/foo\/ 開頭的緩存路徑,這就要求我們需要將 ctx.set('Cache-Control', 'no-cache') 做爲默認處理,加在中間件的第一行。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"又比如,我們想緩存 \/foo 頁面 5 分鐘,\/bar 頁面 1 天,又需要引入一個時間配置表。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個中間件和相應的配置就會變得越來越難以維護。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此,我們換一種思路,緩存規則不再交給中間件,而是轉到 Vue SSR 的 entry-server,通過 metadata 可以做到頁面級別的配置。由於 SSR 方案的差異性,不再贅述具體實現。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"七、緩存失效"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"緩存失效是個中性詞,如何處理 CDN 緩存失效,此中利弊不得不慎重權衡。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一方面,它會間歇增加服務壓力,在 Serverless 應用中還會提高計算成本。而另一方面,許多場景我們不得不主動觸發它,才能真正更新資源。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CDN 緩存的黑暗面無法讓人忽視。對用戶而言,緩存是透明的,對產品、技術卻很可能成爲阻礙。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果處理不當,它將影響新功能能否及時發佈、阻斷後置所有服務的埋點、提高風險感知的成本,以及無法保障一致性,增加了線上問題的排查難度。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此,十分有必要設立一個負責緩存刷新、預熱的觸發式服務,用以改進開發人員的體驗。可是 CDN 緩存可控性很低,刷新也不能做到全然實時生效。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"處於頻繁變化的頁面,最好考慮進入穩定期再開啓 CDN 緩存。即使是穩定的、大流量的頁面,也還需要考慮 CDN 緩存穿透的防範措施。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一旦 CDN 緩存在 SSR 架構中得到重用,就要做好長期調整決策的準備。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"總結"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CDN 緩存是一把利刃,在大流量的場景下,可以替源站攔截幾乎所有的請求,能提供極強伸縮性的負載。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那麼 SSR 應用適合接入 CDN 緩存嗎?再一次細數上面提到的諸多問題…"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"路徑控制"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"頁面降級"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"狀態治理"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"緩存失效"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"答案得你自己說了算。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"實際上,極少數 SSR 頁面場景才需要 CDN 緩存,如門戶首頁。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"流量不高、路徑分散的一般業務,只需要使用動態的 CDN 加速和靜態文件緩存,就能基本滿足 CDN 代理層的優化需要。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"頭圖:Unsplash"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作者:齊雲雷"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文:https:\/\/mp.weixin.qq.com\/s\/2jW4Xc94IHeRsgHLl6hU_g"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文:SSR 頁面 CDN 緩存實踐"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"來源:微醫大前端技術 - 微信公衆號 [ID:wed_fed]"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"轉載:著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。"}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章