概述
本文將分析在釋出前後端未分離專案(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
連結打上的是新版本號。
解決方案:待專案部署完成後重新整理頁面就可以了
2、script
指令碼連結是新版本號但拉取到的卻是舊指令碼程式碼
正常來說,部署專案後,瀏覽器根據新版本號去請求CDN
上的靜態指令碼檔案,如果CDN
快取中沒有對應新版本號對應的指令碼檔案,則會向後端服務拉取新指令碼,然後CDN
在做一次快取,後面的指令碼請求直接由CDN
返回。
但是,如果部署還未完成瀏覽器就去訪問了,此時這個階段新服務和舊服務是同時存在的,當新版本號對應的指令碼在CDN
上找不到時,就會去服務請求,恰恰請求命中的是舊服務(服務響應跟版本號無關),舊服務返回舊的指令碼,然後CDN
快取新版號對應的舊指令碼,這樣後續每次請求拉取到的都是CDN
上快取的就指令碼,因此就出現了上述問題。
我們實際的解決方案是對此類專案維護兩個釋出任務(重複批),也就是部署兩次。我們先假設三次部署過程版本號和指令碼的對映關係:
版本號 | 指令碼 | |
---|---|---|
上一次部署 | V1.0 | J1.0 |
重複批次1 | V1.1 | J1.1 |
重複批次2 | V1.2 | J1.2 |
注:重複批次1與重複批次2程式碼沒有任何改變,J1.1
與J1.2
程式碼一模一樣
假如重複批次1部署未完成時訪問走的是錯誤的命中流程,CDN
快取<V1.0,J1.0>
,那麼後續再怎麼訪問拿到的都是不正確的指令碼程式碼。當重複批次1的服務滾動釋出完成後,服務上的指令碼都是J1.1
,如果再部署重複批次2,部署未完成時請求,可能命中指令碼J1.1
或者J1.2
,鑑於J1.1
與J1.2
程式碼一模一樣,CDN
快取<V1.0,J1.1>
或者<V1.0,J1.2>
,結果都是正確的;如果請求時部署完成時發起的,命中的指令碼就是J1.2
,CDN
快取<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次請求。
除了上圖所示外,還有一個引數Action
,值為RefreshObjectCaches
,返回值與預熱介面一致,如下所示:
示例程式碼:
https://cdn.aliyuncs.com?Action=RefreshObjectCaches
&ObjectPath=abc.com/image/1.png\nabc.com/image/2.png
&ObjectType=File
&<公共請求引數>
預熱僅支援完整URL預熱,不支援目錄預熱,將指定的資源主動預熱到CDN
的L2
二級節點上,使用者首次訪問即可直接命中快取。生效時間為5分鐘內,API
介面PushObjectCache。
呼叫PushObjectCache
將源站的內容主動預熱到L2 Cache
節點上,您首次訪問可直接命中快取,緩解源站壓力。呼叫該介面前,請注意:
- 支援
post
請求,引數用form
表單。 - 重新整理預熱類介面包含
RefreshObjectCaches
重新整理介面和PushObjectCache
預熱介面。 - 同一個ID每天最多可提交500條
URL
預熱。 - 每次請求最多隻能提交100條
URL
預熱。 - 每秒最多50次請求。
- 單個
ID
的預熱佇列最大限制為100條,根據提交的先後順序來預熱。如果佇列任務堆積到100條,則需要等提交的預熱請求完成之後才能提交新的,以此來保持佇列大小始終不超過100。 CDN
的L2 Cache
節點架設在L1 Cache
節點和源站之間,幫助您緩解源站壓力。
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:可能是您快取重新整理和預熱的時間間隔太近,導致重新整理失敗,建議您重新整理和預熱的間隔時間為五分鐘以上。