背景
这里所说的云同步,指魅族的业务背景下,在移动应用场景中,经同步服务把数据保持多端一致的服务。它提供了如联系人、便笺、信息、通话记录、日历、文件等类型的数据同步功能,由移动设备上的客户端和服务端组成。
魅族云同步于 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 并非新技术,我们描述了一种数据交换的思路。欢迎大家讨论交流。
感谢郭蕾对本文的审校。
给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ , @丁晓昀),微信(微信号: InfoQChina )关注我们。
评论