本文作者:绎推
布景
在云音乐全面转跨端的年代,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!