在c/s結構的程序中,客戶端自動更新是簡化部署的一個常見需求.更新實際上做的操作是:
- 與服務器當前的文件做diff,找出所有變動的文件
- 下載變動的文件,替換掉本地的文件
從這兩個操作來看,服務器起碼要向客戶端提供兩個功能:
- 當前有那些文件
- 下載文件
其實第一個功能也可以轉為下載文件,只要提供描述當前文件列表信息的文件就行了。通常有兩種更新模式,增量更新與直接更新。增量更新是指低版本向高版本逐版本更新,這種方式的好處在於,服務器發布新版本時可以直接預生成diff補丁包,客戶端直接按照補丁包進行替換,但壞處是:
- 跨版本比較多時,會進行重復操作
- 管理起來麻煩,要保存所有歷史版本
直接升級則不會有重復操作,且服務器只要保存一個最新版本即可.本文描述的方案就是直接升級的.
服務端
基於文件服務器工作,FTP或者HTTP.用兩個json文件來管理版本,app.json和appflist.json.文件服務器上的結構為:
- 文件服務器根目錄
- app1
- app2
app就表示客戶端的根目錄,每個app里都會有app.json和appflist.json.服務器的開發工作就是生成這兩個文件,以及更新app目錄.
app.json
{"version":"1.0.0","info":"版本信息"}
用於描述版本號與版本信息,主要給客戶端進行快速判斷是否要升級.客戶端可以簡單的判斷版本號是否相等來決定要不要升級.
appflist.json
[{"path":"foo.dll","md5":"","ctime":147989119}]
用於描述文件列表信息,包含當前版本所有文件信息(除app.json,appflist.json),用於給客戶端做diff.
path
為客戶端根目錄的相對路徑
客戶端
客戶端通過本地的app.json和appflist.json與服務器上的同名文件來決定更新操作。更新時會用ftp://127.0.0.1/app1/foo.dll
這樣的url去下載文件,url的格式為協議://host:port/app目錄/文件路徑
更新分為五個步驟:
判斷是否要更新
通過比對app.json的version來決定是否要更新diff
下載appflist.json,然后通過md5篩選出增刪改的文件下載變動的文件
用path和app名下載增改的文件本地替換
進行增刪改操作刪除下載的文件
在更新時要注意用獨立的進程操作,因為要替換主程序的文件.
還有如何處理更新失敗的情況,更新失敗有兩種情況:
- 更新時程序被強制結束
- 文件操作時失敗
第一種情況發生時,如果正在替換文件,可能會損壞文件,導致主程序不能正常打開,這個問題不在於文件損壞,而是主程序無法運行,也就不能再次進入更新流程了,如果能進來則把流程重來一次就好了,不能進來時只能讓用戶手動打開更新程序了.因此這種情況是不需要在客戶端寫特定代碼處理的.
第二種情況發生時,也直接重試流程,如果一直錯,那可能是無操作文件的權限,此時並不會損壞舊文件,因此也不需要特殊處理.
最后我寫了一個shupd實現了這些功能,放在github上,shupd的server部分為一個用來生成app.json,appflist.json,拷貝變動文件的工具;client部分為c實現的動態庫。目前基於ftp工作,有興趣的朋友可以看看.