背景
這里所說的雲同步,指魅族的業務背景下,在移動應用場景中,經同步服務把數據保持多端一致的服務。它提供了如聯系人、便箋、信息、通話記錄、日歷、文件等類型的數據同步功能,由移動設備上的客戶端和服務端組成。
魅族雲同步於2008年開始使用,目前服務千萬級用戶。以下就同步協議,架構部署和數據處理等方面進行一些分享。
業務定義、同步協議
根據場景,把同步業務划分為如下3類型4協議:
MZ-SyncML協議
MZ-SyncML,基於微軟的SyncMl協議(俗稱4步同步),進行重新設計和開發實現的魅族同步協議。它交互精簡、業務聚焦,解決了典型的結構化數據同步需求,如聯系人、日歷、便箋、通話記錄、信息等業務。
它采用JSON進行數據交換,由接口層、同步點管理、數據解析、中間緩存數據管理、同步引擎、同步策略、沖突解決機制和Mapping關系管理等邏輯組成。
在同步策略上,實現了雙向同步200(Two-way、快同步)、慢同步201(Slow sync)、客戶端刷新同步203(Refresh from client)、服務端刷新同步205(Refresh from server)。
在同步點管理上,設計了客戶端同步點(ClientAnchor),用於校驗驗證采用何種同步類型,管理選取客戶端增量數據;還有服務端同步點(ServerAnchor),用於管理選取服務端增量數據。
一個完整的同步有4個階段,分別為Request、Submitdata、Getdata、Result,簡單示意圖如下。
其中,sessionId為服務端的會話標識,isFinal為分批數據結束標識,clientData為客戶端業務數據,serverData為服務端業務數據,resultList為處理data結果數據(標識成功或失敗)。
Semi-Sync協議
半同步協議,在快同步和慢同步中,同步失敗時,實現不重復上傳和拉取已同步成功的數據。請注意,它有別於MySQL同步復制的半同步方案(Semi-Synchronous)。
雲同步是批量傳輸數據的,如果某批次出錯,或單批次有若干數據出錯,導致同步失敗。下次同步時會有兩個問題,一是客戶端重復提交上次的所有數據(包括已成功的數據),二是拉取上次同步成功的數據。
MZ-SyncML設計了ClientAnchor和ServerAnchor兩個同步點,在半同步協議里,我們增加一個半同步錨點(SemiAnchor)。
以201慢同步為例說明。
第一次201同步:
客戶端提交100個聯系人,當前同步點Anchor
服務端成功寫入40個,60個失敗;則生成這40個Mapping數據,包括Luid、Guid、Anchor三個字段;更新SemiAnchor的值為Anchor;返回100個Result數據,40個成功,60個失敗
客戶端接收到100個Result數據,對40個成功的數據生成Mapping;並更新SemiAnchor為Anchor;同步結束,結果為失敗
第二次繼續發起201同步:
客戶端檢測有100個聯系人,但SemiAnchor與Mapping標記了40個同步過的數據,過濾掉這40個聯系人,只上傳60個聯系人
服務端接收到60個聯系人,並成功寫入;但服務端有40個聯系人,需要返回給客戶端;進一步檢測到SemiAnchor和Mapping,發現這40個已同步成功,無需返回給客戶端
客戶端拉取數據為0,無需處理;同步結束,結果為成功;下次則會發起200快同步
如上,根據SemiAnchor和Mapping數據,可以解決同步失敗下數據重復提交,二次拉取數據兩個場景問題。
File-Sync協議
文件同步協議,是從MZ_SyncML分離出來的獨立文件同步協議,它描述了文件類數據的同步方式。
采用和MZ-SyncML類似的方式,把變更文件信息列表進行同步,再按需進行上傳與下拉文件。
業務上需要考慮一點,即文件對象需與父對象保持一致。
注意,文件同步需在父對像同步完成后進行;並且,文件同步的類型需與父對象同步類型一致。 如聯系人本次同步類型是205,則聯系人頭像同步類型也必須是205。否則,會導致聯系人頭像同步時,解決沖突的合並規則與父對象不一致。
One-Sync協議
一次同步,即一次交互完成數據同步的協議,它解決聯系人分組,便箋分組,短信快速回復、郵箱帳號和瀏覽器書簽等小數據同步的場景。
數據模型設計為{key,value}的結構。key存儲業務的唯一值,如分組名稱;value以JSON格式存儲多個附屬值,如郵箱帳號的帳號類型,帳號名稱等。
一般而言,數據變更有增刪改(N、U、D)三個類型。對於key變更的操作,One-Sync里把U分解為N、D兩個操作。而對於key不變,value變化的變更操作仍為U。
One-Sync只有一個同步點,由服務端同步點ServerAnchor。
在一次請求中,客戶端會上傳SyncType、ServerAnchor和變更數據;服務端則把ClientData和ServerData進行比對后存儲,然后把比對后的SyncType、變更數據和New_ServerAnchor返回客戶端。
One-Sync同樣支持200、201、203、205同步類型。以客戶端發起205同步為例(服務端刷新同步),邏輯如下:
客戶端請求205的同步,不提交本地數據
服務端丟棄客戶端提交的數據,返回服務端的所有數據
客戶端接收數據成功,數據處理成功;然后清除本地舊數據,保證本地數據與服務端一致;本次同步結束,結果為成功,下次會進行200快同步
客戶端接收數據失敗,或接收成功后數據處理失敗;則不清除本地舊數據;本次同步結束,結果為失敗;下次仍進行205同步
同步失敗處理機制
同步涉及到客戶端、服務端邏輯,實際邏輯中會有失敗的場景,我們設計了失敗處理的機制。
在Submitdata階段,服務端處理完客戶端數據,會返回每條的結果數據Result給客戶端,標記成功或失敗
在Getdata階段,客戶端處理完服務端數據后,也會提交每條Result數據給服務端,標記成功或失敗
只要有一條數據標記為失敗,當次同步即被認為失敗
下次同步會使用上次的同步點(如何不重復傳輸數據,請看Semi-Sync協議)
服務架構
分析雲同步的業務場景,具有下面的特性:
雲同步的服務端服務能力,和用戶數成正比關系。魅族雲同步隨着用戶規模的增長,經過幾次的重構、線上運行、探索與沉淀,逐步形成了當前平台的架構。
接口規范
我們規范了接口的固有參數,擴展參數,統一返回格式,可精確到代碼行的錯誤碼,匹配錯誤碼的信息。
如userid、imei、sn為固有參數;像apk版本,系統固件為擴展參數,部分接口不需要。
返回格式例子如下:
{
"code":10001
"message":"參數不合法"
"value":""
}
其中code為錯誤碼,200為成功,其他為錯誤。value是成功時返回的數據,建議使用JSON格式傳遞。
message是code對應的錯誤信息,它與code共同放在資源文件。在返回時由一個MessageHandler截停,自動填上code對應的message,不需要人工維護。
模塊化+單元化
通過業務規整,抽象出了聯系人模塊、短信模塊 、通話記錄模塊、小業務模塊、文件同步模塊。
聯系人、短信、通話記錄是使用頻次和數據量最大的3個項,故此獨立成模塊。
文件同步,與結構化數據分離,進行IO等有針對性的性能調優。
我們在一個IDC里划分了多個服務單元(建議不多於5個),每個單元包括服務模塊和存儲,一個單元服務只服務合理的用戶數。這樣易於控制規模,分拆風險,維護遷移和容災。
海量數據+路由組件
雲同步的數據是海量的,DB記錄數可能達到百億級、千億級,單個數據庫解決不了雲同步的業務場景。
我們按用戶標識(Userid)進行水平拆分,設計出多個數據庫;配備一個Userid到DB的路由規則。
單實例DB服務器上部署多個數據庫,建議10-30個,既降低運維成本,又提高資源利用率。
橫向擴容時,更新路由規則即可平滑完成。
該DB集群,由一個路由組件(SyncRouter)來管理。它包括Userid到DB、Userid到Unit、Userid到Redis、Userid到文件系統的多個路由功能。它具有透明訪問、業務低耦合、DB連接池自管理等特點。
我們采用RDB存取對用戶常用的熱數據,而用NoSQL存取冷備數據。例如聯系人有個時光機的備份功能,我們就采用HBase來實現存儲。
多機房部署
我們做多機房部署的目的有三,一是用戶就近訪問,二是流量調度,三是異地容災。
看如下雲同步的架構圖,以3個IDC為例。
部署了多IDC,設計好路由規則,由路由組件提供Userid到IDC的路由功能。
IDC間的用戶數據分開,不共享,做好異地容災,如把IDC_1的數據備份到IDC_2。而在IDC_1出現故障無法提供服務時,通過GSLB分流,把該用戶群分發到IDC_2里進行服務。
路由DB采用單點寫保證一致性。所有IDC通過專線訪問主IDC,操作主路由DB;主IDC通過MQ通知其他IDC,進行數據更新,保持數據一致。
手機端根據Useric和域名,訪問GSLB的分發服務,獲取到用戶相應的同步服務IP,實現流量調度目的。並且,手機端都通過IP訪問同步服務,不使用域名,有效避免DNS劫持問題。
GSLB根據IP所在區域和所屬線路,在最優的IDC落地,並返回業務的最優IP(GSLB詳見魅族另一篇博文,不再贅述)。
理論上,上述的方案僅適應於有客戶端的Http、Https請求,不適合瀏覽器訪問。 為此,我們做了一個擴展,支持瀏覽器按GSLB的規則進行調度。
把業務分為總域名、多個子域名(匹配多個IDC)
業務層嵌入一個GSLB模塊,根據設定規則進分發到目標子域名
流量優化
流量優化一方面是采用gzip或deflate進行壓縮傳輸,一方面是讓原始數據盡可能的小。
魅族雲同步第一階段采用XML為交換格式;第二階段上線了MZSync項目,采用JSON為交換格式。
我們在考慮數據格式時,選出傳統JSON、精簡JSON和Protobuf三種格式,進行對比。
精簡JSON,是指變量名為少量字符,不輸出空值變量的JSON格式。
Protobuf本質上是對變量進行排序,不需要傳變量名。
精簡JSON和Protobuf的設計原理,有點相似,前者簡化變量名,后者不傳變量名。
最后,我們選擇了精簡JSON格式,與傳統JSON相比,能減少40%-60%的流量,接近Protobuf的值。
參考圖如下(使用多維度抽樣數據,進行實測計算得到)。
結語
以上,便是在業務的實踐、發展中,總結的同步協議,架構部署和數據處理心得。
其中,MZ_SyncML和Semi-sync描述了雲同步的核心解決方案,File-Sync和One-Sync描述了雲同步的業務解決方案;而One-Sync的設計思想較為通用,適用場景不局限於雲同步。
多機房部署、GSLB和路由組件,構成了魅族雲同步的核心架構,也保證了高可用;而精簡JSON並非新技術,我們描述了一種數據交換的思路。歡迎大家討論交流。