從7月中旬左右,我們客戶端更新失敗率由原來的2%上升到10%。更新后台數據統計顯示更新失敗中的90%為HTTP下載失敗,具體的失敗原因是文件下載完成后MD5與服務器預期的MD5不匹配。
在着手調查解決這個問題時,我第一個懷疑的點是客戶端下載器。我希望能在代碼里找到發生以下兩種情況的可能性:一種是客戶端在代碼是否會導致文件下載不完整,另一種則是客戶端的HTTP訪問有沒有明確禁用Http Cache。但客戶端代碼的Code Review表明每次Http請求都在Http頭中明確啟動了禁用Cache標志,對HTTP文件下載文件長度校驗也吻合Http1.1的規范。
在第一次分析失敗的基礎上,我系統地整理了客戶端從構建完成直至其被傳送到最終用戶機器上的全部流程,從完整的數據流路徑中來分析問題的可能性。這個大的路徑分天然地分成了兩個獨立的部分。
一是目標文件的部署過程:
為了避免可能存在的cache,我們客戶端的每個新版本發布,都會在cdn源站上以此版本號創建創建一個全新的目錄,下載地址形式如下:
部署過程的URI配置、源和目錄配置都有完整的手工驗證,這兒不會出現錯誤。唯一有可能出錯的過程是Rysnc目標文件到CDN源站的過程,但事實上如果Rsync出錯,則影響的升級錯誤量不可能只有10%這么少。所以部署流程中的錯誤可以全部排除。
二是客戶端的下載過程:
更進一步地,把把客戶端的下載過程進行細分,可以得到兩個子流程,即DNS解析過程和客戶端文件傳輸過程:
我們嘗試從客戶端下載的這兩個子流程中去猜測錯誤發生的可能性:
1. ISP DNS或者CNAME服務器出錯,返回了不正確的CDN節點IP給客戶端,導致客戶端連接上了錯誤的目標站點,從而下載不到任何文件或者被拒絕訪問。
2. CDN節點有可能存在不正確的文件緩存機制,導致客戶端訪問到的是此文件的歷史版本。
3. 這些失敗的用戶中的大部分為網吧用戶,因為網吧的網管軟件升級和客戶端升級相沖突,可能存在不正確的歷史版本文件緩存。
4. ISP網關或ISP中間服務器存在不正確的文件緩存機制,導致客戶端訪問到的文件是此文件的歷史版本。
針對可能性1,我們從錯誤日志中分析得到失敗最多的10個CDN節點IP (來自這些IP的失敗占了總失敗的80%以上),經和CDN廠商確認,均是其CDN邊緣節點,故障1可以排除。
針對可能性2,我們從10個失敗最多的CDN節點里抽樣調查並下載了數個校驗失敗文件, 結果顯示CDN節點上的數據准確無誤。故障2可以排除。
剩下的問題就只有可能是猜測3或猜測4,而這兩個猜測均指向了文件緩存機制(http cache).於是我們繼續進行錯誤日志分析,希望能找到歷史版本中的文件MD5和失敗時的MD5匹配。而日志分析結果也確實證明了這一點,日志的分析結果如下:
1.文件下載校驗失敗只能解釋為Cache失敗--這些校驗失敗的文件的MD5,95%以上對應的是我們某一些客戶端歷史版本該文件的MD5。也就是說客戶端總是下載到歷史上存在該文件。
2.下載失敗的客戶端IP散落在廣大的北方,其中80%以上是聯通節點的失敗。但是IP分布並無規律,不集中在一些地區。
3.校驗失敗的文件總是那四個4個文件,它們的檢驗失敗占了總校驗失敗的90%以上。
4.失敗的IP不在我們能查詢到的網吧IP列表當中。
5.失敗的客戶端安裝目錄無網吧安裝目錄結構特征。
6.失敗的客戶端網吧管理軟件檢測顯陽性的樣本占總失敗的比例不到1%
日志分析結果的4、5、6項讓網吧的可能性也被排除在外,那么這段時間的文件校驗失敗,最大可能是客戶端網絡接入商這一環有問題--某些二三級網絡服務接入商為了節省給最終用戶的帶寬,而僅僅使用域名+文件名作為Cache KEY來實現HTTP下載Cache.且不管用戶在HTTP訪問時是否帶有No Cache標志,它都以自己的Cache命中的文件優先返回給了最終用戶。這樣的解釋在理論上說得通,也能與我們遇到的失敗率(10%左右)相一致。
針對可能存在的這種不良Cache策略,我們嘗試的解決方案如下:
對每一個新版本的客戶端升級文件壓縮包的名字前帶上編譯號前綴,這樣看起來每次升級,它的文件名和目錄均不同。新的客戶端升級URL形式為:
假如ISP的HTTP Cache策略如我們所推測,那么他們僅使用的域名文件名作為Cache Key在我們修改文件名之后就會失效,相應的就會CDN節點上獲取正確的文件版本,最終客戶端的下載就會成功。
在此方案實施后,客戶端升級校驗失敗率降到了0.5%,問題得以解決.