共计 3830 个字符,预计需要花费 10 分钟才能阅读完成。
本文作者:绎推
背景
在云音乐全面转跨端的时代,H5 / RN 缓存模块是十分重要的组成部分,对页面的稳定性,页面性能等都有十分大影响,目前云音乐应用的缓存库曾经“历史悠久”,没法在现有的根底上来撑持日益宏大的跨端需要,面临着以后架构没法修复的问题:
- 后盾 wake up 问题与后盾频繁 I / O 操作导致的解体 – 据统计,最高 50% 以上的后盾解体是老缓存库导致
- 主线程偶现卡死问题 – 线程治理问题
- RN / H5 页面偶现空白问题 – 数据不统一导致
- Fatal Exception,Bundler Error 等降级错误率高
- RN unregister module 谬误高
- 大量细散反复日志,节约网络资源
- 没有整体日志监控,难以定位问题
因而咱们基于缓存库的可扩大架构,从问题登程,从新设计了一套新的跨端缓存库 – NEMichelinCache,全文以 RN 缓存的角度来形容
缓存
首先咱们要晓得缓存的目标是什么?目标是以空间换工夫。说起缓存,很多人会想到操作系统的缓存设计以及缓存中的直写与回写模式(Write Through and Write Back)。
直写模式
CPU 将数据同时更新到 Cache 和 Memory 中
长处
- 有助于数据恢复(在停电或系统故障的状况下)
- Cache 和 Memory 数据始终保持统一
- 间接 I / O 拜访,能够获取到最新数据
毛病
- 慢
- 写操作多
回写模式
CPU 将数据更新到 Cache 时,对 Cache 做一个标记,但不同步更新到 Memory 中(异步更新)
长处
- 速度快
- 写操作少
毛病
- 容易造成 Cache 和 Memory 数据不统一
- 间接 I / O 拜访,不能获取到最新数据
思考
对于一个跨端缓存库计划,次要思考以下几个方面:
- 如何解决目前面临的问题:收集缓存库相干问题,从问题登程设计解决方案
- 如何进步缓存的稳定性:须要综合缓存的优缺点,在数据一致性,读写速度等方面思考计划
- 谬误疾速定位能力:针对各个阶段的谬误,设计谬误上报模块,须要做到不多报、不误报、不漏报
- 欠缺的日志模块:以 本地回捞日志(贮存于客户端,须要时通过指令上报的 debug 日志)为主,缩小服务端压力,尽量保障日志的信息量足
- 缓存库新老切换老本:AB 切换老本,新老缓存迁徙老本,各指标定义等
- 业务拓展性:针对数据源,缓存类型等,给业务提供拓展点
- 业务接入老本:内置通用计划,升高接入老本
通过各方调研,跨端缓存计划有些相似回写模式,然而须要着重关注回写的毛病。
问题解决方案
从缓存的回写模式毛病登程
- 保证数据一致性:保障内存缓存、引擎、磁盘缓存数据一致性
- 不提供任何 I / O 间接拜访缓存的办法给业务方
因缓存库导致的后盾解体 / 主线程卡死问题
- 线程模块设计 – 设计线程池,保障 I / O 操作 / 耗时操作都在次线程实现
- 下载更新模块 – 以保证数据一致性为外围,责任链模块设计,各节点性能原子化,保障耗时操作在次线程实现
- 数据库模块设计 – 对立治理,FMDB Queue
降级谬误 / 加载失败 / 页面空白 / 卡片模块隐没空白 / unregister module 等引擎谬误
- 同步数据库机会 – 齐全胜利后同步,保障磁盘缓存必是可用的
- 数据库模块 – 反对事务,可 Fallback,保障出错时可回退
- 缓存多版本并存 – 保障本地 Bundle 缓存互不烦扰
- 援用计数模块 – 用于清空缓存,保障应用中的缓存不被提前清空
-
接口批改
- 删除对外提供清空缓存的接口 – 防止业务方随便删除缓存
- 删除对外提供间接读取本地磁盘的接口 – 防止业务方随便读取缓存
- 责任链 Runner – 优先级队列,优先保障正在加载的页面加载速度
- 数据库 / 文件迁徙 – 保障新版本兼容老版本数据,防止反复下载
- 接口 CDN 迁徙
- 网络模块强行应用 https,防拦挡
无效疾速定位问题
- 日志模块设计
- 整体监控谬误日志 – 自定义 Domain,不便辨别各个阶段,不便归因
- 删除冗余日志
- 联合加载流程做到异样信息细化,造成闭环
方案设计
业务接口层
对业务方而言,次要是面向业务接口层开发,设计的初衷为了缩小接入的难度,使接口可控,不让业务方随便拜访磁盘等,如何设计这一层十分要害,对业务方来说,他们只有晓得他们须要做什么,以及可能失去什么,咱们的想法是这一层应该具备以下几点:
- 初始化参数 CacheConfig:缓存名,缓存根目录,其余自定义参数
@interface NEMichelinCacheConfig : NSObject
- (instancetype)initWithAppName:(NSString *)appName
cacheRootPath:(NSString *)cacheRootPath
xxx
@end
- DataProvider 协定 – 业务方只须要实现一个接口即可失常应用缓存性能
- (void)fetchBundleCacheResWithLocalApps:(NSArray<NEMCAppInfo *> *)apps
completionHandler:(void (^)(NSArray<NEMichelinResVersionInfo * > *infoList, NSError *error))completionHandler;
- 缓存更新接口
- (void)updateResourceOfAppInfo:(id<NEMCAppInfo *>)appInfo
priority:(NEMichelinSerialChainPriority)priority
completeBlock:(void (^)(NSError *error, NSDictionary *result))completeBlock;
- 自定义缓存 / 数据协定 – 只有在非凡自定义缓存时,须要非凡实现
除了业务须要关怀的以上接口外,此层中解决了:新老缓存库 AB 切换,外部协定定义,其余自定义接口预留等
责任链模块
- 拆分 前置判断 , 下载 ,MD5 校验,zip / gz 解压, 合并 ,tar 解压, 更新缓存 节点等,颗粒度细化
-
自定义链路能力
- 可删除,减少节点
- 反对暂停 pause,持续 resume 能力
- 全局 Context 传递
- 失败异样抛出能力 – 节点执行失败后,中断执行,用于收集异样
- 生命周期监听能力 – 反对各个节点开始与完结生命周期监听
- 节点职责繁多(只负责本人模块,谁创立,谁开释(包含本地临时文件))
- 逻辑内聚,只依赖数据:节点自行判断 Context 数据,节点间不相互依赖。
责任链模块的设计,为后续日志模块,谬误模块设计打下了良好基础,能够不便在这个设计下收集各个模块的日志,以及删除冗余日志,谬误也能够及时抛出,不会呈现反复抛出的状况,也为前面跨端 APM 数据收集打下了根底,能够不便的在各个节点间插桩,缩小了 APM 建设的工作量。最重要的收益是晋升了稳定性,升高的出错可能性,各个节点齐全掌控本人的 长期变量,不会呈现漏删文件,变量等状况。
责任链 Runner
- 优先级队列能力
- 反对一个 key 对应多个责任链
- 责任链缓存能力
次要为了反对优先级队列的能力,能够让优先级高的链插队,无效晋升缓存速度。
解压 / 合并模块
- 抽离 zip,tar,gz 解压,压缩,合并能力
- 可自定义配置 zip,tar,gz 压缩包解压库能力
- 缩小对三方库的依赖,可任意替换三方库
数据库
- FMDB 替换 sqlite3
- 应用事务
- 数据迁徙
- 数据校验
- 出错回滚
多版本并存
- AppInfo:相当于缓存形容,外面有 Bundle 文件门路
- Bundle 文件:RN 读取的 JS Bundle 文件
为什么要做多版本并存?
依据上图能够看出,AppInfo 读取机会跟引擎加载本地 Bundle 文件的机会是不统一的,所以有可能读取的 AppInfo 中的本地缓存门路曾经被更改,从而导致不可预估的问题。
多版本
为了保证数据的一致性,就呈现了多版本共存的状况,简略了解是同一个版本,在应用期间,数据库、内存、文件都不会被删除,也不会被笼罩。这样操作不就会导致磁盘缓存有限放大么?所以咱们就想到了通过援用计数的形式删除冗余缓存。
援用计数 – 本地 Bundle 缓存清理机会
- Bridge 创立时,Bridge 对应的本地缓存会被援用持有
- 直到所有的 Bridge 被开释时,就会做本地缓存清理操作
- 本地缓存清理操作是乐观操作,也会校验是否是最新缓存,是否在应用
总结
数据统计
CCCandyWebCache(老缓存库) | NEMichelinCache | 论断 | |
---|---|---|---|
md5 校验成功率 | 97% | 100% | 回升 3% |
降级谬误 | * W | * W | 降落 94% |
xcode 获取 wakeup 导致的 crash | 22 年 6 月份:最高到近 70% 的量;去年一年:Top10 中占了 3 个 | 0 | 降落 100% |
ANR | 抽样卡死 104 次,影响 94 用户 | 暂未发现 | 降落 100% |
卡顿 | 抽样卡顿 46061 次,影响 2519 用户 | 卡顿事件 3 个 | 降落 99% |
CPU 异样 | 抽样数量 100+ | 暂未找到 | 降落 100% |
OOM | 抽样谬误量 236,影响用户 67 | 暂未找到 | 降落 100% |
引擎谬误 | 9000+ | 481 | 降落 94% |
24 小时降级率 | Vip(96.43%),Square(75.25%) | Vip(98.21%), Square(96.78%) | 降级率回升 2% 到 20% 不等 |
除了以上模块,咱们对谬误通过 Domain 定义进行了具体分类,日志模块以云音乐自研的 Corona 平台,本地回捞等伎俩进行了具体监控,网络模块以网络库作为根底,反对了断点续传等能力。目前新库已在云音乐 RN 模块全量应用,错误率降落非常明显,前面将继续替换 H5 缓存,DSL 模版缓存等。
参考资料
- Write Through and Write Back in Cache
本文公布自网易云音乐技术团队,文章未经受权禁止任何模式的转载。咱们长年招收各类技术岗位,如果你筹备换工作,又恰好喜爱云音乐,那就退出咱们 grp.music-fe(at)corp.netease.com!