記錄MVC專案部署時的CDN快取問題

記得要微笑發表於2021-12-06

概述

本文將分析在釋出前後端未分離專案(freemaker)時遇到的CDN快取問題,主要有以下兩個問題:

  • 頁面請求獲取的html裡面卻是舊版本號的script連結
  • script指令碼連結是新版本號但拉取到的卻是舊指令碼程式碼

問題分析

1、頁面請求獲取的html裡面卻是舊版本號的script連結

問題分析前首先我們要知道以下知識點:

(1)freemaker專案的頁面是後端服務將ftl處理成html返回的

(2)部署時會遍歷ftl檔案,對所有的script連結打上版本號

// 構建前 supplierQuoteDetailPaging.ftl

<!DOCTYPE html>
    <head>
        <script type="text/javascript" src="${Global.getConfig("web.app.static.url")}/js/supplierQuoteDetail.js"></script>
    </head>
</html>


// 在`Jenkins`構建後會對請求靜態指令碼的`url`加上版本號 supplierQuoteDetailPaging.ftl

<!DOCTYPE html>
    <head>
        <script type="text/javascript" src="${Global.getConfig("web.app.static.url")}/js/supplierQuoteDetail.js?version=1638706227856"></script>
    </head>
</html>

(3)後端是叢集服務,部署採用滾動釋出,也就是說部署時節點服務是一批一批來更新的,直到叢集中所有的例項都更新成新版本,而不是一次性全量更新

當專案還是部署時,因為服務採用滾動釋出,因此在這個期間新服務和舊服務會同時存在。如果在這個階段訪問頁面,頁面介面可能命中舊服務,也可能命中新服務,當命中舊服務時,請求得到的html裡面script連結打上的是舊版本號;當部署完成時,群中所有的例項都更新成新版本,頁面請求命中新服務,請求得到的html裡面script連結打上的是新版本號。

部署後html中不是最新的版本號.png

解決方案:待專案部署完成後重新整理頁面就可以了

2、script指令碼連結是新版本號但拉取到的卻是舊指令碼程式碼

正常來說,部署專案後,瀏覽器根據新版本號去請求CDN上的靜態指令碼檔案,如果CDN快取中沒有對應新版本號對應的指令碼檔案,則會向後端服務拉取新指令碼,然後CDN在做一次快取,後面的指令碼請求直接由CDN返回。

但是,如果部署還未完成瀏覽器就去訪問了,此時這個階段新服務和舊服務是同時存在的,當新版本號對應的指令碼在CDN上找不到時,就會去服務請求,恰恰請求命中的是舊服務(服務響應跟版本號無關),舊服務返回舊的指令碼,然後CDN快取新版號對應的舊指令碼,這樣後續每次請求拉取到的都是CDN上快取的就指令碼,因此就出現了上述問題。

新版本號未拉取到新指令碼.png

我們實際的解決方案是對此類專案維護兩個釋出任務(重複批),也就是部署兩次。我們先假設三次部署過程版本號和指令碼的對映關係:

版本號指令碼
上一次部署V1.0J1.0
重複批次1V1.1J1.1
重複批次2V1.2J1.2
注:重複批次1與重複批次2程式碼沒有任何改變,J1.1J1.2程式碼一模一樣

假如重複批次1部署未完成時訪問走的是錯誤的命中流程,CDN快取<V1.0,J1.0>,那麼後續再怎麼訪問拿到的都是不正確的指令碼程式碼。當重複批次1的服務滾動釋出完成後,服務上的指令碼都是J1.1,如果再部署重複批次2,部署未完成時請求,可能命中指令碼J1.1或者J1.2,鑑於J1.1J1.2程式碼一模一樣,CDN快取<V1.0,J1.1>或者<V1.0,J1.2>,結果都是正確的;如果請求時部署完成時發起的,命中的指令碼就是J1.2CDN快取<V1.0,J1.2>,結果也是正確的。

另外,還有一些其他的解決方案,比如CDN重新整理和預熱

CDN重新整理和預熱

CDN提供資源的重新整理和預熱功能。通過重新整理功能,您可以強制CDN節點回源並獲取最新檔案;通過預熱功能您可以在業務高峰期預熱熱門資源,提高資源訪問效率。快取重新整理和快取預熱的區別如下所示:

  • 快取重新整理:提交快取重新整理請求後,CDN節點的快取內容將會被強制過期。當使用者向CDN節點請求資源時,CDN會直接回源站請求對應的資源返回給使用者,並將其快取。
  • 快取預熱:提交快取預熱請求後,源站將會主動將對應的資源快取到CDN節點。當使用者首次請求時,就能直接從CDN節點快取中獲取到最新的資源,無需再回源站請求。

重新整理分為兩種:URL重新整理和目錄重新整理。

  • URL重新整理:通過提供目錄下檔案的方式,強制CDN節點回源獲取最新檔案,生效時間5分鐘內,API介面RefreshObjectCaches
  • 目錄重新整理:通過提供目錄及目錄下所有檔案的方式,強制CDN節點回源獲取最新檔案。生效時間為5分鐘內,API介面同上。

呼叫該介面前,請注意:

  • 支援post請求,引數用form表單。
  • 重新整理預熱類介面包含RefreshObjectCaches重新整理介面和PushObjectCache預熱介面。
  • 同一個ID 每天最多可提交2000條URL重新整理和100個目錄重新整理。
  • 每次請求最多隻能提交1000條URL重新整理。
  • 每秒最多50次請求。

img

除了上圖所示外,還有一個引數Action,值為RefreshObjectCaches,返回值與預熱介面一致,如下所示:

img

示例程式碼:

https://cdn.aliyuncs.com?Action=RefreshObjectCaches
&ObjectPath=abc.com/image/1.png\nabc.com/image/2.png
&ObjectType=File
&<公共請求引數>

預熱僅支援完整URL預熱,不支援目錄預熱,將指定的資源主動預熱到CDNL2二級節點上,使用者首次訪問即可直接命中快取。生效時間為5分鐘內,API介面PushObjectCache

呼叫PushObjectCache將源站的內容主動預熱到L2 Cache節點上,您首次訪問可直接命中快取,緩解源站壓力。呼叫該介面前,請注意:

  • 支援post請求,引數用form表單。
  • 重新整理預熱類介面包含RefreshObjectCaches重新整理介面和PushObjectCache預熱介面。
  • 同一個ID每天最多可提交500條URL預熱。
  • 每次請求最多隻能提交100條URL預熱。
  • 每秒最多50次請求。
  • 單個ID的預熱佇列最大限制為100條,根據提交的先後順序來預熱。如果佇列任務堆積到100條,則需要等提交的預熱請求完成之後才能提交新的,以此來保持佇列大小始終不超過100。
  • CDNL2 Cache節點架設在L1 Cache節點和源站之間,幫助您緩解源站壓力。

img

img

http(s)://cdn.aliyuncs.com/?Action=PushObjectCache
&ObjectPath=abc.com/image/1.png\nabc.com/image/2.png
&<公共請求引數>

Q&A

Q:快取預熱失敗怎麼辦?

A:快取預熱失敗的可能原因是:

  • 執行大批量檔案的集中預熱時,可能會導致您的源站頻寬資源被佔滿。預熱時請儘量分批次執行,您也可以通過擴充源站頻寬來提升預熱效率。
  • 檢查資源對應的快取過期時間是否為0,如果為0,不允許快取會導致預熱失敗;
  • 排查源站的cache-control配置,配置private、no-cache、no-store將導致CDN不能快取引起預熱失敗,如果不配置,預設為private。
  • 目前不支援預熱目錄、動態檔案和快取過期時間為0的url
  • 當您預熱多個URL且以“;”進行分隔時,需切換到英文狀態,中文狀態下的“;”會導致預熱失敗

Q:做了重新整理和預熱操作,為什麼訪問的檔案還是舊的?

A:可能是您快取重新整理和預熱的時間間隔太近,導致重新整理失敗,建議您重新整理和預熱的間隔時間為五分鐘以上。

參考:
華為雲CDN
CDN重新整理和預熱

相關文章