动态化方案通常都是比较大型的, 好比react native 、flutter 等都是从UI,运行逻辑等多方面完整的动态更新。但实际上,移动端还有不少细粒度的配置类数据须要支持动态更新的。 前端
好比某一个文案或者广告的位置但愿能够根据用户表现来随时改动,又好比你开开发了一个线上功能,但上线后才发现里面潜藏了一个严重的问题, 但愿能够同过一个线上开关当即关闭此功能。react
这一类需求用一句话来说就是叫作:千万不要写死。 git
Android里面你们都很数据的sharedpreference,里面能够存储不少的K-V类型的数据。那咱们如何来实现一套可动态更新的K-V存储机制呢? 从而将这类小型配置或AB开关准时下发到因此的客户端。算法
为了实现这个功能,不少公司都研发了一套本身的配置中心系统,好比阿里内部的orange系统,能狗作到秒级下发因此配置到全量客户端。后端
要实现这样一套系统,须要考虑到几个问题点:缓存
首先咱们要解决的问题是:如何才能尽快告诉客户端,配置数据以及更新了?安全
1.主动拉取(Pull)性能优化
第一种方式,咱们可让客户端进行主动轮询:服务器
关于长轮询说明下,普通的短链接是客户端发起socket请求,服务端收到后,无论有没有数据,都会当即返回。网络
而若是长轮询,服务端若是没有数据则会hold该请求,将socket等请求信息保存起来, 不当即返回, 等到有数据时才经过socket从新写回客户端。当客户端收到结果,或者判断请求超时,便会发起下一次的长轮询请求, 此时的超时时间通常会比正常的http请求的超时时间长, 好比一分钟,比减小请求次数。
这种方式可能可以达到比较好的实时性,但很显然,会带来很是多的流量浪费,并且对后端也会带来很是多无用的请求,形成机器资源的浪费。
2.推送(Push)
这种方式能够借滋长链接
当咱们发布更新配置后,能够经过长链接通道向在线用户下发配置变动通知, 用户收到通知后便会主动去拉取配置。
这种方式能够极大的下降流量损耗,只有配置变动时才会拉取数据,但也存在一些问题:
* 须要维护稳定长链接通道,存在必定的技术成本。 * 若是用户长链接断开,会致使没法接收到消息,从而不能保证明时变动。
所以,这种非方式虽然节省流量,但不可以保证100%的实时配置生效率。
3.推拉结合
既然主动拉取和推送都作不到,很天然的,咱们会想到能不能两种方式结合起来,方案以下:
* 客户端与服务端保持一个长链接,当有配置变动时,当即下发; * 为防止长连失效致使更新不及时,客户端还会做按期轮询,拉取配置;
经过这种方式,能够得到较低的流量开销和较高的更新率。业界很多企业采用的是相似的方案,好比携程的配置中心系统Apollo。
4.统一网关
这里介绍第四种更新机制,来自阿里的Orange移动配置系统,它无需任何轮询请求和长链接通道下发,并且能够作到在线用户100%的秒级更新率。
它的秘密就是利用了全集团的统一网关,这套网关同时运行在客户端(Android、iOS)和服务端。移动端网关会接管全部客户端的请求,然后端网关则会承接全部移动端发过来的请求。具体流程以下:
* 客户端的任何网络请求在通过移动端网关时,带上本地配置的版本号; * 服务端网关收到请求后,抽取出配置版本号,发送给配置中心服务,其余参数透传给业务后端 * 配置中心服务基于当前APP的版本号,配置版本号信息,判断是否有新配置,若是有则返回新配置版本号给网关 * 当业务后端返回时,带上这个新配置版本号给客户端 * 客户端发现新配置版本号,则去CDN拉取最新配置数据,完成更新。
在这个流程里,咱们没有为配置单独发起轮询请求,也不须要依赖长链接服务进行下发,并且借助现有的业务数据,加上网关的协议,来实现配置的动态更新。只要客户端处于活跃状态,就能当即发现配置更细你, 并且很是稳定。
不少开发者担忧本身开发的功能上线后悔出现问,因此会加上各类各样的配置开关,随着版本的不断迭代,配置数据确定会愈来愈大,若是每次数据更新,都需啊哟去全量下载,那消耗的流量会愈来愈多,并且,数据包越大,下载更新失败率会也会逐步变高。
由于,咱们还要想办法减小下载配置数据包大小,通常会从两个角度考虑
1.增量更新 2.压缩
除此以外,有些配置数据是敏感的,因此应该要实现加密。
增量算法有不少,咱们能够结合具体场景来选择。
因为咱们的配置都是纯文本,因此能够优先考虑文本Diff算法,如Google的diff-match-patch,就是专门针对纯文本的高性能Diff/Patch算法。并且它提供了多种语言版本实现,包括Java、Objective-C、Python、Dart等,
diff-match-patch内部是基于Myers算法,这个算法就是咱们每天用的 git diff 和 RecyclerView 里的 DiffUtil的实现原理。并且diff-match-patch在这个基础上还作了很多性能优化。
固然,除了纯文本Diff,咱们也能够考虑二进制Diff算法,如BsDiff算法,通常apk的更新、Tinker补丁包更新均可以采用这个增量算法。
对于文本压缩,用的最多的就是Gzip了,Http协议传输也是用的Gzip压缩,兼具压缩率和性能。因此此处也能够考虑Gzip压缩,接入成本最低,效果也不错。另外也能够考虑zStd压缩和Brotli压缩,感兴趣的读者能够去深刻了解下。腾讯云CDN服务目前已经支持了Gzip压缩和Brotli压缩,当文本文件大小在256Byte - 2048KB之间时,采用Gzip压缩;更大的文件则采用Brotli压缩。
很多状况下,咱们会用配置中心来下发一些敏感数据。好比双十一活动,某个优惠券的生效时间戳,若是用户分析出来了,能够人为修改配置数据从而致使程序运行异常。所以,为了保护下发的敏感配置数据,咱们需进行加密。
第一种方式,下发链路加密。咱们能够对下发的配置数据压缩包进行总体加密,客户端拿到后,流式解密和解压到本地文件。之后客户端读取配置时能够直接读取明文。这种方式的问题就是配置数据是以明文形式落盘(存储到磁盘)的,这可能存在数据泄漏风险。
第二种方式,单Value加密。咱们会对每一个Key对应的Value进行加密,而后下发到客户端,解压后存储在磁盘里的是明文Key+密文Value,在须要读取某个配置时,再实时解密并缓存在内存里,从而下降数据泄漏风险。
在选择具体加密方式方面,考虑到客户端的解密耗时,可使用相似AES/CBC/PKCS7Padding加密算法,密钥能够存储到客户端保护起来(为了安全能够放到so 内部,经过混淆提升安全性,或者采用白盒加密方案,将密钥与算法一块儿编译生成代码从而隐藏密钥),也能够支持动态更新。
另外,Value加密后是二进制数据,此时要利用Base64编码,把二进制数据转成文本写入配置文件中。
前面说了,咱们的配置中心可以作到秒级下发,那这里会存在一个问题:若是配置有问题,可能会当即致使大规模线上故障。所以,这里须要支持配置的灰度发布。
通常能够支持多维度灰度,好比基于用户的App版本区间、Uid或者DeviceId、城市、渠道等维度信息来进行灰度。灰度发布后开始观察线上数据表现,肯定逻辑运行正常且稳定,若是没问题,才能够全量发布。
那若是出了问题呢?那就要进行回滚。这里咱们能够经过从新发布一个历史版本的配置数据来实现回滚。
另外,全量发布后,咱们会将更新的配置数据压缩发布到CDN。这时,大量客户端在检测到更新后,会当即访问CDN下载对应的更新配置数据。若是用户量级很大,须要考虑会出现的CDN请求高峰,要确保CDN能承受对应的访问压力,不能被打挂。并且,因为CDN同步须要一段时间,因此此时确定会有不少CDN发生回源,对源服务器也会形成必定的压力。所以,在每次发布后,能够结合具体场景及用户QPS,有选择地考虑对CDN进行预热。
配置中心逐步成为各家公司的标配,一套好的配置中心系统,不只可以支持各类维度的配置下发,如业务配置、技术配置、开关配置等,也要可以作到实时下发,可以尽快触达全部用户。尤为在一些用户量较大的场景下,要可以考虑流量压力,保证CDN等基础设施可以稳定运行。