乐趣区

关于ios:云音乐-iOS-跨端缓存库-NEMichelinCache

本文作者:绎推

背景

在云音乐全面转跨端的时代,H5 / RN 缓存模块是十分重要的组成部分,对页面的稳定性,页面性能等都有十分大影响,目前云音乐应用的缓存库曾经“历史悠久”,没法在现有的根底上来撑持日益宏大的跨端需要,面临着以后架构没法修复的问题:

  1. 后盾 wake up 问题与后盾频繁 I / O 操作导致的解体 – 据统计,最高 50% 以上的后盾解体是老缓存库导致
  2. 主线程偶现卡死问题 – 线程治理问题
  3. RN / H5 页面偶现空白问题 – 数据不统一导致
  4. Fatal Exception,Bundler Error 等降级错误率高
  5. RN unregister module 谬误高
  6. 大量细散反复日志,节约网络资源
  7. 没有整体日志监控,难以定位问题

因而咱们基于缓存库的可扩大架构,从问题登程,从新设计了一套新的跨端缓存库 – NEMichelinCache,全文以 RN 缓存的角度来形容

缓存

首先咱们要晓得缓存的目标是什么?目标是以空间换工夫。说起缓存,很多人会想到操作系统的缓存设计以及缓存中的直写与回写模式(Write Through and Write Back)。

直写模式

CPU 将数据同时更新到 Cache 和 Memory 中

长处

  • 有助于数据恢复(在停电或系统故障的状况下)
  • Cache 和 Memory 数据始终保持统一
  • 间接 I / O 拜访,能够获取到最新数据

毛病

  • 写操作多

回写模式

CPU 将数据更新到 Cache 时,对 Cache 做一个标记,但不同步更新到 Memory 中(异步更新)

长处

  • 速度快
  • 写操作少

毛病

  • 容易造成 Cache 和 Memory 数据不统一
  • 间接 I / O 拜访,不能获取到最新数据

思考

对于一个跨端缓存库计划,次要思考以下几个方面:

  • 如何解决目前面临的问题:收集缓存库相干问题,从问题登程设计解决方案
  • 如何进步缓存的稳定性:须要综合缓存的优缺点,在数据一致性,读写速度等方面思考计划
  • 谬误疾速定位能力:针对各个阶段的谬误,设计谬误上报模块,须要做到不多报、不误报、不漏报
  • 欠缺的日志模块:以 本地回捞日志(贮存于客户端,须要时通过指令上报的 debug 日志)为主,缩小服务端压力,尽量保障日志的信息量足
  • 缓存库新老切换老本:AB 切换老本,新老缓存迁徙老本,各指标定义等
  • 业务拓展性:针对数据源,缓存类型等,给业务提供拓展点
  • 业务接入老本:内置通用计划,升高接入老本

通过各方调研,跨端缓存计划有些相似回写模式,然而须要着重关注回写的毛病。

问题解决方案

从缓存的回写模式毛病登程

  1. 保证数据一致性:保障内存缓存、引擎、磁盘缓存数据一致性
  2. 不提供任何 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!

退出移动版